Rework ActiveServer handling.
Remove blocking call on setting the server. Implement offline server display more cleanly. Reconfigure the SourceFactory when the active server has changed
This commit is contained in:
parent
6da83db9df
commit
3ca25ed1c6
|
@ -2,8 +2,6 @@
|
||||||
<SmellBaseline>
|
<SmellBaseline>
|
||||||
<ManuallySuppressedIssues/>
|
<ManuallySuppressedIssues/>
|
||||||
<CurrentIssues>
|
<CurrentIssues>
|
||||||
<ID>ComplexCondition:DownloadHandler.kt$DownloadHandler.<no name provided>$!append && !playNext && !unpin && !background</ID>
|
|
||||||
<ID>ComplexMethod:DownloadFile.kt$DownloadFile.DownloadTask$override fun execute()</ID>
|
|
||||||
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.<no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
|
<ID>ImplicitDefaultLocale:EditServerFragment.kt$EditServerFragment.<no name provided>$String.format( "%s %s", resources.getString(R.string.settings_connection_failure), getErrorMessage(error) )</ID>
|
||||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Failed to write log to %s", file)</ID>
|
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Failed to write log to %s", file)</ID>
|
||||||
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Log file rotated, logging into file %s", file?.name)</ID>
|
<ID>ImplicitDefaultLocale:FileLoggerTree.kt$FileLoggerTree$String.format("Log file rotated, logging into file %s", file?.name)</ID>
|
||||||
|
@ -12,7 +10,7 @@
|
||||||
<ID>LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
|
<ID>LongMethod:EditServerFragment.kt$EditServerFragment$override fun onViewCreated(view: View, savedInstanceState: Bundle?)</ID>
|
||||||
<ID>LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)</ID>
|
<ID>LongMethod:NavigationActivity.kt$NavigationActivity$override fun onCreate(savedInstanceState: Bundle?)</ID>
|
||||||
<ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )</ID>
|
<ID>LongMethod:ShareHandler.kt$ShareHandler$private fun showDialog( fragment: Fragment, shareDetails: ShareDetails, swipe: SwipeRefreshLayout?, cancellationToken: CancellationToken )</ID>
|
||||||
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, private var data: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
|
<ID>LongParameterList:ServerRowAdapter.kt$ServerRowAdapter$( private var context: Context, passedData: Array<ServerSetting>, private val model: ServerSettingsModel, private val activeServerProvider: ActiveServerProvider, private val manageMode: Boolean, private val serverDeletedCallback: ((Int) -> Unit), private val serverEditRequestedCallback: ((Int) -> Unit) )</ID>
|
||||||
<ID>MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192</ID>
|
<ID>MagicNumber:ActiveServerProvider.kt$ActiveServerProvider$8192</ID>
|
||||||
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$0.05f</ID>
|
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$0.05f</ID>
|
||||||
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$50</ID>
|
<ID>MagicNumber:JukeboxMediaPlayer.kt$JukeboxMediaPlayer$50</ID>
|
||||||
|
|
|
@ -40,7 +40,7 @@ import androidx.navigation.ui.setupWithNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import com.google.android.material.button.MaterialButton
|
import com.google.android.material.button.MaterialButton
|
||||||
import com.google.android.material.navigation.NavigationView
|
import com.google.android.material.navigation.NavigationView
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
|
@ -54,6 +54,7 @@ import org.moire.ultrasonic.service.DownloadFile
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController
|
import org.moire.ultrasonic.service.MediaPlayerController
|
||||||
import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
|
import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
|
||||||
import org.moire.ultrasonic.service.RxBus
|
import org.moire.ultrasonic.service.RxBus
|
||||||
|
import org.moire.ultrasonic.service.plusAssign
|
||||||
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.InfoDialog
|
import org.moire.ultrasonic.util.InfoDialog
|
||||||
|
@ -83,8 +84,8 @@ class NavigationActivity : AppCompatActivity() {
|
||||||
private var headerBackgroundImage: ImageView? = null
|
private var headerBackgroundImage: ImageView? = null
|
||||||
|
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
private var themeChangedEventSubscription: Disposable? = null
|
|
||||||
private var playerStateSubscription: Disposable? = null
|
private var rxBusSubscription: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
private val serverSettingsModel: ServerSettingsModel by viewModel()
|
private val serverSettingsModel: ServerSettingsModel by viewModel()
|
||||||
private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
|
private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
|
||||||
|
@ -181,25 +182,25 @@ class NavigationActivity : AppCompatActivity() {
|
||||||
hideNowPlaying()
|
hideNowPlaying()
|
||||||
}
|
}
|
||||||
|
|
||||||
playerStateSubscription = RxBus.playerStateObservable.subscribe {
|
rxBusSubscription += RxBus.playerStateObservable.subscribe {
|
||||||
if (it.state === PlayerState.STARTED || it.state === PlayerState.PAUSED)
|
if (it.state === PlayerState.STARTED || it.state === PlayerState.PAUSED)
|
||||||
showNowPlaying()
|
showNowPlaying()
|
||||||
else
|
else
|
||||||
hideNowPlaying()
|
hideNowPlaying()
|
||||||
}
|
}
|
||||||
|
|
||||||
themeChangedEventSubscription = RxBus.themeChangedEventObservable.subscribe {
|
rxBusSubscription += RxBus.themeChangedEventObservable.subscribe {
|
||||||
recreate()
|
recreate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rxBusSubscription += RxBus.activeServerChangeObservable.subscribe {
|
||||||
|
updateNavigationHeaderForServer()
|
||||||
|
}
|
||||||
|
|
||||||
serverRepository.liveServerCount().observe(this) { count ->
|
serverRepository.liveServerCount().observe(this) { count ->
|
||||||
cachedServerCount = count ?: 0
|
cachedServerCount = count ?: 0
|
||||||
updateNavigationHeaderForServer()
|
updateNavigationHeaderForServer()
|
||||||
}
|
}
|
||||||
|
|
||||||
ActiveServerProvider.liveActiveServerId.observe(this) {
|
|
||||||
updateNavigationHeaderForServer()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNavigationHeaderForServer() {
|
private fun updateNavigationHeaderForServer() {
|
||||||
|
@ -239,8 +240,7 @@ class NavigationActivity : AppCompatActivity() {
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
themeChangedEventSubscription?.dispose()
|
rxBusSubscription.dispose()
|
||||||
playerStateSubscription?.dispose()
|
|
||||||
imageLoaderProvider.clearImageLoader()
|
imageLoaderProvider.clearImageLoader()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.moire.ultrasonic.util.Util
|
||||||
*/
|
*/
|
||||||
internal class ServerRowAdapter(
|
internal class ServerRowAdapter(
|
||||||
private var context: Context,
|
private var context: Context,
|
||||||
private var data: Array<ServerSetting>,
|
passedData: Array<ServerSetting>,
|
||||||
private val model: ServerSettingsModel,
|
private val model: ServerSettingsModel,
|
||||||
private val activeServerProvider: ActiveServerProvider,
|
private val activeServerProvider: ActiveServerProvider,
|
||||||
private val manageMode: Boolean,
|
private val manageMode: Boolean,
|
||||||
|
@ -38,6 +38,12 @@ internal class ServerRowAdapter(
|
||||||
private val serverEditRequestedCallback: ((Int) -> Unit)
|
private val serverEditRequestedCallback: ((Int) -> Unit)
|
||||||
) : BaseAdapter() {
|
) : BaseAdapter() {
|
||||||
|
|
||||||
|
private var data: MutableList<ServerSetting> = mutableListOf()
|
||||||
|
|
||||||
|
init {
|
||||||
|
setData(passedData)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val MENU_ID_EDIT = 1
|
private const val MENU_ID_EDIT = 1
|
||||||
private const val MENU_ID_DELETE = 2
|
private const val MENU_ID_DELETE = 2
|
||||||
|
@ -49,12 +55,19 @@ internal class ServerRowAdapter(
|
||||||
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
|
||||||
|
|
||||||
fun setData(data: Array<ServerSetting>) {
|
fun setData(data: Array<ServerSetting>) {
|
||||||
this.data = data
|
this.data.clear()
|
||||||
|
|
||||||
|
// In read mode show the offline server as well
|
||||||
|
if (!manageMode) {
|
||||||
|
this.data.add(ActiveServerProvider.OFFLINE_DB)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.data.addAll(data)
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
return if (manageMode) data.size else data.size + 1
|
return data.size
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItem(position: Int): Any {
|
override fun getItem(position: Int): Any {
|
||||||
|
@ -69,11 +82,11 @@ internal class ServerRowAdapter(
|
||||||
* Creates the Row representation of a Server Setting
|
* Creates the Row representation of a Server Setting
|
||||||
*/
|
*/
|
||||||
@Suppress("LongMethod")
|
@Suppress("LongMethod")
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
|
override fun getView(pos: Int, convertView: View?, parent: ViewGroup?): View? {
|
||||||
var index = position
|
var position = pos
|
||||||
|
|
||||||
// Skip "Offline" in manage mode
|
// Skip "Offline" in manage mode
|
||||||
if (manageMode) index++
|
if (manageMode) position++
|
||||||
|
|
||||||
var vi: View? = convertView
|
var vi: View? = convertView
|
||||||
if (vi == null) vi = inflater.inflate(R.layout.server_row, parent, false)
|
if (vi == null) vi = inflater.inflate(R.layout.server_row, parent, false)
|
||||||
|
@ -83,22 +96,17 @@ internal class ServerRowAdapter(
|
||||||
val layout = vi?.findViewById<ConstraintLayout>(R.id.server_layout)
|
val layout = vi?.findViewById<ConstraintLayout>(R.id.server_layout)
|
||||||
val image = vi?.findViewById<ImageView>(R.id.server_image)
|
val image = vi?.findViewById<ImageView>(R.id.server_image)
|
||||||
val serverMenu = vi?.findViewById<ImageButton>(R.id.server_menu)
|
val serverMenu = vi?.findViewById<ImageButton>(R.id.server_menu)
|
||||||
val setting = data.singleOrNull { t -> t.index == index }
|
val setting = data.singleOrNull { t -> t.index == position }
|
||||||
|
|
||||||
if (index == 0) {
|
|
||||||
text?.text = context.getString(R.string.main_offline)
|
|
||||||
description?.text = ""
|
|
||||||
} else {
|
|
||||||
text?.text = setting?.name ?: ""
|
text?.text = setting?.name ?: ""
|
||||||
description?.text = setting?.url ?: ""
|
description?.text = setting?.url ?: ""
|
||||||
if (setting == null) serverMenu?.visibility = View.INVISIBLE
|
if (setting == null) serverMenu?.visibility = View.INVISIBLE
|
||||||
}
|
|
||||||
|
|
||||||
val icon: Drawable?
|
val icon: Drawable?
|
||||||
val background: Drawable?
|
val background: Drawable?
|
||||||
|
|
||||||
// Configure icons for the row
|
// Configure icons for the row
|
||||||
if (index == 0) {
|
if (setting?.id == ActiveServerProvider.OFFLINE_DB_ID) {
|
||||||
serverMenu?.visibility = View.INVISIBLE
|
serverMenu?.visibility = View.INVISIBLE
|
||||||
icon = Util.getDrawableFromAttribute(context, R.attr.screen_on_off)
|
icon = Util.getDrawableFromAttribute(context, R.attr.screen_on_off)
|
||||||
background = ContextCompat.getDrawable(context, R.drawable.circle)
|
background = ContextCompat.getDrawable(context, R.drawable.circle)
|
||||||
|
@ -116,7 +124,7 @@ internal class ServerRowAdapter(
|
||||||
image?.background = background
|
image?.background = background
|
||||||
|
|
||||||
// Highlight the Active Server's row by changing its background
|
// Highlight the Active Server's row by changing its background
|
||||||
if (index == activeServerProvider.getActiveServer().index) {
|
if (position == activeServerProvider.getActiveServer().index) {
|
||||||
layout?.background = ContextCompat.getDrawable(context, R.drawable.select_ripple)
|
layout?.background = ContextCompat.getDrawable(context, R.drawable.select_ripple)
|
||||||
} else {
|
} else {
|
||||||
layout?.background = ContextCompat.getDrawable(context, R.drawable.default_ripple)
|
layout?.background = ContextCompat.getDrawable(context, R.drawable.default_ripple)
|
||||||
|
@ -128,7 +136,7 @@ internal class ServerRowAdapter(
|
||||||
R.drawable.select_ripple_circle
|
R.drawable.select_ripple_circle
|
||||||
)
|
)
|
||||||
|
|
||||||
serverMenu?.setOnClickListener { view -> serverMenuClick(view, index) }
|
serverMenu?.setOnClickListener { view -> serverMenuClick(view, position) }
|
||||||
|
|
||||||
return vi
|
return vi
|
||||||
}
|
}
|
||||||
|
@ -192,7 +200,8 @@ internal class ServerRowAdapter(
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
MENU_ID_DELETE -> {
|
MENU_ID_DELETE -> {
|
||||||
serverDeletedCallback.invoke(position)
|
val server = getItem(position) as ServerSetting
|
||||||
|
serverDeletedCallback.invoke(server.id)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
MENU_ID_UP -> {
|
MENU_ID_UP -> {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package org.moire.ultrasonic.data
|
package org.moire.ultrasonic.data
|
||||||
|
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
@ -11,6 +10,7 @@ import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.di.DB_FILENAME
|
import org.moire.ultrasonic.di.DB_FILENAME
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory.resetMusicService
|
import org.moire.ultrasonic.service.MusicServiceFactory.resetMusicService
|
||||||
|
import org.moire.ultrasonic.service.RxBus
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
|
@ -52,12 +52,32 @@ class ActiveServerProvider(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to Offline
|
// Fallback to Offline
|
||||||
setActiveServerId(OFFLINE_DB_ID)
|
setActiveServerById(OFFLINE_DB_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return OFFLINE_DB
|
return OFFLINE_DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the index (sort order) of a server to its id (unique)
|
||||||
|
* @param index: The index of the server in the server selector
|
||||||
|
* @return id: The unique id of the server
|
||||||
|
*/
|
||||||
|
fun getServerIdFromIndex(index: Int): Int {
|
||||||
|
if (index <= OFFLINE_DB_INDEX) {
|
||||||
|
// Offline mode is selected
|
||||||
|
return OFFLINE_DB_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
var id: Int
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
id = repository.findByIndex(index)?.id ?: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Active Server by the Server Index in the Server Selector List
|
* Sets the Active Server by the Server Index in the Server Selector List
|
||||||
* @param index: The index of the Active Server in the Server Selector List
|
* @param index: The index of the Active Server in the Server Selector List
|
||||||
|
@ -66,13 +86,13 @@ class ActiveServerProvider(
|
||||||
Timber.d("setActiveServerByIndex $index")
|
Timber.d("setActiveServerByIndex $index")
|
||||||
if (index <= OFFLINE_DB_INDEX) {
|
if (index <= OFFLINE_DB_INDEX) {
|
||||||
// Offline mode is selected
|
// Offline mode is selected
|
||||||
setActiveServerId(OFFLINE_DB_ID)
|
setActiveServerById(OFFLINE_DB_ID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
launch {
|
launch {
|
||||||
val serverId = repository.findByIndex(index)?.id ?: 0
|
val serverId = repository.findByIndex(index)?.id ?: 0
|
||||||
setActiveServerId(serverId)
|
setActiveServerById(serverId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,8 +200,6 @@ class ActiveServerProvider(
|
||||||
minimumApiVersion = null
|
minimumApiVersion = null
|
||||||
)
|
)
|
||||||
|
|
||||||
val liveActiveServerId: MutableLiveData<Int> = MutableLiveData(getActiveServerId())
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Queries if the Active Server is the "Offline" mode of Ultrasonic
|
* Queries if the Active Server is the "Offline" mode of Ultrasonic
|
||||||
* @return True, if the "Offline" mode is selected
|
* @return True, if the "Offline" mode is selected
|
||||||
|
@ -198,13 +216,16 @@ class ActiveServerProvider(
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the Id of the Active Server
|
* Sets the Active Server by its unique id
|
||||||
|
* @param serverId: The id of the desired server
|
||||||
*/
|
*/
|
||||||
fun setActiveServerId(serverId: Int) {
|
fun setActiveServerById(serverId: Int) {
|
||||||
resetMusicService()
|
resetMusicService()
|
||||||
|
|
||||||
Settings.activeServer = serverId
|
Settings.activeServer = serverId
|
||||||
liveActiveServerId.postValue(serverId)
|
|
||||||
|
Timber.i("setActiveServerById done, new id: %s", serverId)
|
||||||
|
RxBus.activeServerChangePublisher.onNext(serverId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -9,14 +9,12 @@ import android.widget.ListView
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
|
import org.koin.androidx.viewmodel.ext.android.viewModel
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.adapters.ServerRowAdapter
|
import org.moire.ultrasonic.adapters.ServerRowAdapter
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
|
import org.moire.ultrasonic.data.ServerSetting
|
||||||
import org.moire.ultrasonic.fragment.EditServerFragment.Companion.EDIT_SERVER_INTENT_INDEX
|
import org.moire.ultrasonic.fragment.EditServerFragment.Companion.EDIT_SERVER_INTENT_INDEX
|
||||||
import org.moire.ultrasonic.model.ServerSettingsModel
|
import org.moire.ultrasonic.model.ServerSettingsModel
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController
|
import org.moire.ultrasonic.service.MediaPlayerController
|
||||||
|
@ -26,6 +24,8 @@ import timber.log.Timber
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays the list of configured servers, they can be selected or edited
|
* Displays the list of configured servers, they can be selected or edited
|
||||||
|
*
|
||||||
|
* TODO: Manage mode is unused. Remove it...
|
||||||
*/
|
*/
|
||||||
class ServerSelectorFragment : Fragment() {
|
class ServerSelectorFragment : Fragment() {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -59,6 +59,7 @@ class ServerSelectorFragment : Fragment() {
|
||||||
SERVER_SELECTOR_MANAGE_MODE,
|
SERVER_SELECTOR_MANAGE_MODE,
|
||||||
false
|
false
|
||||||
) ?: false
|
) ?: false
|
||||||
|
|
||||||
if (manageMode) {
|
if (manageMode) {
|
||||||
FragmentTitle.setTitle(this, R.string.settings_server_manage_servers)
|
FragmentTitle.setTitle(this, R.string.settings_server_manage_servers)
|
||||||
} else {
|
} else {
|
||||||
|
@ -72,31 +73,26 @@ class ServerSelectorFragment : Fragment() {
|
||||||
serverSettingsModel,
|
serverSettingsModel,
|
||||||
activeServerProvider,
|
activeServerProvider,
|
||||||
manageMode,
|
manageMode,
|
||||||
{
|
::deleteServerById,
|
||||||
i ->
|
::editServerByIndex
|
||||||
onServerDeleted(i)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
i ->
|
|
||||||
editServer(i)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
listView?.adapter = serverRowAdapter
|
listView?.adapter = serverRowAdapter
|
||||||
|
|
||||||
listView?.onItemClickListener = AdapterView.OnItemClickListener {
|
listView?.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ ->
|
||||||
_, _, position, _ ->
|
|
||||||
|
val server = parent.getItemAtPosition(position) as ServerSetting
|
||||||
if (manageMode) {
|
if (manageMode) {
|
||||||
editServer(position + 1)
|
editServerByIndex(position + 1)
|
||||||
} else {
|
} else {
|
||||||
setActiveServer(position)
|
setActiveServerById(server.id)
|
||||||
findNavController().popBackStack(R.id.mainFragment, false)
|
findNavController().popBackStack(R.id.mainFragment, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val fab = view.findViewById<FloatingActionButton>(R.id.server_add_fab)
|
val fab = view.findViewById<FloatingActionButton>(R.id.server_add_fab)
|
||||||
fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
editServer(-1)
|
editServerByIndex(-1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,44 +109,37 @@ class ServerSelectorFragment : Fragment() {
|
||||||
/**
|
/**
|
||||||
* Sets the active server when a list item is clicked
|
* Sets the active server when a list item is clicked
|
||||||
*/
|
*/
|
||||||
private fun setActiveServer(index: Int) {
|
private fun setActiveServerById(id: Int) {
|
||||||
// TODO this is still a blocking call - we shouldn't leave this activity before the active server is updated.
|
|
||||||
// Maybe this can be refactored by using LiveData, or this can be made more user friendly with a ProgressDialog
|
|
||||||
runBlocking {
|
|
||||||
controller.clearIncomplete()
|
controller.clearIncomplete()
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
if (activeServerProvider.getActiveServer().index != index) {
|
if (activeServerProvider.getActiveServer().id != id) {
|
||||||
activeServerProvider.setActiveServerByIndex(index)
|
ActiveServerProvider.setActiveServerById(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.isJukeboxEnabled =
|
|
||||||
activeServerProvider.getActiveServer().jukeboxByDefault
|
|
||||||
}
|
|
||||||
Timber.i("Active server was set to: $index")
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Callback handles the deletion of a Server Setting
|
* This Callback handles the deletion of a Server Setting
|
||||||
*/
|
*/
|
||||||
private fun onServerDeleted(index: Int) {
|
private fun deleteServerById(id: Int) {
|
||||||
ErrorDialog.Builder(context)
|
ErrorDialog.Builder(context)
|
||||||
.setTitle(R.string.server_menu_delete)
|
.setTitle(R.string.server_menu_delete)
|
||||||
.setMessage(R.string.server_selector_delete_confirmation)
|
.setMessage(R.string.server_selector_delete_confirmation)
|
||||||
.setPositiveButton(R.string.common_delete) { dialog, _ ->
|
.setPositiveButton(R.string.common_delete) { dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
|
||||||
val activeServerIndex = activeServerProvider.getActiveServer().index
|
// Get the id of the current active server
|
||||||
val id = ActiveServerProvider.getActiveServerId()
|
val activeServerId = ActiveServerProvider.getActiveServerId()
|
||||||
|
|
||||||
// If the currently active server is deleted, go offline
|
// If the currently active server is deleted, go offline
|
||||||
if (index == activeServerIndex) setActiveServer(-1)
|
if (id == activeServerId) setActiveServerById(ActiveServerProvider.OFFLINE_DB_ID)
|
||||||
|
|
||||||
serverSettingsModel.deleteItem(index)
|
serverSettingsModel.deleteItemById(id)
|
||||||
|
|
||||||
// Clear the metadata cache
|
// Clear the metadata cache
|
||||||
activeServerProvider.deleteMetaDatabase(id)
|
activeServerProvider.deleteMetaDatabase(activeServerId)
|
||||||
|
|
||||||
Timber.i("Server deleted: $index")
|
Timber.i("Server deleted, id: $id")
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.common_cancel) { dialog, _ ->
|
.setNegativeButton(R.string.common_cancel) { dialog, _ ->
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
|
@ -161,7 +150,7 @@ class ServerSelectorFragment : Fragment() {
|
||||||
/**
|
/**
|
||||||
* Starts the Edit Server Fragment to edit the details of a server
|
* Starts the Edit Server Fragment to edit the details of a server
|
||||||
*/
|
*/
|
||||||
private fun editServer(index: Int) {
|
private fun editServerByIndex(index: Int) {
|
||||||
val bundle = Bundle()
|
val bundle = Bundle()
|
||||||
bundle.putInt(EDIT_SERVER_INTENT_INDEX, index)
|
bundle.putInt(EDIT_SERVER_INTENT_INDEX, index)
|
||||||
findNavController().navigate(R.id.serverSelectorToEditServer, bundle)
|
findNavController().navigate(R.id.serverSelectorToEditServer, bundle)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import kotlinx.coroutines.runBlocking
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
|
import org.moire.ultrasonic.data.ActiveServerProvider.Companion.OFFLINE_DB_ID
|
||||||
import org.moire.ultrasonic.data.ServerSetting
|
import org.moire.ultrasonic.data.ServerSetting
|
||||||
import org.moire.ultrasonic.data.ServerSettingDao
|
import org.moire.ultrasonic.data.ServerSettingDao
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -30,6 +31,8 @@ class ServerSettingsModel(
|
||||||
/**
|
/**
|
||||||
* Retrieves the list of the configured servers from the database.
|
* Retrieves the list of the configured servers from the database.
|
||||||
* This function is asynchronous, uses LiveData to provide the Setting.
|
* This function is asynchronous, uses LiveData to provide the Setting.
|
||||||
|
*
|
||||||
|
* It does not include the Offline "server".
|
||||||
*/
|
*/
|
||||||
fun getServerList(): LiveData<List<ServerSetting>> {
|
fun getServerList(): LiveData<List<ServerSetting>> {
|
||||||
// This check should run before returning any result
|
// This check should run before returning any result
|
||||||
|
@ -92,14 +95,14 @@ class ServerSettingsModel(
|
||||||
/**
|
/**
|
||||||
* Removes a Setting from the database
|
* Removes a Setting from the database
|
||||||
*/
|
*/
|
||||||
fun deleteItem(index: Int) {
|
fun deleteItemById(id: Int) {
|
||||||
if (index == 0) return
|
if (id == OFFLINE_DB_ID) return
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val itemToBeDeleted = repository.findByIndex(index)
|
val itemToBeDeleted = repository.findById(id)
|
||||||
if (itemToBeDeleted != null) {
|
if (itemToBeDeleted != null) {
|
||||||
repository.delete(itemToBeDeleted)
|
repository.delete(itemToBeDeleted)
|
||||||
Timber.d("deleteItem deleted index: $index")
|
Timber.d("deleteItem deleted id: $id")
|
||||||
reindexSettings()
|
reindexSettings()
|
||||||
activeServerProvider.invalidateCache()
|
activeServerProvider.invalidateCache()
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,12 @@
|
||||||
|
|
||||||
package org.moire.ultrasonic.playback
|
package org.moire.ultrasonic.playback
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.media3.common.C
|
import androidx.media3.common.C
|
||||||
import androidx.media3.common.PlaybackException
|
import androidx.media3.common.PlaybackException
|
||||||
import androidx.media3.common.util.Assertions
|
import androidx.media3.common.util.Assertions
|
||||||
import androidx.media3.common.util.UnstableApi
|
|
||||||
import androidx.media3.common.util.Util
|
import androidx.media3.common.util.Util
|
||||||
import androidx.media3.datasource.BaseDataSource
|
import androidx.media3.datasource.BaseDataSource
|
||||||
import androidx.media3.datasource.DataSourceException
|
import androidx.media3.datasource.DataSourceException
|
||||||
|
@ -43,15 +43,15 @@ import timber.log.Timber
|
||||||
* priority) the `dataSpec`, [.setRequestProperty] and the default parameters used to
|
* priority) the `dataSpec`, [.setRequestProperty] and the default parameters used to
|
||||||
* construct the instance.
|
* construct the instance.
|
||||||
*/
|
*/
|
||||||
|
@SuppressLint("UnsafeOptInUsageError")
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
@UnstableApi
|
|
||||||
open class APIDataSource private constructor(
|
open class APIDataSource private constructor(
|
||||||
subsonicAPIClient: SubsonicAPIClient
|
subsonicAPIClient: SubsonicAPIClient
|
||||||
) : BaseDataSource(true),
|
) : BaseDataSource(true),
|
||||||
HttpDataSource {
|
HttpDataSource {
|
||||||
|
|
||||||
/** [DataSource.Factory] for [APIDataSource] instances. */
|
/** [DataSource.Factory] for [APIDataSource] instances. */
|
||||||
class Factory(private val subsonicAPIClient: SubsonicAPIClient) : HttpDataSource.Factory {
|
class Factory(private var subsonicAPIClient: SubsonicAPIClient) : HttpDataSource.Factory {
|
||||||
private val defaultRequestProperties: RequestProperties = RequestProperties()
|
private val defaultRequestProperties: RequestProperties = RequestProperties()
|
||||||
private var transferListener: TransferListener? = null
|
private var transferListener: TransferListener? = null
|
||||||
|
|
||||||
|
@ -75,6 +75,10 @@ open class APIDataSource private constructor(
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setAPIClient(newClient: SubsonicAPIClient) {
|
||||||
|
this.subsonicAPIClient = newClient
|
||||||
|
}
|
||||||
|
|
||||||
override fun createDataSource(): APIDataSource {
|
override fun createDataSource(): APIDataSource {
|
||||||
val dataSource = APIDataSource(
|
val dataSource = APIDataSource(
|
||||||
subsonicAPIClient
|
subsonicAPIClient
|
||||||
|
@ -318,6 +322,7 @@ open class APIDataSource private constructor(
|
||||||
return C.RESULT_END_OF_INPUT
|
return C.RESULT_END_OF_INPUT
|
||||||
}
|
}
|
||||||
bytesRead += read.toLong()
|
bytesRead += read.toLong()
|
||||||
|
// TODO
|
||||||
// bytesTransferred(read)
|
// bytesTransferred(read)
|
||||||
return read
|
return read
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,21 +29,27 @@ import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
import androidx.media3.session.MediaLibraryService
|
import androidx.media3.session.MediaLibraryService
|
||||||
import androidx.media3.session.MediaSession
|
import androidx.media3.session.MediaSession
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.activity.NavigationActivity
|
import org.moire.ultrasonic.activity.NavigationActivity
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
|
import org.moire.ultrasonic.service.RxBus
|
||||||
|
import org.moire.ultrasonic.service.plusAssign
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.Settings
|
import org.moire.ultrasonic.util.Settings
|
||||||
|
|
||||||
class PlaybackService : MediaLibraryService(), KoinComponent {
|
class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||||
private lateinit var player: ExoPlayer
|
private lateinit var player: ExoPlayer
|
||||||
private lateinit var mediaLibrarySession: MediaLibrarySession
|
private lateinit var mediaLibrarySession: MediaLibrarySession
|
||||||
|
private lateinit var apiDataSource: APIDataSource.Factory
|
||||||
private lateinit var dataSourceFactory: DataSource.Factory
|
private lateinit var dataSourceFactory: DataSource.Factory
|
||||||
|
|
||||||
private lateinit var librarySessionCallback: MediaLibrarySession.MediaLibrarySessionCallback
|
private lateinit var librarySessionCallback: MediaLibrarySession.MediaLibrarySessionCallback
|
||||||
|
|
||||||
|
private var rxBusSubscription = CompositeDisposable()
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer,
|
* For some reason the LocalConfiguration of MediaItem are stripped somewhere in ExoPlayer,
|
||||||
* and thereby customarily it is required to rebuild it..
|
* and thereby customarily it is required to rebuild it..
|
||||||
|
@ -64,11 +70,18 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
initializeSessionAndPlayer()
|
initializeSessionAndPlayer()
|
||||||
|
|
||||||
|
rxBusSubscription += RxBus.activeServerChangeObservable.subscribe {
|
||||||
|
// Update the API endpoint when the active server has changed
|
||||||
|
val newClient: SubsonicAPIClient by inject()
|
||||||
|
apiDataSource.setAPIClient(newClient)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
player.release()
|
player.release()
|
||||||
mediaLibrarySession.release()
|
mediaLibrarySession.release()
|
||||||
|
rxBusSubscription.dispose()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,8 +101,10 @@ class PlaybackService : MediaLibraryService(), KoinComponent {
|
||||||
val subsonicAPIClient: SubsonicAPIClient by inject()
|
val subsonicAPIClient: SubsonicAPIClient by inject()
|
||||||
|
|
||||||
// Create a MediaSource which passes calls through our OkHttp Stack
|
// Create a MediaSource which passes calls through our OkHttp Stack
|
||||||
|
apiDataSource = APIDataSource.Factory(subsonicAPIClient)
|
||||||
|
|
||||||
dataSourceFactory = APIDataSource.Factory(subsonicAPIClient)
|
dataSourceFactory = APIDataSource.Factory(subsonicAPIClient)
|
||||||
val cacheDataSourceFactory: DataSource.Factory = CachedDataSource.Factory(dataSourceFactory)
|
val cacheDataSourceFactory: DataSource.Factory = CachedDataSource.Factory(apiDataSource)
|
||||||
|
|
||||||
// Create a renderer with HW rendering support
|
// Create a renderer with HW rendering support
|
||||||
val renderer = DefaultRenderersFactory(this)
|
val renderer = DefaultRenderersFactory(this)
|
||||||
|
|
|
@ -18,6 +18,7 @@ import androidx.media3.common.Timeline
|
||||||
import androidx.media3.session.MediaController
|
import androidx.media3.session.MediaController
|
||||||
import androidx.media3.session.SessionToken
|
import androidx.media3.session.SessionToken
|
||||||
import com.google.common.util.concurrent.MoreExecutors
|
import com.google.common.util.concurrent.MoreExecutors
|
||||||
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
|
@ -61,6 +62,8 @@ class MediaPlayerController(
|
||||||
private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject()
|
private val jukeboxMediaPlayer: JukeboxMediaPlayer by inject()
|
||||||
private val activeServerProvider: ActiveServerProvider by inject()
|
private val activeServerProvider: ActiveServerProvider by inject()
|
||||||
|
|
||||||
|
private val rxBusSubscription: CompositeDisposable = CompositeDisposable()
|
||||||
|
|
||||||
private var sessionToken =
|
private var sessionToken =
|
||||||
SessionToken(context, ComponentName(context, PlaybackService::class.java))
|
SessionToken(context, ComponentName(context, PlaybackService::class.java))
|
||||||
|
|
||||||
|
@ -109,6 +112,11 @@ class MediaPlayerController(
|
||||||
// controller?.play()
|
// controller?.play()
|
||||||
}, MoreExecutors.directExecutor())
|
}, MoreExecutors.directExecutor())
|
||||||
|
|
||||||
|
rxBusSubscription += RxBus.activeServerChangeObservable.subscribe {
|
||||||
|
// Update the Jukebox state when the active server has changed
|
||||||
|
isJukeboxEnabled = activeServerProvider.getActiveServer().jukeboxByDefault
|
||||||
|
}
|
||||||
|
|
||||||
created = true
|
created = true
|
||||||
Timber.i("MediaPlayerController created")
|
Timber.i("MediaPlayerController created")
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,8 +27,14 @@ import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
import org.moire.ultrasonic.di.OFFLINE_MUSIC_SERVICE
|
import org.moire.ultrasonic.di.OFFLINE_MUSIC_SERVICE
|
||||||
import org.moire.ultrasonic.di.ONLINE_MUSIC_SERVICE
|
import org.moire.ultrasonic.di.ONLINE_MUSIC_SERVICE
|
||||||
import org.moire.ultrasonic.di.musicServiceModule
|
import org.moire.ultrasonic.di.musicServiceModule
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
// TODO Refactor everywhere to use DI way to get MusicService, and then remove this class
|
/*
|
||||||
|
* TODO: When resetMusicService is called, a large number of classes are completely newly instantiated,
|
||||||
|
* which take quite a bit of time.
|
||||||
|
*
|
||||||
|
* Instead it would probably be faster to listen to Rx
|
||||||
|
*/
|
||||||
object MusicServiceFactory : KoinComponent {
|
object MusicServiceFactory : KoinComponent {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getMusicService(): MusicService {
|
fun getMusicService(): MusicService {
|
||||||
|
@ -45,6 +51,7 @@ object MusicServiceFactory : KoinComponent {
|
||||||
*/
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun resetMusicService() {
|
fun resetMusicService() {
|
||||||
|
Timber.i("Regenerating Koin Music Service Module")
|
||||||
unloadKoinModules(musicServiceModule)
|
unloadKoinModules(musicServiceModule)
|
||||||
loadKoinModules(musicServiceModule)
|
loadKoinModules(musicServiceModule)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +1,19 @@
|
||||||
package org.moire.ultrasonic.service
|
package org.moire.ultrasonic.service
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.support.v4.media.session.MediaSessionCompat
|
|
||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
import io.reactivex.rxjava3.core.Observable
|
import io.reactivex.rxjava3.core.Observable
|
||||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||||
import io.reactivex.rxjava3.disposables.Disposable
|
import io.reactivex.rxjava3.disposables.Disposable
|
||||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import org.moire.ultrasonic.domain.PlayerState
|
import org.moire.ultrasonic.domain.PlayerState
|
||||||
|
|
||||||
class RxBus {
|
class RxBus {
|
||||||
companion object {
|
companion object {
|
||||||
var mediaSessionTokenPublisher: PublishSubject<MediaSessionCompat.Token> =
|
|
||||||
|
var activeServerChangePublisher: PublishSubject<Int> =
|
||||||
PublishSubject.create()
|
PublishSubject.create()
|
||||||
val mediaSessionTokenObservable: Observable<MediaSessionCompat.Token> =
|
var activeServerChangeObservable: Observable<Int> =
|
||||||
mediaSessionTokenPublisher.observeOn(AndroidSchedulers.mainThread())
|
activeServerChangePublisher.observeOn(AndroidSchedulers.mainThread())
|
||||||
.replay(1)
|
|
||||||
.autoConnect(0)
|
|
||||||
|
|
||||||
val themeChangedEventPublisher: PublishSubject<Unit> =
|
val themeChangedEventPublisher: PublishSubject<Unit> =
|
||||||
PublishSubject.create()
|
PublishSubject.create()
|
||||||
|
@ -43,38 +39,11 @@ class RxBus {
|
||||||
.replay(1)
|
.replay(1)
|
||||||
.autoConnect(0)
|
.autoConnect(0)
|
||||||
|
|
||||||
val playbackPositionPublisher: PublishSubject<Int> =
|
|
||||||
PublishSubject.create()
|
|
||||||
val playbackPositionObservable: Observable<Int> =
|
|
||||||
playbackPositionPublisher.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.throttleFirst(1, TimeUnit.SECONDS)
|
|
||||||
.replay(1)
|
|
||||||
.autoConnect(0)
|
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
val dismissNowPlayingCommandPublisher: PublishSubject<Unit> =
|
val dismissNowPlayingCommandPublisher: PublishSubject<Unit> =
|
||||||
PublishSubject.create()
|
PublishSubject.create()
|
||||||
val dismissNowPlayingCommandObservable: Observable<Unit> =
|
val dismissNowPlayingCommandObservable: Observable<Unit> =
|
||||||
dismissNowPlayingCommandPublisher.observeOn(AndroidSchedulers.mainThread())
|
dismissNowPlayingCommandPublisher.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
|
||||||
val playFromMediaIdCommandPublisher: PublishSubject<Pair<String?, Bundle?>> =
|
|
||||||
PublishSubject.create()
|
|
||||||
val playFromMediaIdCommandObservable: Observable<Pair<String?, Bundle?>> =
|
|
||||||
playFromMediaIdCommandPublisher.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
|
|
||||||
val playFromSearchCommandPublisher: PublishSubject<Pair<String?, Bundle?>> =
|
|
||||||
PublishSubject.create()
|
|
||||||
val playFromSearchCommandObservable: Observable<Pair<String?, Bundle?>> =
|
|
||||||
playFromSearchCommandPublisher.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
|
|
||||||
val skipToQueueItemCommandPublisher: PublishSubject<Long> =
|
|
||||||
PublishSubject.create()
|
|
||||||
val skipToQueueItemCommandObservable: Observable<Long> =
|
|
||||||
skipToQueueItemCommandPublisher.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
|
|
||||||
fun releaseMediaSessionToken() {
|
|
||||||
mediaSessionTokenPublisher = PublishSubject.create()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class StateWithTrack(val state: PlayerState, val track: DownloadFile?, val index: Int = -1)
|
data class StateWithTrack(val state: PlayerState, val track: DownloadFile?, val index: Int = -1)
|
||||||
|
|
Loading…
Reference in New Issue