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
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]
@ -84,11 +85,12 @@ namespace QuickPlay
string filename = playerConfigUrl.Substring("file://".Length);
var reader = new StreamReader(filename);
cfgProvider = new FileConfigurationProvider(reader);
} else if (playerConfigUrl == "default") {
} else if (playerConfigUrl == "dummy") {
cfgProvider = new DummyConfigurationProvider();
} 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;
try

@ -3,9 +3,11 @@ using Android.App;
using Android.OS;
using Android.Views;
using Android.Widget;
using Android.Content;
using Android.Support.V7.App;
using Toolbar = Android.Support.V7.Widget.Toolbar;
using GridLayoutManager = Android.Support.V7.Widget.GridLayoutManager;
using Android.Runtime;
namespace QuickPlay
{
@ -25,15 +27,6 @@ namespace QuickPlay
appConfig = AppConfiguration.loadConfiguration();
var playerConfig = await appConfig.GetPlayerConfig();
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
SetContentView(Resource.Layout.activity_main);
@ -51,8 +44,7 @@ namespace QuickPlay
// Since this is rather complicated, it is in a separate method
InitializeRecyclerView();
// FIXME: This should be in OnResume...
// Refresh player info
// I don't know why, but this needs to be here in order to show correct colors and player name.
OnPlayerUpdate();
}
@ -77,9 +69,33 @@ namespace QuickPlay
{
if (item.ItemId == Resource.Id.action_settings)
{
// Show the play bar
var bar = FindViewById(Resource.Id.currentSongBar);
bar.Visibility = ViewStates.Visible;
// Set player config URL
var b = new Android.Support.V7.App.AlertDialog.Builder(this);
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)
@ -135,5 +151,51 @@ namespace QuickPlay
{
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)
{
// Make sure we are connected
await this.ConnectAsync();
await mpd.SendAsync(new MpcCore.Commands.Player.Stop());
await SetReasonableOptions();
await mpd.SendAsync(new MpcCore.Commands.Queue.ClearQueue());
@ -29,6 +31,8 @@ namespace QuickPlay
}
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.SetRandom(false));
await mpd.SendAsync(new MpcCore.Commands.Options.SetRepeat(false));

@ -1,5 +1,5 @@
<?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-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>

File diff suppressed because it is too large Load Diff

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