From 27c74b1d3b608cdbbaa15f174348e45e0ee0acbd Mon Sep 17 00:00:00 2001 From: casey langen Date: Mon, 15 Jan 2018 21:37:19 -0800 Subject: [PATCH] Added the ability to modify the playback transport from the android client. --- .../remote/service/websocket/Messages.kt | 4 +- .../service/websocket/model/IDataProvider.kt | 5 +- .../service/websocket/model/IGainSettings.kt | 16 ------ .../service/websocket/model/ReplayGainMode.kt | 18 +++++++ .../service/websocket/model/TransportType.kt | 17 ++++++ .../model/impl/remote/RemoteDataProvider.kt | 27 +++++++++- .../model/impl/remote/RemoteGainSettings.kt | 5 +- .../activity/RemoteSettingsActivity.kt | 45 ++++++++++++---- .../viewmodel/RemoteSettingsViewModel.kt | 54 ++++++++++++------- .../res/layout/activity_remote_settings.xml | 22 +++++++- .../app/src/main/res/values/strings.xml | 3 ++ .../main/res/values/transport_type_array.xml | 7 +++ 12 files changed, 173 insertions(+), 50 deletions(-) create mode 100644 src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/ReplayGainMode.kt create mode 100644 src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/TransportType.kt create mode 100644 src/musikdroid/app/src/main/res/values/transport_type_array.xml diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/Messages.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/Messages.kt index f04c8699b..3948ffa0c 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/Messages.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/Messages.kt @@ -36,7 +36,9 @@ class Messages { SetDefaultOutputDriver("set_default_output_driver"), GetGainSettings("get_gain_settings"), UpdateGainSettings("update_gain_settings"), - RunIndexer("run_indexer"); + RunIndexer("run_indexer"), + GetTransportType("get_transport_type"), + SetTransportType("set_transport_type"); override fun toString(): String = rawValue fun matches(name: String): Boolean = (rawValue == name) diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IDataProvider.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IDataProvider.kt index 6b65a9eeb..384f3320e 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IDataProvider.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IDataProvider.kt @@ -52,10 +52,13 @@ interface IDataProvider { fun setDefaultOutputDriver(driverName: String, deviceId: String = "default"): Observable fun getGainSettings(): Observable - fun updateGainSettings(replayGainMode: IGainSettings.ReplayGainMode, preampGain: Float): Observable + fun updateGainSettings(replayGainMode: ReplayGainMode, preampGain: Float): Observable fun reindexMetadata(): Observable fun rebuildMetadata(): Observable + fun getTransportType(): Observable + fun setTransportType(type: TransportType): Observable + val state: State } diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IGainSettings.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IGainSettings.kt index ce8def1ce..3057680c7 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IGainSettings.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/IGainSettings.kt @@ -1,22 +1,6 @@ package io.casey.musikcube.remote.service.websocket.model interface IGainSettings { - enum class ReplayGainMode(val rawValue: String) { - Disabled("disabled"), - Album("album"), - Track("track"); - - companion object { - fun find(raw: String): ReplayGainMode { - values().forEach { - if (it.rawValue == raw) { - return it - } - } - return Disabled - } - } - } val replayGainMode: ReplayGainMode val preampGain: Float diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/ReplayGainMode.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/ReplayGainMode.kt new file mode 100644 index 000000000..f3e719aab --- /dev/null +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/ReplayGainMode.kt @@ -0,0 +1,18 @@ +package io.casey.musikcube.remote.service.websocket.model + +enum class ReplayGainMode(val rawValue: String) { + Disabled("disabled"), + Album("album"), + Track("track"); + + companion object { + fun find(raw: String): ReplayGainMode { + values().forEach { + if (it.rawValue == raw) { + return it + } + } + return Disabled + } + } +} \ No newline at end of file diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/TransportType.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/TransportType.kt new file mode 100644 index 000000000..8f99bb333 --- /dev/null +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/TransportType.kt @@ -0,0 +1,17 @@ +package io.casey.musikcube.remote.service.websocket.model + +enum class TransportType(val rawValue: String) { + Gapless("gapless"), + Crossfade("crossfade"); + + companion object { + fun find(raw: String): TransportType { + values().forEach { + if (it.rawValue == raw) { + return it + } + } + return Gapless + } + } +} \ No newline at end of file diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteDataProvider.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteDataProvider.kt index c8998ed44..b6a570b81 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteDataProvider.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteDataProvider.kt @@ -435,7 +435,7 @@ class RemoteDataProvider(private val service: WebSocketService) : IDataProvider .observeOn(AndroidSchedulers.mainThread()) } - override fun updateGainSettings(replayGainMode: IGainSettings.ReplayGainMode, preampGain: Float): Observable { + override fun updateGainSettings(replayGainMode: ReplayGainMode, preampGain: Float): Observable { val message = SocketMessage.Builder .request(Messages.Request.UpdateGainSettings) .addOption(Messages.Key.REPLAYGAIN_MODE, replayGainMode.rawValue) @@ -455,6 +455,31 @@ class RemoteDataProvider(private val service: WebSocketService) : IDataProvider return runIndexer(Messages.Value.REBUILD) } + override fun getTransportType(): Observable { + val message = SocketMessage.Builder + .request(Messages.Request.GetTransportType) + .build() + + return service.observe(message, client) + .flatMap { socketMessage -> + Observable.just(TransportType.find( + socketMessage.getStringOption(Messages.Key.TYPE, + TransportType.Gapless.rawValue))) + } + .observeOn(AndroidSchedulers.mainThread()) + } + + override fun setTransportType(type: TransportType): Observable { + val message = SocketMessage.Builder + .request(Messages.Request.SetTransportType) + .addOption(Messages.Key.TYPE, type.rawValue) + .build() + + return service.observe(message, client) + .flatMap { socketMessage -> isSuccessful(socketMessage) } + .observeOn(AndroidSchedulers.mainThread()) + } + override fun observeState(): Observable> = connectionStatePublisher.observeOn(AndroidSchedulers.mainThread()) diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteGainSettings.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteGainSettings.kt index b4acdbf7a..79a753d9e 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteGainSettings.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/service/websocket/model/impl/remote/RemoteGainSettings.kt @@ -2,11 +2,12 @@ package io.casey.musikcube.remote.service.websocket.model.impl.remote import io.casey.musikcube.remote.service.websocket.Messages import io.casey.musikcube.remote.service.websocket.model.IGainSettings +import io.casey.musikcube.remote.service.websocket.model.ReplayGainMode import org.json.JSONObject class RemoteGainSettings(val json: JSONObject): IGainSettings { - override val replayGainMode: IGainSettings.ReplayGainMode - get() = IGainSettings.ReplayGainMode.find(json.optString(Messages.Key.REPLAYGAIN_MODE, "disabled")) + override val replayGainMode: ReplayGainMode + get() = ReplayGainMode.find(json.optString(Messages.Key.REPLAYGAIN_MODE, "disabled")) override val preampGain: Float get() = json.optDouble(Messages.Key.PREAMP_GAIN, 0.0).toFloat() } \ No newline at end of file diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/activity/RemoteSettingsActivity.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/activity/RemoteSettingsActivity.kt index a359d9650..9ba8d33c2 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/activity/RemoteSettingsActivity.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/activity/RemoteSettingsActivity.kt @@ -8,8 +8,9 @@ import android.widget.* import io.casey.musikcube.remote.R import io.casey.musikcube.remote.framework.ViewModel import io.casey.musikcube.remote.service.websocket.model.IDevice -import io.casey.musikcube.remote.service.websocket.model.IGainSettings import io.casey.musikcube.remote.service.websocket.model.IOutput +import io.casey.musikcube.remote.service.websocket.model.ReplayGainMode +import io.casey.musikcube.remote.service.websocket.model.TransportType import io.casey.musikcube.remote.ui.settings.viewmodel.RemoteSettingsViewModel import io.casey.musikcube.remote.ui.shared.activity.BaseActivity import io.casey.musikcube.remote.ui.shared.extension.slideThisDown @@ -25,6 +26,7 @@ class RemoteSettingsActivity: BaseActivity() { private lateinit var loadingOverlay: View private lateinit var driverSpinner: Spinner private lateinit var deviceSpinner: Spinner + private lateinit var transportSpinner: Spinner private lateinit var replayGainSpinner: Spinner private lateinit var preampSeekbar: SeekBar private lateinit var preampTextView: TextView @@ -45,6 +47,7 @@ class RemoteSettingsActivity: BaseActivity() { loadingOverlay = findViewById(R.id.loading_overlay) driverSpinner = findViewById(R.id.output_driver_spinner) deviceSpinner = findViewById(R.id.output_device_spinner) + transportSpinner = findViewById(R.id.transport_spinner) replayGainSpinner = findViewById(R.id.replaygain_spinner) preampSeekbar = findViewById(R.id.gain_seekbar) preampTextView = findViewById(R.id.gain_textview) @@ -91,6 +94,7 @@ class RemoteSettingsActivity: BaseActivity() { driverSpinner.onItemSelectedListener = driverChangeListener deviceSpinner.adapter = DevicesAdapter(viewModel.devicesAt(viewModel.selectedDriverIndex)) deviceSpinner.setSelection(viewModel.selectedDeviceIndex) + transportSpinner.setSelection(TRANSPORT_TYPE_TO_INDEX[viewModel.transportType]!!) replayGainSpinner.setSelection(REPLAYGAIN_MODE_TO_INDEX[viewModel.replayGainMode]!!) initialized = true } @@ -119,7 +123,9 @@ class RemoteSettingsActivity: BaseActivity() { } } - viewModel.save(replayGainMode, preampGain, driverName, deviceId) + val transport = indexToTransportType(transportSpinner.selectedItemPosition) + + viewModel.save(replayGainMode, preampGain, transport, driverName, deviceId) } private fun initListeners() { @@ -145,10 +151,10 @@ class RemoteSettingsActivity: BaseActivity() { /* replaygain / preamp */ val replayGainModes = ArrayAdapter.createFromResource( - this, R.array.replaygain_mode_array, android.R.layout.simple_spinner_dropdown_item) + this, R.array.replaygain_mode_array, + android.R.layout.simple_spinner_dropdown_item) replayGainModes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - replayGainSpinner.adapter = replayGainModes preampSeekbar.setOnSeekBarChangeListener(object:SeekBar.OnSeekBarChangeListener { @@ -166,6 +172,14 @@ class RemoteSettingsActivity: BaseActivity() { }) preampSeekbar.progress = 2000 + + /* transport */ + val transportModes = ArrayAdapter.createFromResource( + this, R.array.transport_type_array, + android.R.layout.simple_spinner_dropdown_item) + + transportModes.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + transportSpinner.adapter = transportModes } private fun initObservers() { @@ -238,17 +252,30 @@ class RemoteSettingsActivity: BaseActivity() { companion object { val REPLAYGAIN_MODE_TO_INDEX = mapOf( - IGainSettings.ReplayGainMode.Disabled to 0, - IGainSettings.ReplayGainMode.Track to 1, - IGainSettings.ReplayGainMode.Album to 2) + ReplayGainMode.Disabled to 0, + ReplayGainMode.Track to 1, + ReplayGainMode.Album to 2) - fun indexToReplayGain(index: Int): IGainSettings.ReplayGainMode { + val TRANSPORT_TYPE_TO_INDEX = mapOf( + TransportType.Gapless to 0, + TransportType.Crossfade to 1) + + fun indexToReplayGain(index: Int): ReplayGainMode { REPLAYGAIN_MODE_TO_INDEX.forEach { if (it.value == index) { return it.key } } - return IGainSettings.ReplayGainMode.Disabled + return ReplayGainMode.Disabled + } + + fun indexToTransportType(index: Int): TransportType { + TRANSPORT_TYPE_TO_INDEX.forEach { + if (it.value == index) { + return it.key + } + } + return TransportType.Gapless } fun getStartIntent(context: Context):Intent = diff --git a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/viewmodel/RemoteSettingsViewModel.kt b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/viewmodel/RemoteSettingsViewModel.kt index 993e2bf53..e99b73fbd 100644 --- a/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/viewmodel/RemoteSettingsViewModel.kt +++ b/src/musikdroid/app/src/main/java/io/casey/musikcube/remote/ui/settings/viewmodel/RemoteSettingsViewModel.kt @@ -7,6 +7,7 @@ import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.functions.BiFunction +import io.reactivex.functions.Function3 import io.reactivex.rxkotlin.subscribeBy class RemoteSettingsViewModel: ViewModel() { @@ -33,6 +34,11 @@ class RemoteSettingsViewModel: ViewModel() { } } + var transportType: TransportType = TransportType.Gapless + private set(value) { + field = value + } + val driverName: String get() { outputs?.let { @@ -70,12 +76,12 @@ class RemoteSettingsViewModel: ViewModel() { return Math.max(0, deviceIndex) } - val replayGainMode: IGainSettings.ReplayGainMode + val replayGainMode: ReplayGainMode get() { gain?.let { return it.replayGainMode } - return IGainSettings.ReplayGainMode.Disabled + return ReplayGainMode.Disabled } val preampGain: Float @@ -103,12 +109,19 @@ class RemoteSettingsViewModel: ViewModel() { return listOf() } - private fun save(replayGainMode: IGainSettings.ReplayGainMode, preampGain: Float) + private fun save(replayGainMode: ReplayGainMode, + preampGain: Float, + transport: TransportType) { provider?.let { val oldState = state state = State.Saving - it.updateGainSettings(replayGainMode, preampGain) + val gainQuery = it.updateGainSettings(replayGainMode, preampGain) + val transportQuery = it.setTransportType(transport) + Observable.zip( + gainQuery, + transportQuery, + BiFunction { b1, b2 -> b1 && b2 }) .observeOn(AndroidSchedulers.mainThread()) .subscribeBy( onNext = { @@ -119,13 +132,14 @@ class RemoteSettingsViewModel: ViewModel() { } } - fun save(replayGainMode: IGainSettings.ReplayGainMode, + fun save(replayGainMode: ReplayGainMode, preampGain: Float, + transport: TransportType, outputDriver: String, outputDeviceId: String) { if (Strings.empty(outputDriver)) { - save(replayGainMode, preampGain) + save(replayGainMode, preampGain, transport) } else { provider?.let { @@ -133,7 +147,12 @@ class RemoteSettingsViewModel: ViewModel() { state = State.Saving val gainQuery = it.updateGainSettings(replayGainMode, preampGain) val outputQuery = it.setDefaultOutputDriver(outputDriver, outputDeviceId) - Observable.zip(gainQuery, outputQuery, BiFunction { b1, b2 -> b1 && b2 }) + val transportQuery = it.setTransportType(transport) + Observable.zip( + gainQuery, + outputQuery, + transportQuery, + Function3 { b1, b2, b3 -> b1 && b2 && b3 }) .observeOn(AndroidSchedulers.mainThread()) .subscribeBy( onNext = { @@ -162,22 +181,21 @@ class RemoteSettingsViewModel: ViewModel() { state = State.Loading val gainQuery = it.getGainSettings() val outputsQuery = it.listOutputDrivers() - Observable.zip>( + val transportQuery = it.getTransportType() + Observable.zip( gainQuery, outputsQuery, - BiFunction { gainSettings, outputs -> - Pair(gainSettings, outputs) + transportQuery, + Function3 { gainSettings, outputs, transportType -> + this.gain = gainSettings + this.outputs = outputs + this.transportType = transportType + true }) .observeOn(AndroidSchedulers.mainThread()) .subscribeBy( - onNext = { - gain = it.first - outputs = it.second - state = State.Ready - }, - onError = { - state = State.Disconnected - }) + onNext = { state = State.Ready }, + onError = { state = State.Disconnected }) } } diff --git a/src/musikdroid/app/src/main/res/layout/activity_remote_settings.xml b/src/musikdroid/app/src/main/res/layout/activity_remote_settings.xml index 7736ddc7d..a59562722 100644 --- a/src/musikdroid/app/src/main/res/layout/activity_remote_settings.xml +++ b/src/musikdroid/app/src/main/res/layout/activity_remote_settings.xml @@ -46,7 +46,24 @@ + android:layout_height="24dp"/> + + + + + + + android:layout_height="24dp"/> metadata library rescan rebuild + playback transport + gapless + crossfade disabled track album diff --git a/src/musikdroid/app/src/main/res/values/transport_type_array.xml b/src/musikdroid/app/src/main/res/values/transport_type_array.xml new file mode 100644 index 000000000..fb79ac96f --- /dev/null +++ b/src/musikdroid/app/src/main/res/values/transport_type_array.xml @@ -0,0 +1,7 @@ + + + + @string/transport_type_gapless + @string/transport_type_crossfade + + \ No newline at end of file