Merge branch 'develop'

master v0.2
LEdoian 3 years ago
commit 4ea9544d6e

@ -65,7 +65,8 @@ namespace QuickPlay
// Make sure that the configuration is same // Make sure that the configuration is same
var newConfig = AppConfiguration.loadSavedConfiguration(); var newConfig = AppConfiguration.loadSavedConfiguration();
if (this != newConfig) throw new InvalidDataException("Saved configuration is different from the supplied one."); // FIXME: Broken? Throws falsely.
//if (this != newConfig) throw new InvalidDataException("Saved configuration is different from the supplied one.");
} }
[NonSerialized] [NonSerialized]
@ -84,11 +85,12 @@ namespace QuickPlay
string filename = playerConfigUrl.Substring("file://".Length); string filename = playerConfigUrl.Substring("file://".Length);
var reader = new StreamReader(filename); var reader = new StreamReader(filename);
cfgProvider = new FileConfigurationProvider(reader); cfgProvider = new FileConfigurationProvider(reader);
} else if (playerConfigUrl == "default") { } else if (playerConfigUrl == "dummy") {
cfgProvider = new DummyConfigurationProvider(); cfgProvider = new DummyConfigurationProvider();
} else } else
{ {
throw new InvalidOperationException("Schema of player config URL not supported"); Android.Widget.Toast.MakeText(Android.App.Application.Context, "Schema of player config URL not supported, using dummy", Android.Widget.ToastLength.Short).Show();
cfgProvider = new DummyConfigurationProvider();
} }
PlayerConfiguration playercfg; PlayerConfiguration playercfg;
try try

@ -3,9 +3,11 @@ using Android.App;
using Android.OS; using Android.OS;
using Android.Views; using Android.Views;
using Android.Widget; using Android.Widget;
using Android.Content;
using Android.Support.V7.App; using Android.Support.V7.App;
using Toolbar = Android.Support.V7.Widget.Toolbar; using Toolbar = Android.Support.V7.Widget.Toolbar;
using GridLayoutManager = Android.Support.V7.Widget.GridLayoutManager; using GridLayoutManager = Android.Support.V7.Widget.GridLayoutManager;
using Android.Runtime;
namespace QuickPlay namespace QuickPlay
{ {
@ -25,15 +27,6 @@ namespace QuickPlay
appConfig = AppConfiguration.loadConfiguration(); appConfig = AppConfiguration.loadConfiguration();
var playerConfig = await appConfig.GetPlayerConfig(); var playerConfig = await appConfig.GetPlayerConfig();
currentPlayer = playerConfig.GetPlayer(); currentPlayer = playerConfig.GetPlayer();
try
{
await currentPlayer.ConnectAsync();
} catch (CannotConnectException e)
{
//TODO: View a toast with details and change some colors?
var t = Toast.MakeText(this, e.Message + ": " + e.InnerException.Message ?? "", ToastLength.Long);
t.Show();
}
// UI initialization // UI initialization
SetContentView(Resource.Layout.activity_main); SetContentView(Resource.Layout.activity_main);
@ -51,8 +44,7 @@ namespace QuickPlay
// Since this is rather complicated, it is in a separate method // Since this is rather complicated, it is in a separate method
InitializeRecyclerView(); InitializeRecyclerView();
// FIXME: This should be in OnResume... // I don't know why, but this needs to be here in order to show correct colors and player name.
// Refresh player info
OnPlayerUpdate(); OnPlayerUpdate();
} }
@ -77,9 +69,33 @@ namespace QuickPlay
{ {
if (item.ItemId == Resource.Id.action_settings) if (item.ItemId == Resource.Id.action_settings)
{ {
// Show the play bar // Set player config URL
var bar = FindViewById(Resource.Id.currentSongBar); var b = new Android.Support.V7.App.AlertDialog.Builder(this);
bar.Visibility = ViewStates.Visible; b.SetTitle("Player config URL");
var input = new EditText(this);
input.Text = appConfig.playerConfigUrl;
b.SetView(input);
b.SetPositiveButton("Set", delegate
{
string text = input.Text;
appConfig.playerConfigUrl = text;
appConfig.saveConfiguration();
Toast.MakeText(this, "Configuration saved, reloading", ToastLength.Short).Show();
var i = new Intent(this, typeof(MainActivity));
StartActivity(i);
});
b.SetNegativeButton("Scan QR", delegate {
try
{
var i = new Intent("com.google.zxing.client.android.SCAN");
StartActivityForResult(i, 0);
} catch (ActivityNotFoundException e) {
Toast.MakeText(this, "You need ZXing Barcode scanner for this", ToastLength.Long).Show();
}
});
b.SetCancelable(true);
b.Show();
} }
if (item.ItemId == Resource.Id.action_edit) if (item.ItemId == Resource.Id.action_edit)
@ -135,5 +151,51 @@ namespace QuickPlay
{ {
await currentPlayer.Play(song); await currentPlayer.Play(song);
} }
public async void OnResume()
{
base.OnResume();
// Reconnect the player
try
{
await currentPlayer.ConnectAsync();
} catch (CannotConnectException e)
{
//TODO: View a toast with details and change some colors?
var t = Toast.MakeText(this, e.Message + ": " + e.InnerException.Message ?? "", ToastLength.Long);
t.Show();
}
// Refresh player info
OnPlayerUpdate();
}
public void OnDestroy()
{
base.OnDestroy();
this.appConfig.saveConfiguration();
}
protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
{
base.OnActivityResult(requestCode, resultCode, data);
if (requestCode == 0)
{
if (resultCode == Result.Ok)
{
string url = data.GetStringExtra("SCAN_RESULT");
var cfg = new AppConfiguration { playerConfigUrl = url };
cfg.saveConfiguration();
Toast.MakeText(this, "Configuration saved, reloading", ToastLength.Short).Show();
var i = new Intent(this, typeof(MainActivity));
StartActivity(i);
}
else if (resultCode == Result.Canceled)
{
Toast.MakeText(this, "Could not load QR code", ToastLength.Short).Show();
}
}
}
} }
} }

