From c9ce758cc6a621a49f27831e0609ef78b464c86b Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Fri, 18 Jun 2021 16:16:36 +0000 Subject: [PATCH 01/15] WIP: Tweak configuration code to be more coherent and usable It would still love to use reflection, but I am not learning that, at least now. --- QuickPlay/Configuration.cs | 35 ++++++++++++++++++++++++-- QuickPlay/MainActivity.cs | 32 +++++++++++++----------- QuickPlay/QuickPlay.csproj | 1 + QuickPlay/SongRecycler.cs | 50 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 16 deletions(-) create mode 100644 QuickPlay/SongRecycler.cs diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 16771e0..610f528 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -3,12 +3,14 @@ using System.IO; namespace QuickPlay { +#pragma warning disable CS0659 // This is basically an almost-singleton, which will never be put in any hash table. /// /// Application (global) configuration data class /// [Serializable] sealed class AppConfiguration { + // XXX: All the fields need to be checked in overriden Equals method public readonly string playerConfigUrl; public static AppConfiguration loadSavedConfiguration() @@ -25,7 +27,26 @@ namespace QuickPlay var newConfig = AppConfiguration.loadSavedConfiguration(); if (this != newConfig) throw new InvalidDataException("Saved configuration is different from the supplied one."); } + + public PlayerConfiguration GetPlayerConfig() + { + // TODO: decide sensibly + var cfgProvider = new CurlConfigurationProvider(playerConfigUrl); + return cfgProvider.GetConfiguration(); + } + + // We want to compare by values. + // It might actually be a bit more sensible to compare serialized + // versions of the objects, but that seems unneccessarily hard. + public override bool Equals(object obj) + { + var other = (AppConfiguration)obj; + // These fields have to match... + if (this.playerConfigUrl != other.playerConfigUrl) return false; + return true; + } } +#pragma warning restore CS0659 /// /// Configuration of the player. @@ -38,6 +59,11 @@ namespace QuickPlay { return null; // FIXME: Implement } + + public IPlayer GetPlayer() + { + throw new NotImplementedException(); + } } interface IPlayerConfigurationProvider @@ -45,9 +71,14 @@ namespace QuickPlay PlayerConfiguration GetConfiguration(); } - sealed class NetworkConfigurationProvider : IPlayerConfigurationProvider + sealed class CurlConfigurationProvider : IPlayerConfigurationProvider { - PlayerConfiguration IPlayerConfigurationProvider.GetConfiguration() + readonly string configUrl; + public CurlConfigurationProvider(string url) + { + configUrl = url; + } + public PlayerConfiguration GetConfiguration() { throw new NotImplementedException(); } diff --git a/QuickPlay/MainActivity.cs b/QuickPlay/MainActivity.cs index 7e8ac12..406d40d 100644 --- a/QuickPlay/MainActivity.cs +++ b/QuickPlay/MainActivity.cs @@ -10,6 +10,7 @@ using Android.Widget; //using Google.Android.Material.Snackbar; using Android.Support.V7.App; using Toolbar = Android.Support.V7.Widget.Toolbar; +using GridLayoutManager = Android.Support.V7.Widget.GridLayoutManager; using System.Net; //using MpcNET; @@ -25,7 +26,8 @@ namespace QuickPlay public class MainActivity : AppCompatActivity { private AppConfiguration appConfig; - private List playerConfigs; + private Android.Support.V7.Widget.RecyclerView recyclerView; + private IPlayer currentPlayer; protected override void OnCreate(Bundle savedInstanceState) { @@ -33,8 +35,8 @@ namespace QuickPlay // App initialization - //appConfig = AppConfiguration.loadSavedConfiguration(); - //playerConfigs = acquirePlayerConfigs(); + appConfig = AppConfiguration.loadSavedConfiguration(); + currentPlayer = appConfig.GetPlayerConfig().GetPlayer(); // UI initialization SetContentView(Resource.Layout.activity_main); @@ -47,6 +49,19 @@ namespace QuickPlay // Hide the play bar by default var bar = FindViewById(Resource.Id.currentSongBar); bar.Visibility = ViewStates.Invisible; + + // Initialize the RecyclerView + // Since this is rather complicated, it is in a separate method + InitializeRecyclerView(); + } + + private void InitializeRecyclerView() + { + recyclerView = FindViewById(Resource.Id.recyclerView1); + var adapter = new SongRecyclerAdapter(currentPlayer); + recyclerView.SetAdapter(adapter); + var layoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Vertical, false); + recyclerView.SetLayoutManager(layoutManager); } public override bool OnCreateOptionsMenu(IMenu menu) @@ -72,16 +87,5 @@ namespace QuickPlay } return base.OnOptionsItemSelected(item); } - - List acquirePlayerConfigs() - { - // FIXME: Bad! We have IPlayerConfigurationProviders - ///var url = appConfig.playerConfigUrl; - // TODO: Learn cURL and get configs :-) - - return null; - throw new NotImplementedException(); - } - } } diff --git a/QuickPlay/QuickPlay.csproj b/QuickPlay/QuickPlay.csproj index 0894629..cca639b 100644 --- a/QuickPlay/QuickPlay.csproj +++ b/QuickPlay/QuickPlay.csproj @@ -72,6 +72,7 @@ + diff --git a/QuickPlay/SongRecycler.cs b/QuickPlay/SongRecycler.cs new file mode 100644 index 0000000..334e791 --- /dev/null +++ b/QuickPlay/SongRecycler.cs @@ -0,0 +1,50 @@ +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Support.V7.Widget; +using Android.Views; +using Android.Widget; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace QuickPlay +{ + class SongRecyclerAdapter : Android.Support.V7.Widget.RecyclerView.Adapter + { + public SongRecyclerAdapter(IPlayer player) + { + throw new NotImplementedException(); + } + public override int ItemCount + { + get + { + throw new NotImplementedException(); + } + } + public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) + { + throw new NotImplementedException(); + } + public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) + { + throw new NotImplementedException(); + } + + public SongRecyclerAdapter(int TODO) + { + + } + + } + class SongRecyclerViewHolder : Android.Support.V7.Widget.RecyclerView.ViewHolder + { + public SongRecyclerViewHolder(View itemView) : base(itemView) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file From 253b9cc87dd73ccf98aa667c898e036df016365c Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Fri, 18 Jun 2021 16:35:12 +0000 Subject: [PATCH 02/15] Fall back to default configuration if there is no configuration to be loaded. --- QuickPlay/Configuration.cs | 16 +++++++++++++++- QuickPlay/MainActivity.cs | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 610f528..01198f8 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -11,8 +11,18 @@ namespace QuickPlay sealed class AppConfiguration { // XXX: All the fields need to be checked in overriden Equals method - public readonly string playerConfigUrl; + // Also: sensible defaults should be provided in defaultConfiguration field + public string playerConfigUrl; + public static AppConfiguration loadConfiguration() + { + var cfg = loadSavedConfiguration(); + if (cfg == null) + { + cfg = defaultConfiguration; + } + return cfg; + } public static AppConfiguration loadSavedConfiguration() { throw new NotImplementedException(); @@ -28,6 +38,10 @@ namespace QuickPlay if (this != newConfig) throw new InvalidDataException("Saved configuration is different from the supplied one."); } + public static AppConfiguration defaultConfiguration = new AppConfiguration + { + playerConfigUrl = "file:///dev/null", + }; public PlayerConfiguration GetPlayerConfig() { // TODO: decide sensibly diff --git a/QuickPlay/MainActivity.cs b/QuickPlay/MainActivity.cs index 406d40d..9e2fd28 100644 --- a/QuickPlay/MainActivity.cs +++ b/QuickPlay/MainActivity.cs @@ -35,7 +35,7 @@ namespace QuickPlay // App initialization - appConfig = AppConfiguration.loadSavedConfiguration(); + appConfig = AppConfiguration.loadConfiguration(); currentPlayer = appConfig.GetPlayerConfig().GetPlayer(); // UI initialization From d479889fd97b36a243d1c79748656771fc13eb16 Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Fri, 18 Jun 2021 16:39:05 +0000 Subject: [PATCH 03/15] Remove some obscure useless code --- QuickPlay/SongRecycler.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/QuickPlay/SongRecycler.cs b/QuickPlay/SongRecycler.cs index 334e791..a279a2c 100644 --- a/QuickPlay/SongRecycler.cs +++ b/QuickPlay/SongRecycler.cs @@ -33,12 +33,6 @@ namespace QuickPlay { throw new NotImplementedException(); } - - public SongRecyclerAdapter(int TODO) - { - - } - } class SongRecyclerViewHolder : Android.Support.V7.Widget.RecyclerView.ViewHolder { From 33f1c0f91ffa9adef7a21b5a0ee72bb65038963a Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Fri, 18 Jun 2021 22:20:39 +0000 Subject: [PATCH 04/15] Implement App config saves and restores --- QuickPlay/Configuration.cs | 40 ++++++++++++++++++++++++++++++++------ QuickPlay/Interfaces.cs | 2 +- 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 01198f8..bc81adf 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -8,12 +8,19 @@ namespace QuickPlay /// Application (global) configuration data class /// [Serializable] - sealed class AppConfiguration + public sealed class AppConfiguration { // XXX: All the fields need to be checked in overriden Equals method // Also: sensible defaults should be provided in defaultConfiguration field public string playerConfigUrl; + // XXX: Any other fields of this model need [NonSerialized] attribute + [NonSerialized] + public static readonly string configFilePath = + Path.Combine( + System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), + "appConfig.xml" + ); public static AppConfiguration loadConfiguration() { var cfg = loadSavedConfiguration(); @@ -25,20 +32,41 @@ namespace QuickPlay } public static AppConfiguration loadSavedConfiguration() { - throw new NotImplementedException(); + var serializer = new System.Xml.Serialization.XmlSerializer(typeof(AppConfiguration)); + AppConfiguration result = null; + try + { + using (var stream = new System.IO.FileStream(configFilePath, System.IO.FileMode.Open)) + { + result = (AppConfiguration)serializer.Deserialize(stream); + + } + } catch (FileNotFoundException) + { + // This is fine, we will return null anyway. + } catch (InvalidCastException) { + // This is not so much fine, TODO log it. + // Still, we cannot supply config, so treat it as if it does not exist. + } + return result; } public void saveConfiguration() { - - + // First we save the config + var serializer = new System.Xml.Serialization.XmlSerializer(typeof(AppConfiguration)); + using (var stream = new System.IO.FileStream(configFilePath, System.IO.FileMode.Create)) + { + serializer.Serialize(stream, this); + } // Make sure that the configuration is same var newConfig = AppConfiguration.loadSavedConfiguration(); if (this != newConfig) throw new InvalidDataException("Saved configuration is different from the supplied one."); } - public static AppConfiguration defaultConfiguration = new AppConfiguration + [NonSerialized] + public static readonly AppConfiguration defaultConfiguration = new AppConfiguration { playerConfigUrl = "file:///dev/null", }; @@ -67,7 +95,7 @@ namespace QuickPlay /// /// Contains details about connection, configured songs &c. /// - class PlayerConfiguration + public class PlayerConfiguration { public static PlayerConfiguration FromFile(StreamReader reader) { diff --git a/QuickPlay/Interfaces.cs b/QuickPlay/Interfaces.cs index 0f3e44d..aa5194a 100644 --- a/QuickPlay/Interfaces.cs +++ b/QuickPlay/Interfaces.cs @@ -9,7 +9,7 @@ namespace QuickPlay /// /// That means that the interface may be extended in the future, which is sad. /// - interface IPlayer + public interface IPlayer { void Play(string identifier); float CurrentProgress { get; } From 63146ec4b79031cf4ef64c40d6ca0ab80faf4470 Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Fri, 18 Jun 2021 22:52:33 +0000 Subject: [PATCH 05/15] Add adapter constructor --- QuickPlay/SongRecycler.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QuickPlay/SongRecycler.cs b/QuickPlay/SongRecycler.cs index a279a2c..ba21b74 100644 --- a/QuickPlay/SongRecycler.cs +++ b/QuickPlay/SongRecycler.cs @@ -14,9 +14,10 @@ namespace QuickPlay { class SongRecyclerAdapter : Android.Support.V7.Widget.RecyclerView.Adapter { + IPlayer player; public SongRecyclerAdapter(IPlayer player) { - throw new NotImplementedException(); + this.player = player; } public override int ItemCount { From ec2ab8f619ba72d50d656397a5b1321c93365ecc Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sat, 19 Jun 2021 08:53:27 +0000 Subject: [PATCH 06/15] Implement HTTP ini download, Ini parser. WIP. --- QuickPlay/Configuration.cs | 32 ++++++++++++++---- QuickPlay/IniParser.cs | 67 ++++++++++++++++++++++++++++++++++++++ QuickPlay/QuickPlay.csproj | 1 + QuickPlay/SongRecycler.cs | 1 - 4 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 QuickPlay/IniParser.cs diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index bc81adf..0e3c913 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -1,5 +1,8 @@ using System; +using System.Net.Http; using System.IO; +using System.Threading.Tasks; +using System.Collections.Generic; namespace QuickPlay { @@ -73,7 +76,7 @@ namespace QuickPlay public PlayerConfiguration GetPlayerConfig() { // TODO: decide sensibly - var cfgProvider = new CurlConfigurationProvider(playerConfigUrl); + var cfgProvider = new HttpConfigurationProvider(playerConfigUrl); return cfgProvider.GetConfiguration(); } @@ -97,9 +100,23 @@ namespace QuickPlay /// public class PlayerConfiguration { + List songs; + public static PlayerConfiguration FromFile(StreamReader reader) { - return null; // FIXME: Implement + var parser = new IniParser(reader); + var ini = parser.Parse(); + foreach (string key in ini.Keys) + { + // Two options: either general options, or song descriptions + if (key == "general") + { + + } else + { + // Song + } + } } public IPlayer GetPlayer() @@ -113,16 +130,19 @@ namespace QuickPlay PlayerConfiguration GetConfiguration(); } - sealed class CurlConfigurationProvider : IPlayerConfigurationProvider + sealed class HttpConfigurationProvider : IPlayerConfigurationProvider { readonly string configUrl; - public CurlConfigurationProvider(string url) + public HttpConfigurationProvider(string url) { configUrl = url; } - public PlayerConfiguration GetConfiguration() + public async Task GetConfiguration() { - throw new NotImplementedException(); + var client = new HttpClient() ; + var resp = await client.GetStreamAsync(configUrl); + var sr = new StreamReader(resp); + return PlayerConfiguration.FromFile(sr); } } diff --git a/QuickPlay/IniParser.cs b/QuickPlay/IniParser.cs new file mode 100644 index 0000000..4a20be6 --- /dev/null +++ b/QuickPlay/IniParser.cs @@ -0,0 +1,67 @@ +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; + +namespace QuickPlay +{ + /// + /// This class implements (yet another) INI-style parser. + /// + /// INI specification: everything is in sections, comments using both '#' + /// or ';', space around '=' is trimmed, last value assigned overrides + /// previous assignments, sections may not repeat. Section names may only + /// contain reasonable characters (at least [a-zA-Z0-9. ], not really + /// enforced). Everything is case-sensitive. + /// + /// Both CRLF and just LF should be supported as line ends. + /// + class IniParser + { + StreamReader reader; + string currentSection = null; + public IniParser(StreamReader sr) + { + reader = sr; + } + + // The return type is dictionary by sections, which in turn holds the section key-value dictionary + // Maybe the code is a bit more Pythonic than it should be... + public Dictionary> Parse() + { + Dictionary> result = new Dictionary>(); + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + line = StripComments(line).Trim(); + if (line.Length == 0) continue; + if (line.StartsWith('[')) + { + // This does not really do the right thing, but for QuickPlay's use it is good enough. + currentSection = line.Split(new char[] { '[', ']' })[1]; + if (result.ContainsKey(currentSection)) throw new InvalidOperationException("Multiple sections with same name"); + result[currentSection] = new Dictionary(); + } + else + { + // Other lines may only be assignments + int equalSignPosition = line.IndexOf('='); + string key = line.Substring(0, equalSignPosition).Trim(); + string value = line.Substring(equalSignPosition + 1).Trim(); + result[currentSection][key] = value; + } + } + return result; + } + string StripComments(string line) + { + return line.Split(new char[] { ';', '#' })[0]; + } + } +} \ No newline at end of file diff --git a/QuickPlay/QuickPlay.csproj b/QuickPlay/QuickPlay.csproj index cca639b..2d3d180 100644 --- a/QuickPlay/QuickPlay.csproj +++ b/QuickPlay/QuickPlay.csproj @@ -43,6 +43,7 @@ false false false + Xamarin.Android.Net.AndroidClientHandler True diff --git a/QuickPlay/SongRecycler.cs b/QuickPlay/SongRecycler.cs index ba21b74..48b3a87 100644 --- a/QuickPlay/SongRecycler.cs +++ b/QuickPlay/SongRecycler.cs @@ -7,7 +7,6 @@ using Android.Views; using Android.Widget; using System; using System.Collections.Generic; -using System.Linq; using System.Text; namespace QuickPlay From eb7d1dda65fb31b58615d53b4784c79cc3b60423 Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sat, 19 Jun 2021 16:03:37 +0000 Subject: [PATCH 07/15] Implement PlayerConfig building --- QuickPlay/Configuration.cs | 31 ++++++++++++++++++++++++++----- QuickPlay/Interfaces.cs | 10 +++++++--- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 0e3c913..3ab5a5d 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -100,23 +100,27 @@ namespace QuickPlay /// public class PlayerConfiguration { - List songs; + Dictionary songs; + string playerName; public static PlayerConfiguration FromFile(StreamReader reader) { var parser = new IniParser(reader); var ini = parser.Parse(); - foreach (string key in ini.Keys) + var result = new PlayerConfiguration(); + foreach (var elem in ini) { // Two options: either general options, or song descriptions - if (key == "general") + if (elem.Key == "general") { - + result.playerName = elem.Value["name"]; } else { - // Song + var song = new Song(elem.Key, elem.Value); + result.songs[elem.Key] = song; } } + return result; } public IPlayer GetPlayer() @@ -168,4 +172,21 @@ namespace QuickPlay { public PlayerConfiguration GetConfiguration() => null; } + + class Song: IPlayable + { + public string Identifier { get; private set; } + public PlayableMetadata Metadata { get; private set; } + public Song(string id, Dictionary data) { + Identifier = id; + Metadata = new PlayableMetadata + { + usualPlayingTime = TimeSpan.Parse(data["time"]) + }; + } + public void Play() + { + throw new NotImplementedException(); + } + } } \ No newline at end of file diff --git a/QuickPlay/Interfaces.cs b/QuickPlay/Interfaces.cs index aa5194a..8f0b6f5 100644 --- a/QuickPlay/Interfaces.cs +++ b/QuickPlay/Interfaces.cs @@ -11,6 +11,7 @@ namespace QuickPlay /// public interface IPlayer { + Dictionary Songs { get; } void Play(string identifier); float CurrentProgress { get; } bool IsReady { get; } @@ -21,12 +22,15 @@ namespace QuickPlay /// // This is not really an interface, but since it is a dataclass, I treat it // more as a part of interface, so it belongs to this file. - class PlayableMetadata + + // Also, in order not to need boilerplate code, this should be semantically + // treated as immutable even though it's not. + public class PlayableMetadata { - public readonly TimeSpan usualPlayingTime; + public TimeSpan usualPlayingTime; } - interface IPlayable + public interface IPlayable { void Play(); string Identifier { get; } From 8d1dffc7434636811f4877e45143bd39309d087f Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sat, 19 Jun 2021 20:44:29 +0000 Subject: [PATCH 08/15] Make cfgProviders asynchronous (in a quite bad ways sometimes...) --- QuickPlay/Configuration.cs | 12 ++++++------ QuickPlay/QuickPlay.csproj | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 3ab5a5d..9c45760 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -77,7 +77,7 @@ namespace QuickPlay { // TODO: decide sensibly var cfgProvider = new HttpConfigurationProvider(playerConfigUrl); - return cfgProvider.GetConfiguration(); + return cfgProvider.GetConfigurationAsync().Result; // Bad code, but hopefully we dont hang. } // We want to compare by values. @@ -131,7 +131,7 @@ namespace QuickPlay interface IPlayerConfigurationProvider { - PlayerConfiguration GetConfiguration(); + Task GetConfigurationAsync(); } sealed class HttpConfigurationProvider : IPlayerConfigurationProvider @@ -141,7 +141,7 @@ namespace QuickPlay { configUrl = url; } - public async Task GetConfiguration() + public async Task GetConfigurationAsync() { var client = new HttpClient() ; var resp = await client.GetStreamAsync(configUrl); @@ -162,15 +162,15 @@ namespace QuickPlay { this.reader = new StreamReader(filename); } - public PlayerConfiguration GetConfiguration() + public Task GetConfigurationAsync() { - return PlayerConfiguration.FromFile(reader); + return Task.FromResult(PlayerConfiguration.FromFile(reader)); } } sealed class DummyConfigurationProvider : IPlayerConfigurationProvider { - public PlayerConfiguration GetConfiguration() => null; + public Task GetConfigurationAsync() => null; } class Song: IPlayable diff --git a/QuickPlay/QuickPlay.csproj b/QuickPlay/QuickPlay.csproj index 2d3d180..ff49baa 100644 --- a/QuickPlay/QuickPlay.csproj +++ b/QuickPlay/QuickPlay.csproj @@ -60,6 +60,7 @@ + @@ -68,6 +69,7 @@ + From f7dea469f84fb82c8faf4415cc8b036d2a491fb3 Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sat, 19 Jun 2021 20:51:45 +0000 Subject: [PATCH 09/15] Allow both HTTP and local URLs for playerConfig --- QuickPlay/Configuration.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 9c45760..64abf2d 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -75,8 +75,19 @@ namespace QuickPlay }; public PlayerConfiguration GetPlayerConfig() { - // TODO: decide sensibly - var cfgProvider = new HttpConfigurationProvider(playerConfigUrl); + IPlayerConfigurationProvider cfgProvider; + if (playerConfigUrl.StartsWith("http:") || playerConfigUrl.StartsWith("https:")) + { + cfgProvider = new HttpConfigurationProvider(playerConfigUrl); + } else if (playerConfigUrl.StartsWith("file://")) + { + string filename = playerConfigUrl.Substring("file://".Length); + var reader = new StreamReader(filename); + cfgProvider = new FileConfigurationProvider(reader); + } else + { + throw new InvalidOperationException("Schema of player config URL not supported"); + } return cfgProvider.GetConfigurationAsync().Result; // Bad code, but hopefully we dont hang. } From 85066e197c3113d1b66712444a0c42dbb8613bef Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sun, 20 Jun 2021 12:43:17 +0000 Subject: [PATCH 10/15] Add skeleton for a MPD-backed IPlayer --- QuickPlay/Configuration.cs | 24 +++++++++++++++++++++--- QuickPlay/MpdPlayer.cs | 36 ++++++++++++++++++++++++++++++++++++ QuickPlay/QuickPlay.csproj | 1 + QuickPlay/SongRecycler.cs | 2 +- 4 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 QuickPlay/MpdPlayer.cs diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 64abf2d..4f06c9f 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -104,6 +104,11 @@ namespace QuickPlay } #pragma warning restore CS0659 + public enum PlayerType + { + MPD, + } + /// /// Configuration of the player. /// @@ -111,8 +116,10 @@ namespace QuickPlay /// public class PlayerConfiguration { - Dictionary songs; - string playerName; + public Dictionary songs; + public string playerName; + public PlayerType playerType; + public string playerConnectionDetails; // This is opaque to this class, the player will make sense of it. public static PlayerConfiguration FromFile(StreamReader reader) { @@ -125,6 +132,8 @@ namespace QuickPlay if (elem.Key == "general") { result.playerName = elem.Value["name"]; + result.playerType = PlayerType.MPD; //FIXME: not always. + result.playerConnectionDetails = elem.Value["connection"]; } else { var song = new Song(elem.Key, elem.Value); @@ -136,7 +145,16 @@ namespace QuickPlay public IPlayer GetPlayer() { - throw new NotImplementedException(); + IPlayer result; + switch (playerType) + { + case PlayerType.MPD: + result = new MpdPlayer(this); + break; + default: + throw new InvalidOperationException("Cannot happen: Player had no type."); + } + return result; } } diff --git a/QuickPlay/MpdPlayer.cs b/QuickPlay/MpdPlayer.cs new file mode 100644 index 0000000..6cba223 --- /dev/null +++ b/QuickPlay/MpdPlayer.cs @@ -0,0 +1,36 @@ +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using System; +using System.Collections.Generic; + +namespace QuickPlay +{ + class MpdPlayer: IPlayer + { + public Dictionary Songs { get; private set; } + public float CurrentProgress { get { + throw new NotImplementedException(); + } } + public bool IsReady { get + { + throw new NotImplementedException(); + } } + public void Play(string songId) + { + throw new NotImplementedException(); + } + public void SetReasonableOptions() + { + throw new NotImplementedException(); + } + public MpdPlayer(PlayerConfiguration cfg) + { + //TODO + Songs = cfg.songs; + } + } +} \ No newline at end of file diff --git a/QuickPlay/QuickPlay.csproj b/QuickPlay/QuickPlay.csproj index ff49baa..bfdf3c7 100644 --- a/QuickPlay/QuickPlay.csproj +++ b/QuickPlay/QuickPlay.csproj @@ -73,6 +73,7 @@ + diff --git a/QuickPlay/SongRecycler.cs b/QuickPlay/SongRecycler.cs index 48b3a87..e0a0927 100644 --- a/QuickPlay/SongRecycler.cs +++ b/QuickPlay/SongRecycler.cs @@ -22,7 +22,7 @@ namespace QuickPlay { get { - throw new NotImplementedException(); + return player.Songs.Count; } } public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) From 35c4937632ad3c85a09aeffc6ebb31b3dc8a041e Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sat, 3 Jul 2021 17:29:31 +0000 Subject: [PATCH 11/15] Add more code :-) --- QuickPlay/Configuration.cs | 24 +++++++++++++++------- QuickPlay/Interfaces.cs | 20 ++++++++++++++++++ QuickPlay/MainActivity.cs | 27 +++++++++++++++++++++++-- QuickPlay/MpdMonitorService.cs | 2 +- QuickPlay/MpdPlayer.cs | 37 +++++++++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 11 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index 4f06c9f..d681e31 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -71,9 +71,9 @@ namespace QuickPlay [NonSerialized] public static readonly AppConfiguration defaultConfiguration = new AppConfiguration { - playerConfigUrl = "file:///dev/null", + playerConfigUrl = "http://www.ms.mff.cuni.cz/~turinskp/znelky.ini", }; - public PlayerConfiguration GetPlayerConfig() + public async Task GetPlayerConfig() { IPlayerConfigurationProvider cfgProvider; if (playerConfigUrl.StartsWith("http:") || playerConfigUrl.StartsWith("https:")) @@ -84,11 +84,13 @@ namespace QuickPlay string filename = playerConfigUrl.Substring("file://".Length); var reader = new StreamReader(filename); cfgProvider = new FileConfigurationProvider(reader); + } else if (playerConfigUrl == "default") { + cfgProvider = new DummyConfigurationProvider(); } else { throw new InvalidOperationException("Schema of player config URL not supported"); } - return cfgProvider.GetConfigurationAsync().Result; // Bad code, but hopefully we dont hang. + return await cfgProvider.GetConfigurationAsync(); } // We want to compare by values. @@ -116,10 +118,10 @@ namespace QuickPlay /// public class PlayerConfiguration { - public Dictionary songs; - public string playerName; + public Dictionary songs = new Dictionary(); + public string playerName = ""; public PlayerType playerType; - public string playerConnectionDetails; // This is opaque to this class, the player will make sense of it. + public string playerConnectionDetails = ""; // This is opaque to this class, the player will make sense of it. public static PlayerConfiguration FromFile(StreamReader reader) { @@ -199,7 +201,15 @@ namespace QuickPlay sealed class DummyConfigurationProvider : IPlayerConfigurationProvider { - public Task GetConfigurationAsync() => null; + public Task GetConfigurationAsync() + { + PlayerConfiguration cfg = new PlayerConfiguration(); + cfg.playerType = PlayerType.MPD; + cfg.playerName = "Dummy player"; + cfg.playerConnectionDetails = "127.0.0.1"; + + return Task.FromResult(cfg); + } } class Song: IPlayable diff --git a/QuickPlay/Interfaces.cs b/QuickPlay/Interfaces.cs index 8f0b6f5..9b35594 100644 --- a/QuickPlay/Interfaces.cs +++ b/QuickPlay/Interfaces.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; +using Android.Content; // MPD client abstractions and simplifications namespace QuickPlay @@ -16,6 +18,17 @@ namespace QuickPlay float CurrentProgress { get; } bool IsReady { get; } void SetReasonableOptions(); //TODO + /// + /// Attach to the real player. + /// + /// Since this operation can be asynchronous, we cannot put it in th + /// constructor. And this allows for some tweaks before connecting. + /// + /// + /// Context to run possible services from. + /// + /// + Task ConnectAsync(Context context); } /// /// A simple dataclass to hold auxiliary data of the Playable objects. @@ -45,4 +58,11 @@ namespace QuickPlay ILayout LayOut(ICollection playables); } + public class CannotConnectException: Exception + { + public CannotConnectException(string msg, Exception e) : base(msg, e) { } + public CannotConnectException(string msg) : base(msg) { } + public CannotConnectException() : base() { } + } + } \ No newline at end of file diff --git a/QuickPlay/MainActivity.cs b/QuickPlay/MainActivity.cs index 9e2fd28..0d1bc87 100644 --- a/QuickPlay/MainActivity.cs +++ b/QuickPlay/MainActivity.cs @@ -29,14 +29,25 @@ namespace QuickPlay private Android.Support.V7.Widget.RecyclerView recyclerView; private IPlayer currentPlayer; - protected override void OnCreate(Bundle savedInstanceState) + protected override async void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); // App initialization appConfig = AppConfiguration.loadConfiguration(); - currentPlayer = appConfig.GetPlayerConfig().GetPlayer(); + var playerConfig = await appConfig.GetPlayerConfig(); + currentPlayer = playerConfig.GetPlayer(); + try + { + await currentPlayer.ConnectAsync(this); + } catch (CannotConnectException e) + { + //TODO: View a toast with details and change some colors? + Console.WriteLine("This is bad."); + var t = Toast.MakeText(this, e.Message + ": " + e.InnerException.Message ?? "", ToastLength.Long); + t.Show(); + } // UI initialization SetContentView(Resource.Layout.activity_main); @@ -87,5 +98,17 @@ namespace QuickPlay } return base.OnOptionsItemSelected(item); } + + /// + /// A callback for all player updates. + /// + /// The update should be easy on resources, so no need to have separate + /// partial updates. This simply performs full update of the activity + /// state and views. + /// + public void OnPlayerUpdate() + { + throw new NotImplementedException("Activity should update."); + } } } diff --git a/QuickPlay/MpdMonitorService.cs b/QuickPlay/MpdMonitorService.cs index bef31d7..6de4936 100644 --- a/QuickPlay/MpdMonitorService.cs +++ b/QuickPlay/MpdMonitorService.cs @@ -11,7 +11,7 @@ using System.Text; namespace QuickPlay { - [Service(Exported = true, Name = "cz.ledoian.quickplay.mpdmonior")] + [Service] class MpdMonitorService : Service { public override void OnCreate() diff --git a/QuickPlay/MpdPlayer.cs b/QuickPlay/MpdPlayer.cs index 6cba223..c91d894 100644 --- a/QuickPlay/MpdPlayer.cs +++ b/QuickPlay/MpdPlayer.cs @@ -6,11 +6,20 @@ using Android.Views; using Android.Widget; using System; using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; +using MpcCore; +using System.Net.Sockets; namespace QuickPlay { class MpdPlayer: IPlayer { + MpcCoreClient mpd; + Thread serviceThread; + // MpcCore uses strings, so be it + string mpdIP, mpdPort; + public Dictionary Songs { get; private set; } public float CurrentProgress { get { throw new NotImplementedException(); @@ -29,8 +38,34 @@ namespace QuickPlay } public MpdPlayer(PlayerConfiguration cfg) { - //TODO + // Populate known fields/properties Songs = cfg.songs; + // NOTE: We separate the port by '@', since ':' could be part of IPv6 and we do not want to complicate parsing. + var connDetails = cfg.playerConnectionDetails.Split('@'); + if (connDetails.Length > 2) throw new InvalidOperationException("Bad connection details"); + mpdIP = connDetails[0]; + mpdPort = connDetails.Length >=2 ? connDetails[1] : "6600"; // XXX: Unneccessary default here... + // Connecting and monitoring remote player is done in ConnectAsync. + } + public async Task ConnectAsync(Context ctx) + { + // Create a persistent connection + var conn = new MpcCoreConnection(mpdIP, mpdPort); + mpd = new MpcCoreClient(conn); + try + { + await mpd.ConnectAsync(); + } catch (SocketException e) + { + throw new CannotConnectException("MPD connect failed", e); + } + // Start the monitoring service + Console.WriteLine("Hello! Will run thr."); + serviceThread = new Thread(() => + { + var intent = new Intent(ctx, typeof(MpdMonitorService)); + }); + Console.WriteLine("Thread possibly started"); } } } \ No newline at end of file From cda69227eb08ed7a7df0283a84bc2faed007b3cd Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sat, 3 Jul 2021 23:04:11 +0000 Subject: [PATCH 12/15] Implement a PoC of the recycler view --- QuickPlay/Interfaces.cs | 6 +---- QuickPlay/LayoutStrategies.cs | 21 +++++++++++++++++ QuickPlay/MainActivity.cs | 4 ++-- QuickPlay/QuickPlay.csproj | 6 +++++ QuickPlay/Resources/Resource.designer.cs | 24 +++++++++++-------- QuickPlay/Resources/layout/songLayout.xml | 28 +++++++++++++++++++++++ QuickPlay/SongRecycler.cs | 15 ++++++++---- 7 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 QuickPlay/LayoutStrategies.cs create mode 100644 QuickPlay/Resources/layout/songLayout.xml diff --git a/QuickPlay/Interfaces.cs b/QuickPlay/Interfaces.cs index 9b35594..3281365 100644 --- a/QuickPlay/Interfaces.cs +++ b/QuickPlay/Interfaces.cs @@ -49,13 +49,9 @@ namespace QuickPlay string Identifier { get; } PlayableMetadata Metadata { get; } } - interface ILayout - { - // TODO - } interface ILayoutStrategy { - ILayout LayOut(ICollection playables); + List LayOut(ICollection playables); } public class CannotConnectException: Exception diff --git a/QuickPlay/LayoutStrategies.cs b/QuickPlay/LayoutStrategies.cs new file mode 100644 index 0000000..9d9d8b4 --- /dev/null +++ b/QuickPlay/LayoutStrategies.cs @@ -0,0 +1,21 @@ +using Android.App; +using Android.Content; +using Android.OS; +using Android.Runtime; +using Android.Views; +using Android.Widget; +using System; +using System.Collections.Generic; + +namespace QuickPlay +{ + class LexicographicLayoutStrategy : ILayoutStrategy + { + public List LayOut(ICollection playables) + { + List list = new List(playables); + list.Sort((IPlayable x, IPlayable y) => x.Identifier.CompareTo(y.Identifier)); + return list; + } + } +} \ No newline at end of file diff --git a/QuickPlay/MainActivity.cs b/QuickPlay/MainActivity.cs index 0d1bc87..0670541 100644 --- a/QuickPlay/MainActivity.cs +++ b/QuickPlay/MainActivity.cs @@ -44,7 +44,6 @@ namespace QuickPlay } catch (CannotConnectException e) { //TODO: View a toast with details and change some colors? - Console.WriteLine("This is bad."); var t = Toast.MakeText(this, e.Message + ": " + e.InnerException.Message ?? "", ToastLength.Long); t.Show(); } @@ -69,7 +68,8 @@ namespace QuickPlay private void InitializeRecyclerView() { recyclerView = FindViewById(Resource.Id.recyclerView1); - var adapter = new SongRecyclerAdapter(currentPlayer); + var layoutStrategy = new LexicographicLayoutStrategy(); + var adapter = new SongRecyclerAdapter(currentPlayer, layoutStrategy); recyclerView.SetAdapter(adapter); var layoutManager = new GridLayoutManager(this, 2, GridLayoutManager.Vertical, false); recyclerView.SetLayoutManager(layoutManager); diff --git a/QuickPlay/QuickPlay.csproj b/QuickPlay/QuickPlay.csproj index bfdf3c7..2f7d5e7 100644 --- a/QuickPlay/QuickPlay.csproj +++ b/QuickPlay/QuickPlay.csproj @@ -71,6 +71,7 @@ + @@ -221,6 +222,11 @@ + + + Designer + + + + + + + + diff --git a/QuickPlay/SongRecycler.cs b/QuickPlay/SongRecycler.cs index e0a0927..39fbd88 100644 --- a/QuickPlay/SongRecycler.cs +++ b/QuickPlay/SongRecycler.cs @@ -14,9 +14,11 @@ namespace QuickPlay class SongRecyclerAdapter : Android.Support.V7.Widget.RecyclerView.Adapter { IPlayer player; - public SongRecyclerAdapter(IPlayer player) + ILayoutStrategy layoutStrategy; + public SongRecyclerAdapter(IPlayer player, ILayoutStrategy layoutStrategy) { this.player = player; + this.layoutStrategy = layoutStrategy; } public override int ItemCount { @@ -27,18 +29,23 @@ namespace QuickPlay } public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { - throw new NotImplementedException(); + // I admit I have little idea what I am doing. + View itemView = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.songLayout, parent, false); + return new SongRecyclerViewHolder(itemView); } public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position) { - throw new NotImplementedException(); + SongRecyclerViewHolder vh = (SongRecyclerViewHolder)holder; + List layout = layoutStrategy.LayOut(player.Songs.Values); + vh.SongName.Text = layout[position].Identifier; } } class SongRecyclerViewHolder : Android.Support.V7.Widget.RecyclerView.ViewHolder { + public TextView SongName { get; private set; } public SongRecyclerViewHolder(View itemView) : base(itemView) { - throw new NotImplementedException(); + SongName = itemView.FindViewById(Resource.Id.songName); } } } \ No newline at end of file From 5ca7a161cc0610746ee299feac64e99fce1ceb69 Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sun, 4 Jul 2021 00:15:34 +0000 Subject: [PATCH 13/15] Tweak apperance --- QuickPlay/Resources/layout/songLayout.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/QuickPlay/Resources/layout/songLayout.xml b/QuickPlay/Resources/layout/songLayout.xml index 028a9e9..e785907 100644 --- a/QuickPlay/Resources/layout/songLayout.xml +++ b/QuickPlay/Resources/layout/songLayout.xml @@ -12,6 +12,8 @@ Date: Sun, 4 Jul 2021 01:30:39 +0000 Subject: [PATCH 14/15] Show network status and player name --- QuickPlay/Configuration.cs | 15 ++++++++++++++- QuickPlay/Interfaces.cs | 1 + QuickPlay/MainActivity.cs | 10 +++++++++- QuickPlay/MpdPlayer.cs | 6 ++++-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/QuickPlay/Configuration.cs b/QuickPlay/Configuration.cs index d681e31..fc3ec1c 100644 --- a/QuickPlay/Configuration.cs +++ b/QuickPlay/Configuration.cs @@ -90,7 +90,20 @@ namespace QuickPlay { throw new InvalidOperationException("Schema of player config URL not supported"); } - return await cfgProvider.GetConfigurationAsync(); + PlayerConfiguration playercfg; + try + { + playercfg = await cfgProvider.GetConfigurationAsync(); + } + catch (Exception e) when (e is Java.Net.UnknownHostException) + { + var ctx = Android.App.Application.Context; + var t = Android.Widget.Toast.MakeText(ctx, "Could not load config:" + e.Message, Android.Widget.ToastLength.Long); + t.Show(); + cfgProvider = new DummyConfigurationProvider(); + playercfg = await cfgProvider.GetConfigurationAsync(); + } + return playercfg; } // We want to compare by values. diff --git a/QuickPlay/Interfaces.cs b/QuickPlay/Interfaces.cs index 3281365..6c25806 100644 --- a/QuickPlay/Interfaces.cs +++ b/QuickPlay/Interfaces.cs @@ -14,6 +14,7 @@ namespace QuickPlay public interface IPlayer { Dictionary Songs { get; } + string PlayerName { get; } void Play(string identifier); float CurrentProgress { get; } bool IsReady { get; } diff --git a/QuickPlay/MainActivity.cs b/QuickPlay/MainActivity.cs index 0670541..4420148 100644 --- a/QuickPlay/MainActivity.cs +++ b/QuickPlay/MainActivity.cs @@ -63,6 +63,9 @@ namespace QuickPlay // Initialize the RecyclerView // Since this is rather complicated, it is in a separate method InitializeRecyclerView(); + + // Refresh player info + OnPlayerUpdate(); } private void InitializeRecyclerView() @@ -108,7 +111,12 @@ namespace QuickPlay /// public void OnPlayerUpdate() { - throw new NotImplementedException("Activity should update."); + TextView playerName = FindViewById(Resource.Id.playerNameText); + playerName.Text = currentPlayer.PlayerName; + Toolbar tb = FindViewById(Resource.Id.toolbar); + // This code is seriously lovely. FML. + tb.SetBackgroundColor(currentPlayer.IsReady ? new Android.Graphics.Color(Android.Support.V4.Content.ContextCompat.GetColor(this, Resource.Color.colorPrimary)) : Android.Graphics.Color.Red); + // throw new NotImplementedException("Activity should update."); } } } diff --git a/QuickPlay/MpdPlayer.cs b/QuickPlay/MpdPlayer.cs index c91d894..b83bcb1 100644 --- a/QuickPlay/MpdPlayer.cs +++ b/QuickPlay/MpdPlayer.cs @@ -20,13 +20,14 @@ namespace QuickPlay // MpcCore uses strings, so be it string mpdIP, mpdPort; - public Dictionary Songs { get; private set; } + public Dictionary Songs { get; private set; } + public string PlayerName { get; private set; } public float CurrentProgress { get { throw new NotImplementedException(); } } public bool IsReady { get { - throw new NotImplementedException(); + return mpd.IsConnected; } } public void Play(string songId) { @@ -40,6 +41,7 @@ namespace QuickPlay { // Populate known fields/properties Songs = cfg.songs; + PlayerName = cfg.playerName; // NOTE: We separate the port by '@', since ':' could be part of IPv6 and we do not want to complicate parsing. var connDetails = cfg.playerConnectionDetails.Split('@'); if (connDetails.Length > 2) throw new InvalidOperationException("Bad connection details"); From 71abb3fcd8ad5348e9477aea7d7b33fa46a23703 Mon Sep 17 00:00:00 2001 From: Me on Windows Date: Sun, 4 Jul 2021 00:49:36 +0000 Subject: [PATCH 15/15] Hide context to the environment --- QuickPlay/Interfaces.cs | 5 +---- QuickPlay/MainActivity.cs | 2 +- QuickPlay/MpdMonitorService.cs | 1 + QuickPlay/MpdPlayer.cs | 11 +++-------- 4 files changed, 6 insertions(+), 13 deletions(-) diff --git a/QuickPlay/Interfaces.cs b/QuickPlay/Interfaces.cs index 6c25806..1d0e079 100644 --- a/QuickPlay/Interfaces.cs +++ b/QuickPlay/Interfaces.cs @@ -25,11 +25,8 @@ namespace QuickPlay /// Since this operation can be asynchronous, we cannot put it in th /// constructor. And this allows for some tweaks before connecting. /// - /// - /// Context to run possible services from. - /// /// - Task ConnectAsync(Context context); + Task ConnectAsync(); } /// /// A simple dataclass to hold auxiliary data of the Playable objects. diff --git a/QuickPlay/MainActivity.cs b/QuickPlay/MainActivity.cs index 4420148..40d146c 100644 --- a/QuickPlay/MainActivity.cs +++ b/QuickPlay/MainActivity.cs @@ -40,7 +40,7 @@ namespace QuickPlay currentPlayer = playerConfig.GetPlayer(); try { - await currentPlayer.ConnectAsync(this); + await currentPlayer.ConnectAsync(); } catch (CannotConnectException e) { //TODO: View a toast with details and change some colors? diff --git a/QuickPlay/MpdMonitorService.cs b/QuickPlay/MpdMonitorService.cs index 6de4936..3075524 100644 --- a/QuickPlay/MpdMonitorService.cs +++ b/QuickPlay/MpdMonitorService.cs @@ -17,6 +17,7 @@ namespace QuickPlay public override void OnCreate() { base.OnCreate(); + // TODO: Create the watching thread } public override IBinder OnBind(Intent intent) { diff --git a/QuickPlay/MpdPlayer.cs b/QuickPlay/MpdPlayer.cs index b83bcb1..228710f 100644 --- a/QuickPlay/MpdPlayer.cs +++ b/QuickPlay/MpdPlayer.cs @@ -16,7 +16,6 @@ namespace QuickPlay class MpdPlayer: IPlayer { MpcCoreClient mpd; - Thread serviceThread; // MpcCore uses strings, so be it string mpdIP, mpdPort; @@ -49,7 +48,7 @@ namespace QuickPlay mpdPort = connDetails.Length >=2 ? connDetails[1] : "6600"; // XXX: Unneccessary default here... // Connecting and monitoring remote player is done in ConnectAsync. } - public async Task ConnectAsync(Context ctx) + public async Task ConnectAsync() { // Create a persistent connection var conn = new MpcCoreConnection(mpdIP, mpdPort); @@ -62,12 +61,8 @@ namespace QuickPlay throw new CannotConnectException("MPD connect failed", e); } // Start the monitoring service - Console.WriteLine("Hello! Will run thr."); - serviceThread = new Thread(() => - { - var intent = new Intent(ctx, typeof(MpdMonitorService)); - }); - Console.WriteLine("Thread possibly started"); + var ctx = Android.App.Application.Context; + var intent = new Intent(ctx, typeof(MpdMonitorService)); } } } \ No newline at end of file