Started refactoring events to ReactiveX

This commit is contained in:
Nite 2021-10-31 15:22:15 +01:00
parent f0c02f5551
commit 5eaf9cccb1
No known key found for this signature in database
GPG Key ID: 1D1AD59B1C6386C1
17 changed files with 97 additions and 118 deletions

View File

@ -43,6 +43,8 @@ ext.versions = [
timber : "4.7.1",
fastScroll : "2.0.1",
colorPicker : "2.2.3",
rxJava : "3.1.2",
rxAndroid : "3.0.0",
]
ext.gradlePlugins = [
@ -91,6 +93,8 @@ ext.other = [
fastScroll : "com.simplecityapps:recyclerview-fastscroll:$versions.fastScroll",
sortListView : "com.github.tzugen:drag-sort-listview:$versions.sortListView",
colorPickerView : "com.github.skydoves:colorpickerview:$versions.colorPicker",
rxJava : "io.reactivex.rxjava3:rxjava:$versions.rxJava",
rxAndroid : "io.reactivex.rxjava3:rxandroid:$versions.rxAndroid",
]
ext.testing = [

View File

@ -106,6 +106,8 @@ dependencies {
implementation other.fastScroll
implementation other.sortListView
implementation other.colorPickerView
implementation other.rxJava
implementation other.rxAndroid
kapt androidSupport.room

View File

@ -18,6 +18,7 @@ import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.domain.PlayerState;
import org.moire.ultrasonic.service.DownloadFile;
import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.service.RxBus;
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.NowPlayingEventDistributor;
@ -26,6 +27,7 @@ import org.moire.ultrasonic.util.Settings;
import org.moire.ultrasonic.util.Util;
import kotlin.Lazy;
import kotlin.Unit;
import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject;
@ -70,8 +72,6 @@ public class NowPlayingFragment extends Fragment {
nowPlayingArtist = view.findViewById(R.id.now_playing_artist);
nowPlayingEventListener = new NowPlayingEventListener() {
@Override
public void onDismissNowPlaying() { }
@Override
public void onHideNowPlaying() { }
@Override
@ -177,7 +177,7 @@ public class NowPlayingFragment extends Fragment {
{
if (deltaY < 0)
{
nowPlayingEventDistributor.getValue().raiseNowPlayingDismissedEvent();
RxBus.INSTANCE.getDismissNowPlayingCommandPublisher().onNext(Unit.INSTANCE);
return false;
}
if (deltaY > 0)

View File

@ -31,12 +31,12 @@ import org.moire.ultrasonic.log.FileLoggerTree;
import org.moire.ultrasonic.provider.SearchSuggestionProvider;
import org.moire.ultrasonic.service.Consumer;
import org.moire.ultrasonic.service.MediaPlayerController;
import org.moire.ultrasonic.service.RxBus;
import org.moire.ultrasonic.util.Constants;
import org.moire.ultrasonic.util.FileUtil;
import org.moire.ultrasonic.util.MediaSessionHandler;
import org.moire.ultrasonic.util.PermissionUtil;
import org.moire.ultrasonic.util.Settings;
import org.moire.ultrasonic.util.ThemeChangedEventDistributor;
import org.moire.ultrasonic.util.TimeSpanPreference;
import org.moire.ultrasonic.util.TimeSpanPreferenceDialogFragmentCompat;
import org.moire.ultrasonic.util.Util;
@ -44,6 +44,7 @@ import org.moire.ultrasonic.util.Util;
import java.io.File;
import kotlin.Lazy;
import kotlin.Unit;
import timber.log.Timber;
import static org.koin.java.KoinJavaComponent.inject;
@ -89,7 +90,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
private final Lazy<MediaPlayerController> mediaPlayerControllerLazy = inject(MediaPlayerController.class);
private final Lazy<PermissionUtil> permissionUtil = inject(PermissionUtil.class);
private final Lazy<ThemeChangedEventDistributor> themeChangedEventDistributor = inject(ThemeChangedEventDistributor.class);
private final Lazy<MediaSessionHandler> mediaSessionHandler = inject(MediaSessionHandler.class);
@Override
@ -192,7 +192,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
} else if (Constants.PREFERENCES_KEY_ID3_TAGS.equals(key)) {
showArtistPicture.setEnabled(sharedPreferences.getBoolean(key, false));
} else if (Constants.PREFERENCES_KEY_THEME.equals(key)) {
themeChangedEventDistributor.getValue().RaiseThemeChangedEvent();
RxBus.INSTANCE.getThemeChangedEventPublisher().onNext(Unit.INSTANCE);
}
}

View File

@ -31,6 +31,7 @@ import androidx.navigation.ui.setupWithNavController
import androidx.preference.PreferenceManager
import com.google.android.material.button.MaterialButton
import com.google.android.material.navigation.NavigationView
import io.reactivex.rxjava3.disposables.Disposable
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.moire.ultrasonic.R
@ -43,6 +44,7 @@ import org.moire.ultrasonic.provider.SearchSuggestionProvider
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.MediaPlayerController
import org.moire.ultrasonic.service.MediaPlayerLifecycleSupport
import org.moire.ultrasonic.service.RxBus
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
import org.moire.ultrasonic.util.Constants
import org.moire.ultrasonic.util.FileUtil
@ -52,8 +54,6 @@ import org.moire.ultrasonic.util.PermissionUtil
import org.moire.ultrasonic.util.ServerColor
import org.moire.ultrasonic.util.Settings
import org.moire.ultrasonic.util.SubsonicUncaughtExceptionHandler
import org.moire.ultrasonic.util.ThemeChangedEventDistributor
import org.moire.ultrasonic.util.ThemeChangedEventListener
import org.moire.ultrasonic.util.Util
import timber.log.Timber
@ -75,14 +75,13 @@ class NavigationActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var nowPlayingEventListener: NowPlayingEventListener
private lateinit var themeChangedEventListener: ThemeChangedEventListener
private var themeChangedEventSubscription: Disposable? = null
private val serverSettingsModel: ServerSettingsModel by viewModel()
private val lifecycleSupport: MediaPlayerLifecycleSupport by inject()
private val mediaPlayerController: MediaPlayerController by inject()
private val imageLoaderProvider: ImageLoaderProvider by inject()
private val nowPlayingEventDistributor: NowPlayingEventDistributor by inject()
private val themeChangedEventDistributor: ThemeChangedEventDistributor by inject()
private val permissionUtil: PermissionUtil by inject()
private val activeServerProvider: ActiveServerProvider by inject()
private val serverRepository: ServerSettingDao by inject()
@ -169,11 +168,12 @@ class NavigationActivity : AppCompatActivity() {
showWelcomeDialog()
}
RxBus.dismissNowPlayingCommandObservable.subscribe {
nowPlayingHidden = true
hideNowPlaying()
}
nowPlayingEventListener = object : NowPlayingEventListener {
override fun onDismissNowPlaying() {
nowPlayingHidden = true
hideNowPlaying()
}
override fun onHideNowPlaying() {
hideNowPlaying()
@ -184,12 +184,11 @@ class NavigationActivity : AppCompatActivity() {
}
}
themeChangedEventListener = object : ThemeChangedEventListener {
override fun onThemeChanged() { recreate() }
themeChangedEventSubscription = RxBus.themeChangedEventObservable.subscribe {
recreate()
}
nowPlayingEventDistributor.subscribe(nowPlayingEventListener)
themeChangedEventDistributor.subscribe(themeChangedEventListener)
serverRepository.liveServerCount().observe(
this,
@ -238,7 +237,7 @@ class NavigationActivity : AppCompatActivity() {
override fun onDestroy() {
super.onDestroy()
nowPlayingEventDistributor.unsubscribe(nowPlayingEventListener)
themeChangedEventDistributor.unsubscribe(themeChangedEventListener)
themeChangedEventSubscription?.dispose()
imageLoaderProvider.clearImageLoader()
permissionUtil.onForegroundApplicationStopped()
}

View File

@ -8,7 +8,6 @@ import org.moire.ultrasonic.util.MediaSessionEventDistributor
import org.moire.ultrasonic.util.MediaSessionHandler
import org.moire.ultrasonic.util.NowPlayingEventDistributor
import org.moire.ultrasonic.util.PermissionUtil
import org.moire.ultrasonic.util.ThemeChangedEventDistributor
/**
* This Koin module contains the registration of general classes needed for Ultrasonic
@ -18,7 +17,6 @@ val applicationModule = module {
single { ImageLoaderProvider(androidContext()) }
single { PermissionUtil(androidContext()) }
single { NowPlayingEventDistributor() }
single { ThemeChangedEventDistributor() }
single { MediaSessionEventDistributor() }
single { MediaSessionHandler() }
}

View File

@ -14,6 +14,7 @@ import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.session.MediaSessionCompat
import androidx.media.MediaBrowserServiceCompat
import androidx.media.utils.MediaConstants
import io.reactivex.rxjava3.disposables.Disposable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -93,16 +94,17 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
private val useId3Tags get() = Settings.shouldUseId3Tags
private val musicFolderId get() = activeServerProvider.getActiveServer().musicFolderId
private var mediaSessionTokenSubscription: Disposable? = null
@Suppress("MagicNumber")
override fun onCreate() {
super.onCreate()
mediaSessionTokenSubscription = RxBus.mediaSessionTokenObservable.subscribe {
if (sessionToken == null) sessionToken = it
}
mediaSessionEventListener = object : MediaSessionEventListener {
override fun onMediaSessionTokenCreated(token: MediaSessionCompat.Token) {
if (sessionToken == null) {
sessionToken = token
}
}
override fun onPlayFromMediaIdRequested(mediaId: String?, extras: Bundle?) {
Timber.d(
@ -182,6 +184,7 @@ class AutoMediaBrowserService : MediaBrowserServiceCompat() {
override fun onDestroy() {
super.onDestroy()
mediaSessionTokenSubscription?.dispose()
mediaSessionEventDistributor.unsubscribe(mediaSessionEventListener)
mediaSessionHandler.release()
serviceJob.cancel()

View File

@ -14,6 +14,7 @@ import android.content.IntentFilter
import android.media.AudioManager
import android.os.Build
import android.view.KeyEvent
import io.reactivex.rxjava3.disposables.Disposable
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.moire.ultrasonic.R
@ -39,7 +40,7 @@ class MediaPlayerLifecycleSupport : KoinComponent {
private var created = false
private var headsetEventReceiver: BroadcastReceiver? = null
private lateinit var mediaSessionEventListener: MediaSessionEventListener
private var mediaButtonEventSubscription: Disposable? = null
fun onCreate() {
onCreate(false, null)
@ -52,13 +53,10 @@ class MediaPlayerLifecycleSupport : KoinComponent {
return
}
mediaSessionEventListener = object : MediaSessionEventListener {
override fun onMediaButtonEvent(keyEvent: KeyEvent?) {
if (keyEvent != null) handleKeyEvent(keyEvent)
}
mediaButtonEventSubscription = RxBus.mediaButtonEventObservable.subscribe {
handleKeyEvent(it)
}
mediaSessionEventDistributor.subscribe(mediaSessionEventListener)
registerHeadsetReceiver()
mediaPlayerController.onCreate()
if (autoPlay) mediaPlayerController.preload()
@ -98,9 +96,8 @@ class MediaPlayerLifecycleSupport : KoinComponent {
mediaPlayerController.playerPosition
)
mediaSessionEventDistributor.unsubscribe(mediaSessionEventListener)
mediaPlayerController.clear(false)
mediaButtonEventSubscription?.dispose()
applicationContext().unregisterReceiver(headsetEventReceiver)
mediaPlayerController.onDestroy()
@ -165,12 +162,7 @@ class MediaPlayerLifecycleSupport : KoinComponent {
}
}
val headsetIntentFilter: IntentFilter =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
IntentFilter(AudioManager.ACTION_HEADSET_PLUG)
} else {
IntentFilter(Intent.ACTION_HEADSET_PLUG)
}
val headsetIntentFilter = IntentFilter(AudioManager.ACTION_HEADSET_PLUG)
applicationContext().registerReceiver(headsetEventReceiver, headsetIntentFilter)
}

View File

@ -22,6 +22,7 @@ import android.support.v4.media.session.MediaSessionCompat
import android.view.KeyEvent
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import io.reactivex.rxjava3.disposables.Disposable
import kotlin.collections.ArrayList
import org.koin.android.ext.android.inject
import org.moire.ultrasonic.R
@ -73,6 +74,7 @@ class MediaPlayerService : Service() {
private var isInForeground = false
private var notificationBuilder: NotificationCompat.Builder? = null
private lateinit var mediaSessionEventListener: MediaSessionEventListener
private var mediaSessionTokenSubscription: Disposable? = null
private val repeatMode: RepeatMode
get() = Settings.repeatMode
@ -102,11 +104,11 @@ class MediaPlayerService : Service() {
localMediaPlayer.onNextSongRequested = Runnable { setNextPlaying() }
mediaSessionEventListener = object : MediaSessionEventListener {
override fun onMediaSessionTokenCreated(token: MediaSessionCompat.Token) {
mediaSessionToken = token
}
mediaSessionTokenSubscription = RxBus.mediaSessionTokenObservable.subscribe {
mediaSessionToken = it
}
mediaSessionEventListener = object : MediaSessionEventListener {
override fun onSkipToQueueItemRequested(id: Long) {
play(id.toInt())
}
@ -134,6 +136,7 @@ class MediaPlayerService : Service() {
super.onDestroy()
instance = null
try {
mediaSessionTokenSubscription?.dispose()
mediaSessionEventDistributor.unsubscribe(mediaSessionEventListener)
mediaSessionHandler.release()
@ -357,19 +360,11 @@ class MediaPlayerService : Service() {
private fun setupOnCurrentPlayingChangedHandler() {
localMediaPlayer.onCurrentPlayingChanged = { currentPlaying: DownloadFile? ->
if (currentPlaying != null) {
Util.broadcastNewTrackInfo(this@MediaPlayerService, currentPlaying.song)
Util.broadcastA2dpMetaDataChange(
this@MediaPlayerService, playerPosition, currentPlaying,
downloader.all.size, downloader.currentPlayingIndex + 1
)
} else {
Util.broadcastNewTrackInfo(this@MediaPlayerService, null)
Util.broadcastA2dpMetaDataChange(
this@MediaPlayerService, playerPosition, null,
downloader.all.size, downloader.currentPlayingIndex + 1
)
}
Util.broadcastNewTrackInfo(this@MediaPlayerService, currentPlaying?.song)
Util.broadcastA2dpMetaDataChange(
this@MediaPlayerService, playerPosition, currentPlaying,
downloader.all.size, downloader.currentPlayingIndex + 1
)
// Update widget
val playerState = localMediaPlayer.playerState

View File

@ -0,0 +1,42 @@
package org.moire.ultrasonic.service
import android.support.v4.media.session.MediaSessionCompat
import android.view.KeyEvent
import io.reactivex.rxjava3.subjects.PublishSubject
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.observables.ConnectableObservable
import timber.log.Timber
object RxBus {
var mediaSessionTokenPublisher: PublishSubject<MediaSessionCompat.Token> =
PublishSubject.create()
val mediaSessionTokenObservable: Observable<MediaSessionCompat.Token> =
mediaSessionTokenPublisher.observeOn(AndroidSchedulers.mainThread())
.replay(1)
.autoConnect()
.doOnEach { Timber.d("RxBus mediaSessionTokenPublisher onEach $it")}
val mediaButtonEventPublisher: PublishSubject<KeyEvent> =
PublishSubject.create()
val mediaButtonEventObservable: Observable<KeyEvent> =
mediaButtonEventPublisher.observeOn(AndroidSchedulers.mainThread())
.doOnEach { Timber.d("RxBus mediaButtonEventPublisher onEach $it")}
val themeChangedEventPublisher: PublishSubject<Unit> =
PublishSubject.create()
val themeChangedEventObservable: Observable<Unit> =
themeChangedEventPublisher.observeOn(AndroidSchedulers.mainThread())
.doOnEach { Timber.d("RxBus themeChangedEventPublisher onEach $it")}
val dismissNowPlayingCommandPublisher: PublishSubject<Unit> =
PublishSubject.create()
val dismissNowPlayingCommandObservable: Observable<Unit> =
dismissNowPlayingCommandPublisher.observeOn(AndroidSchedulers.mainThread())
.doOnEach { Timber.d("RxBus dismissNowPlayingCommandPublisher onEach $it")}
fun releaseMediaSessionToken() { mediaSessionTokenPublisher = PublishSubject.create() }
}

View File

@ -23,30 +23,12 @@ class MediaSessionEventDistributor {
fun subscribe(listener: MediaSessionEventListener) {
eventListenerList.add(listener)
synchronized(this) {
if (cachedToken != null)
listener.onMediaSessionTokenCreated(cachedToken!!)
}
}
fun unsubscribe(listener: MediaSessionEventListener) {
eventListenerList.remove(listener)
}
fun releaseCachedMediaSessionToken() {
synchronized(this) {
cachedToken = null
}
}
fun raiseMediaSessionTokenCreatedEvent(token: MediaSessionCompat.Token) {
synchronized(this) {
cachedToken = token
eventListenerList.forEach { listener -> listener.onMediaSessionTokenCreated(token) }
}
}
fun raisePlayFromMediaIdRequestedEvent(mediaId: String?, extras: Bundle?) {
eventListenerList.forEach {
listener ->
@ -61,8 +43,4 @@ class MediaSessionEventDistributor {
fun raiseSkipToQueueItemRequestedEvent(id: Long) {
eventListenerList.forEach { listener -> listener.onSkipToQueueItemRequested(id) }
}
fun raiseMediaButtonEvent(keyEvent: KeyEvent?) {
eventListenerList.forEach { listener -> listener.onMediaButtonEvent(keyEvent) }
}
}

View File

@ -15,9 +15,9 @@ import android.view.KeyEvent
* Callback interface for MediaSession related event subscribers
*/
interface MediaSessionEventListener {
fun onMediaSessionTokenCreated(token: MediaSessionCompat.Token) {}
// fun onMediaSessionTokenCreated(token: MediaSessionCompat.Token) {}
fun onPlayFromMediaIdRequested(mediaId: String?, extras: Bundle?) {}
fun onPlayFromSearchRequested(query: String?, extras: Bundle?) {}
fun onSkipToQueueItemRequested(id: Long) {}
fun onMediaButtonEvent(keyEvent: KeyEvent?) {}
// fun onMediaButtonEvent(keyEvent: KeyEvent?) {}
}

View File

@ -25,6 +25,7 @@ import org.moire.ultrasonic.domain.PlayerState
import org.moire.ultrasonic.imageloader.BitmapUtils
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver
import org.moire.ultrasonic.service.DownloadFile
import org.moire.ultrasonic.service.RxBus
import timber.log.Timber
private const val INTENT_CODE_MEDIA_BUTTON = 161
@ -53,7 +54,7 @@ class MediaSessionHandler : KoinComponent {
if (referenceCount > 0) return
mediaSession?.isActive = false
mediaSessionEventDistributor.releaseCachedMediaSessionToken()
RxBus.releaseMediaSessionToken()
mediaSession?.release()
mediaSession = null
@ -72,7 +73,7 @@ class MediaSessionHandler : KoinComponent {
mediaSession = MediaSessionCompat(applicationContext, "UltrasonicService")
val mediaSessionToken = mediaSession?.sessionToken ?: return
mediaSessionEventDistributor.raiseMediaSessionTokenCreatedEvent(mediaSessionToken)
RxBus.mediaSessionTokenPublisher.onNext(mediaSessionToken)
updateMediaButtonReceiver()
@ -147,7 +148,7 @@ class MediaSessionHandler : KoinComponent {
// This probably won't be necessary once we implement more
// of the modern media APIs, like the MediaController etc.
val event = mediaButtonEvent.extras!!["android.intent.extra.KEY_EVENT"] as KeyEvent?
mediaSessionEventDistributor.raiseMediaButtonEvent(event)
event?.let { RxBus.mediaButtonEventPublisher.onNext(it) }
return true
}

View File

@ -23,8 +23,4 @@ class NowPlayingEventDistributor {
fun raiseHideNowPlayingEvent() {
eventListenerList.forEach { listener -> listener.onHideNowPlaying() }
}
fun raiseNowPlayingDismissedEvent() {
eventListenerList.forEach { listener -> listener.onDismissNowPlaying() }
}
}

View File

@ -4,7 +4,6 @@ package org.moire.ultrasonic.util
* Callback interface for Now Playing event subscribers
*/
interface NowPlayingEventListener {
fun onDismissNowPlaying()
fun onHideNowPlaying()
fun onShowNowPlaying()
}

View File

@ -1,22 +0,0 @@
package org.moire.ultrasonic.util
/**
* This class distributes Theme change related events to its subscribers.
* It is a primitive implementation of a pub-sub event bus
*/
class ThemeChangedEventDistributor {
var eventListenerList: MutableList<ThemeChangedEventListener> =
listOf<ThemeChangedEventListener>().toMutableList()
fun subscribe(listener: ThemeChangedEventListener) {
eventListenerList.add(listener)
}
fun unsubscribe(listener: ThemeChangedEventListener) {
eventListenerList.remove(listener)
}
fun RaiseThemeChangedEvent() {
eventListenerList.forEach { listener -> listener.onThemeChanged() }
}
}

View File

@ -1,8 +0,0 @@
package org.moire.ultrasonic.util
/**
* Callback interface for Theme change event subscribers
*/
interface ThemeChangedEventListener {
fun onThemeChanged()
}