@ -21,6 +21,8 @@ namespace QuickPlay
} } } }
public async Task Play(IPlayable song) public async Task Play(IPlayable song)
{ {
// Make sure we are connected
await this.ConnectAsync();
await mpd.SendAsync(new MpcCore.Commands.Player.Stop()); await mpd.SendAsync(new MpcCore.Commands.Player.Stop());
await SetReasonableOptions(); await SetReasonableOptions();
await mpd.SendAsync(new MpcCore.Commands.Queue.ClearQueue()); await mpd.SendAsync(new MpcCore.Commands.Queue.ClearQueue());
@ -29,6 +31,8 @@ namespace QuickPlay
} }
public async Task SetReasonableOptions() public async Task SetReasonableOptions()
{ {
// Make sure we are connected
await this.ConnectAsync();
await mpd.SendAsync(new MpcCore.Commands.Options.SetConsume(true)); await mpd.SendAsync(new MpcCore.Commands.Options.SetConsume(true));
await mpd.SendAsync(new MpcCore.Commands.Options.SetRandom(false)); await mpd.SendAsync(new MpcCore.Commands.Options.SetRandom(false));
await mpd.SendAsync(new MpcCore.Commands.Options.SetRepeat(false)); await mpd.SendAsync(new MpcCore.Commands.Options.SetRepeat(false));

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="0.1" package="cz.ledoian.android.quickplay" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="2" android:versionName="0.2" package="cz.ledoian.android.quickplay" android:installLocation="auto">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30" /> <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="30" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"></application> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"></application>

File diff suppressed because it is too large Load Diff

@ -99,8 +99,8 @@ the shown name.
Planned Features Planned Features
==== ====
- [ ] Working application configuration - [x] Working application configuration
- [ ] Player configuration sharing via QR codes - [x] Player configuration sharing via QR codes
- [ ] Showing current song progress - [ ] Showing current song progress
- [ ] Possibility to stop current song - [ ] Possibility to stop current song
- [ ] MPD player monitoring service - [ ] MPD player monitoring service
@ -109,7 +109,7 @@ Planned Features
Bugs Bugs
--- ---
- [ ] The MpcCore client seems to disconnect after some time for no apparent reason. None currently known :-)
The far future The far future
--- ---

@ -0,0 +1,59 @@
---
type: slide
---
# QuickPlay
## A simple app to quickly use remote players
---
# Features
Easy to learn
Central management
(Theoretically) extensible / modular
Usable on Android 4.4+ (Tested on Marshmallow)
(Android only)
---
# Architecture
```graphviz
digraph "Arch" {
layout=fdp
RP -> Player [style="dotted", dir="both"];
RP[label="Remote player"]
XML -> AppConfig -> PlayerConfig -> {Player Playables HTTP};
UI -> LayoutStrategies;
Person -> UI -> Playables -> Metadata
Metadata -> LayoutStrategies
HTTP[label="HTTP + INI"]
Person, RP, UI, XML, HTTP [shape=box]
}
```
---
# Issues
Understanding Android ecosystem is hard
- unintuitive naming
- complex hierarchy of support libraries
- I didn't find comphrehensive overview
- many APIs are replaced
Quite a lot of boilerplate code (esp. UI)
Visual Studio not refreshing Resource files.
No sensible MPD client library.
---
# Lessons learned
- Basic Android architecture knowledge
- Network and asynchronous coding
- Library patching
Loading…
Cancel
Save