From 218b4f1af419e5d2a267c676a0084cc432f0eaf3 Mon Sep 17 00:00:00 2001 From: Jarne Demeulemeester Date: Mon, 6 Nov 2023 23:42:00 +0100 Subject: [PATCH] refactor: replace SharedFlows with Channels for sending events --- .../dev/jdtech/jellyfin/PlayerActivity.kt | 7 +++++-- .../jellyfin/fragments/AddServerFragment.kt | 7 ++++--- .../jellyfin/fragments/DownloadsFragment.kt | 19 +++++++++++------- .../fragments/EpisodeBottomSheetFragment.kt | 14 ++++++------- .../jellyfin/fragments/LoginFragment.kt | 7 ++++--- .../jellyfin/fragments/MovieFragment.kt | 14 ++++++------- .../jellyfin/fragments/SeasonFragment.kt | 7 +++++-- .../fragments/ServerAddressesFragment.kt | 7 ++++--- .../fragments/ServerSelectFragment.kt | 13 ++++++------ .../jdtech/jellyfin/fragments/ShowFragment.kt | 7 +++++-- .../jellyfin/fragments/UsersFragment.kt | 7 ++++--- .../jellyfin/viewmodels/AddServerViewModel.kt | 15 +++++++++----- .../jellyfin/viewmodels/DownloadsViewModel.kt | 15 +++++++++----- .../viewmodels/EpisodeBottomSheetViewModel.kt | 20 ++++++++++--------- .../jellyfin/viewmodels/LoginViewModel.kt | 17 ++++++++++------ .../jellyfin/viewmodels/MovieViewModel.kt | 20 ++++++++++--------- .../jellyfin/viewmodels/SeasonViewModel.kt | 14 ++++++++----- .../viewmodels/ServerAddressesViewModel.kt | 14 ++++++++----- .../viewmodels/ServerSelectViewModel.kt | 20 ++++++++++--------- .../jellyfin/viewmodels/ShowViewModel.kt | 14 ++++++++----- .../jellyfin/viewmodels/UsersViewModel.kt | 14 ++++++++----- .../viewmodels/PlayerActivityViewModel.kt | 14 ++++++++----- 22 files changed, 170 insertions(+), 116 deletions(-) diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt index 46330153..5d5c2a80 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/PlayerActivity.kt @@ -42,6 +42,7 @@ import dev.jdtech.jellyfin.mpv.TrackType import dev.jdtech.jellyfin.utils.PlayerGestureHelper import dev.jdtech.jellyfin.utils.PreviewScrubListener import dev.jdtech.jellyfin.viewmodels.PlayerActivityViewModel +import dev.jdtech.jellyfin.viewmodels.PlayerEvents import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -169,8 +170,10 @@ class PlayerActivity : BasePlayerActivity() { } launch { - viewModel.navigateBack.collect { - if (it) finish() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is PlayerEvents.NavigateBack -> finish() + } } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt index c6b0627a..ef1dab39 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/AddServerFragment.kt @@ -17,6 +17,7 @@ import androidx.navigation.fragment.findNavController import dagger.hilt.android.AndroidEntryPoint import dev.jdtech.jellyfin.adapters.DiscoveredServerListAdapter import dev.jdtech.jellyfin.databinding.FragmentAddServerBinding +import dev.jdtech.jellyfin.viewmodels.AddServerEvent import dev.jdtech.jellyfin.viewmodels.AddServerViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -81,9 +82,9 @@ class AddServerFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.navigateToLogin.collect { - if (it) { - navigateToLoginFragment() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is AddServerEvent.NavigateToLogin -> navigateToLoginFragment() } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/DownloadsFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/DownloadsFragment.kt index 9887717d..1f154cef 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/DownloadsFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/DownloadsFragment.kt @@ -20,6 +20,7 @@ import dev.jdtech.jellyfin.models.FindroidItem import dev.jdtech.jellyfin.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidShow import dev.jdtech.jellyfin.utils.restart +import dev.jdtech.jellyfin.viewmodels.DownloadsEvent import dev.jdtech.jellyfin.viewmodels.DownloadsViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -48,14 +49,18 @@ class DownloadsFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { launch { - viewModel.connectionError.collect { - Snackbar.make(binding.root, CoreR.string.no_server_connection, Snackbar.LENGTH_INDEFINITE) - .setTextMaxLines(2) - .setAction(CoreR.string.offline_mode) { - appPreferences.offlineMode = true - activity?.restart() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is DownloadsEvent.ConnectionError -> { + Snackbar.make(binding.root, CoreR.string.no_server_connection, Snackbar.LENGTH_INDEFINITE) + .setTextMaxLines(2) + .setAction(CoreR.string.offline_mode) { + appPreferences.offlineMode = true + activity?.restart() + } + .show() } - .show() + } } } launch { diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt index b230f2e9..3ec17bb1 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/EpisodeBottomSheetFragment.kt @@ -33,6 +33,7 @@ import dev.jdtech.jellyfin.models.UiText import dev.jdtech.jellyfin.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloading import dev.jdtech.jellyfin.utils.setIconTintColorAttribute +import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetEvent import dev.jdtech.jellyfin.viewmodels.EpisodeBottomSheetViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import kotlinx.coroutines.launch @@ -122,14 +123,11 @@ class EpisodeBottomSheetFragment : BottomSheetDialogFragment() { } launch { - viewModel.downloadError.collect { uiText -> - createErrorDialog(uiText) - } - } - - launch { - viewModel.navigateBack.collect { - if (it) findNavController().navigateUp() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is EpisodeBottomSheetEvent.NavigateBack -> findNavController().navigateUp() + is EpisodeBottomSheetEvent.DownloadError -> createErrorDialog(event.uiText) + } } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt index 39330a1b..a98864a6 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/LoginFragment.kt @@ -19,6 +19,7 @@ import dev.jdtech.jellyfin.AppPreferences import dev.jdtech.jellyfin.adapters.UserLoginListAdapter import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.databinding.FragmentLoginBinding +import dev.jdtech.jellyfin.viewmodels.LoginEvent import dev.jdtech.jellyfin.viewmodels.LoginViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -123,9 +124,9 @@ class LoginFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.navigateToMain.collect { - if (it) { - navigateToHomeFragment() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is LoginEvent.NavigateToHome -> navigateToHomeFragment() } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/MovieFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/MovieFragment.kt index 321fa077..781e240d 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/MovieFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/MovieFragment.kt @@ -37,6 +37,7 @@ import dev.jdtech.jellyfin.models.isDownloaded import dev.jdtech.jellyfin.models.isDownloading import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.setIconTintColorAttribute +import dev.jdtech.jellyfin.viewmodels.MovieEvent import dev.jdtech.jellyfin.viewmodels.MovieViewModel import dev.jdtech.jellyfin.viewmodels.PlayerViewModel import kotlinx.coroutines.launch @@ -118,14 +119,11 @@ class MovieFragment : Fragment() { } launch { - viewModel.downloadError.collect { uiText -> - createErrorDialog(uiText) - } - } - - launch { - viewModel.navigateBack.collect { - if (it) findNavController().navigateUp() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is MovieEvent.NavigateBack -> findNavController().navigateUp() + is MovieEvent.DownloadError -> createErrorDialog(event.uiText) + } } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt index 5ab629e8..66cff353 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/SeasonFragment.kt @@ -20,6 +20,7 @@ import dev.jdtech.jellyfin.models.FindroidEpisode import dev.jdtech.jellyfin.models.PlayerItem import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.viewmodels.PlayerViewModel +import dev.jdtech.jellyfin.viewmodels.SeasonEvent import dev.jdtech.jellyfin.viewmodels.SeasonViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -60,8 +61,10 @@ class SeasonFragment : Fragment() { } launch { - viewModel.navigateBack.collect { - if (it) findNavController().navigateUp() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is SeasonEvent.NavigateBack -> findNavController().navigateUp() + } } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerAddressesFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerAddressesFragment.kt index c753e5d0..02b19b1a 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerAddressesFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerAddressesFragment.kt @@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.adapters.ServerAddressAdapter import dev.jdtech.jellyfin.databinding.FragmentServerAddressesBinding import dev.jdtech.jellyfin.dialogs.AddServerAddressDialog import dev.jdtech.jellyfin.dialogs.DeleteServerAddressDialog +import dev.jdtech.jellyfin.viewmodels.ServerAddressesEvent import dev.jdtech.jellyfin.viewmodels.ServerAddressesViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -57,9 +58,9 @@ class ServerAddressesFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.navigateToMain.collect { - if (it) { - navigateToMainActivity() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is ServerAddressesEvent.NavigateToHome -> navigateToMainActivity() } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt index eb212f14..a9a7fc07 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ServerSelectFragment.kt @@ -14,6 +14,7 @@ import dagger.hilt.android.AndroidEntryPoint import dev.jdtech.jellyfin.adapters.ServerGridAdapter import dev.jdtech.jellyfin.databinding.FragmentServerSelectBinding import dev.jdtech.jellyfin.dialogs.DeleteServerDialogFragment +import dev.jdtech.jellyfin.viewmodels.ServerSelectEvent import dev.jdtech.jellyfin.viewmodels.ServerSelectViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -61,13 +62,11 @@ class ServerSelectFragment : Fragment() { } } launch { - viewModel.navigateToMain.collect { - if (it) navigateToMainActivity() - } - } - launch { - viewModel.navigateToLogin.collect { - if (it) navigateToLoginFragment() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is ServerSelectEvent.NavigateToHome -> navigateToMainActivity() + is ServerSelectEvent.NavigateToLogin -> navigateToLoginFragment() + } } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ShowFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ShowFragment.kt index 2be8b0ba..940a10e6 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ShowFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/ShowFragment.kt @@ -32,6 +32,7 @@ import dev.jdtech.jellyfin.models.isDownloaded import dev.jdtech.jellyfin.utils.checkIfLoginRequired import dev.jdtech.jellyfin.utils.setIconTintColorAttribute import dev.jdtech.jellyfin.viewmodels.PlayerViewModel +import dev.jdtech.jellyfin.viewmodels.ShowEvent import dev.jdtech.jellyfin.viewmodels.ShowViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -79,8 +80,10 @@ class ShowFragment : Fragment() { } launch { - viewModel.navigateBack.collect { - if (it) findNavController().navigateUp() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is ShowEvent.NavigateBack -> findNavController().navigateUp() + } } } } diff --git a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/UsersFragment.kt b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/UsersFragment.kt index 2923653c..52fa5773 100644 --- a/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/UsersFragment.kt +++ b/app/phone/src/main/java/dev/jdtech/jellyfin/fragments/UsersFragment.kt @@ -16,6 +16,7 @@ import dev.jdtech.jellyfin.AppNavigationDirections import dev.jdtech.jellyfin.adapters.UserListAdapter import dev.jdtech.jellyfin.databinding.FragmentUsersBinding import dev.jdtech.jellyfin.dialogs.DeleteUserDialogFragment +import dev.jdtech.jellyfin.viewmodels.UsersEvent import dev.jdtech.jellyfin.viewmodels.UsersViewModel import kotlinx.coroutines.launch import timber.log.Timber @@ -54,9 +55,9 @@ class UsersFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { - viewModel.navigateToMain.collect { - if (it) { - navigateToMainActivity() + viewModel.eventsChannelFlow.collect { event -> + when (event) { + is UsersEvent.NavigateToHome -> navigateToMainActivity() } } } diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt index e9dbf8c2..eb6361bd 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/AddServerViewModel.kt @@ -15,10 +15,10 @@ import dev.jdtech.jellyfin.models.ServerAddress import dev.jdtech.jellyfin.models.UiText import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.discovery.RecommendedServerInfo @@ -38,11 +38,12 @@ constructor( ) : ViewModel() { private val _uiState = MutableStateFlow(UiState.Normal) val uiState = _uiState.asStateFlow() - private val _navigateToLogin = MutableSharedFlow() - val navigateToLogin = _navigateToLogin.asSharedFlow() private val _discoveredServersState = MutableStateFlow(DiscoveredServersState.Loading) val discoveredServersState = _discoveredServersState.asStateFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() + private val discoveredServers = mutableListOf() private var serverFound = false @@ -206,7 +207,7 @@ constructor( } _uiState.emit(UiState.Normal) - _navigateToLogin.emit(true) + eventsChannel.send(AddServerEvent.NavigateToLogin) } /** @@ -269,3 +270,7 @@ constructor( } } } + +sealed interface AddServerEvent { + data object NavigateToLogin : AddServerEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/DownloadsViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/DownloadsViewModel.kt index 9321f676..a2ccb8a1 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/DownloadsViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/DownloadsViewModel.kt @@ -11,11 +11,11 @@ import dev.jdtech.jellyfin.models.FindroidMovie import dev.jdtech.jellyfin.models.FindroidShow import dev.jdtech.jellyfin.models.UiText import dev.jdtech.jellyfin.repository.JellyfinRepository +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -28,8 +28,9 @@ constructor( ) : ViewModel() { private val _uiState = MutableStateFlow(UiState.Loading) val uiState = _uiState.asStateFlow() - private val _connectionError = MutableSharedFlow() - val connectionError = _connectionError.asSharedFlow() + + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() sealed class UiState { data class Normal(val sections: List) : UiState() @@ -49,7 +50,7 @@ constructor( // Give the UI a chance to load delay(100) } catch (e: Exception) { - _connectionError.emit(e) + eventsChannel.send(DownloadsEvent.ConnectionError(e)) } } } @@ -88,3 +89,7 @@ constructor( } } } + +sealed interface DownloadsEvent { + data class ConnectionError(val error: Exception) : DownloadsEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt index 8f55a558..9e49a04b 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/EpisodeBottomSheetViewModel.kt @@ -13,10 +13,10 @@ import dev.jdtech.jellyfin.models.UiText import dev.jdtech.jellyfin.models.isDownloading import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.utils.Downloader -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import java.io.File import java.util.UUID @@ -37,11 +37,8 @@ constructor( private val _downloadStatus = MutableStateFlow(Pair(0, 0)) val downloadStatus = _downloadStatus.asStateFlow() - private val _downloadError = MutableSharedFlow() - val downloadError = _downloadError.asSharedFlow() - - private val _navigateBack = MutableSharedFlow() - val navigateBack = _navigateBack.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() private val handler = Handler(Looper.getMainLooper()) @@ -75,7 +72,7 @@ constructor( ) } catch (_: NullPointerException) { // Navigate back because item does not exist (probably because it's been deleted) - _navigateBack.emit(true) + eventsChannel.send(EpisodeBottomSheetEvent.NavigateBack) } catch (e: Exception) { _uiState.emit(UiState.Error(e)) } @@ -133,7 +130,7 @@ constructor( _downloadStatus.emit(Pair(10, Random.nextInt())) if (result.second != null) { - _downloadError.emit(result.second!!) + eventsChannel.send(EpisodeBottomSheetEvent.DownloadError(result.second!!)) } loadEpisode(item.id) @@ -188,3 +185,8 @@ constructor( handler.removeCallbacksAndMessages(null) } } + +sealed interface EpisodeBottomSheetEvent { + data object NavigateBack : EpisodeBottomSheetEvent + data class DownloadError(val uiText: UiText) : EpisodeBottomSheetEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt index a5055eb5..81f7fcea 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/LoginViewModel.kt @@ -11,11 +11,11 @@ import dev.jdtech.jellyfin.models.UiText import dev.jdtech.jellyfin.models.User import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.api.client.extensions.authenticateWithQuickConnect @@ -38,8 +38,9 @@ constructor( val usersState = _usersState.asStateFlow() private val _quickConnectUiState = MutableStateFlow(QuickConnectUiState.Disabled) val quickConnectUiState = _quickConnectUiState.asStateFlow() - private val _navigateToMain = MutableSharedFlow() - val navigateToMain = _navigateToMain.asSharedFlow() + + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() private var quickConnectJob: Job? = null @@ -121,7 +122,7 @@ constructor( saveAuthenticationResult(authenticationResult) _uiState.emit(UiState.Normal) - _navigateToMain.emit(true) + eventsChannel.send(LoginEvent.NavigateToHome) } catch (e: Exception) { val message = if (e.message?.contains("401") == true) { @@ -157,7 +158,7 @@ constructor( saveAuthenticationResult(authenticationResult) _quickConnectUiState.emit(QuickConnectUiState.Normal) - _navigateToMain.emit(true) + eventsChannel.send(LoginEvent.NavigateToHome) } catch (_: Exception) { _quickConnectUiState.emit(QuickConnectUiState.Normal) } @@ -189,3 +190,7 @@ constructor( } } } + +sealed interface LoginEvent { + data object NavigateToHome : LoginEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/MovieViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/MovieViewModel.kt index aa8e2dbf..0dd9c9d9 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/MovieViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/MovieViewModel.kt @@ -21,10 +21,10 @@ import dev.jdtech.jellyfin.repository.JellyfinRepository import dev.jdtech.jellyfin.utils.Downloader import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Runnable -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.model.api.BaseItemPerson @@ -49,11 +49,8 @@ constructor( private val _downloadStatus = MutableStateFlow(Pair(0, 0)) val downloadStatus = _downloadStatus.asStateFlow() - private val _downloadError = MutableSharedFlow() - val downloadError = _downloadError.asSharedFlow() - - private val _navigateBack = MutableSharedFlow() - val navigateBack = _navigateBack.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() private val handler = Handler(Looper.getMainLooper()) @@ -115,7 +112,7 @@ constructor( ) } catch (_: NullPointerException) { // Navigate back because item does not exist (probably because it's been deleted) - _navigateBack.emit(true) + eventsChannel.send(MovieEvent.NavigateBack) } catch (e: Exception) { _uiState.emit(UiState.Error(e)) } @@ -330,7 +327,7 @@ constructor( _downloadStatus.emit(Pair(10, Random.nextInt())) if (result.second != null) { - _downloadError.emit(result.second!!) + eventsChannel.send(MovieEvent.DownloadError(result.second!!)) } loadData(item.id) @@ -385,3 +382,8 @@ constructor( handler.removeCallbacksAndMessages(null) } } + +sealed interface MovieEvent { + data object NavigateBack : MovieEvent + data class DownloadError(val uiText: UiText) : MovieEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt index 2874bd70..ed270cad 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/SeasonViewModel.kt @@ -6,10 +6,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel import dev.jdtech.jellyfin.models.EpisodeItem import dev.jdtech.jellyfin.models.FindroidSeason import dev.jdtech.jellyfin.repository.JellyfinRepository -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.ItemFields import java.util.UUID @@ -24,8 +24,8 @@ constructor( private val _uiState = MutableStateFlow(UiState.Loading) val uiState = _uiState.asStateFlow() - private val _navigateBack = MutableSharedFlow() - val navigateBack = _navigateBack.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() sealed class UiState { data class Normal(val episodes: List) : UiState() @@ -44,7 +44,7 @@ constructor( _uiState.emit(UiState.Normal(episodes)) } catch (_: NullPointerException) { // Navigate back because item does not exist (probably because it's been deleted) - _navigateBack.emit(true) + eventsChannel.send(SeasonEvent.NavigateBack) } catch (e: Exception) { _uiState.emit(UiState.Error(e)) } @@ -63,3 +63,7 @@ constructor( return listOf(header) + episodes.map { EpisodeItem.Episode(it) } } } + +sealed interface SeasonEvent { + data object NavigateBack : SeasonEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerAddressesViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerAddressesViewModel.kt index 925962ad..4d2bc18c 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerAddressesViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerAddressesViewModel.kt @@ -7,10 +7,10 @@ import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.models.ServerAddress import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import timber.log.Timber import java.util.UUID @@ -32,8 +32,8 @@ constructor( data class Error(val error: Exception) : UiState() } - private val _navigateToMain = MutableSharedFlow() - val navigateToMain = _navigateToMain.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() private var currentServerId: String = "" @@ -75,7 +75,7 @@ constructor( jellyfinApi.api.baseUrl = address.address - _navigateToMain.emit(true) + eventsChannel.send(ServerAddressesEvent.NavigateToHome) } } @@ -87,3 +87,7 @@ constructor( } } } + +sealed interface ServerAddressesEvent { + data object NavigateToHome : ServerAddressesEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt index e098be1d..974758a6 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ServerSelectViewModel.kt @@ -8,10 +8,10 @@ import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.models.Server import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import javax.inject.Inject @@ -26,11 +26,8 @@ constructor( private val _uiState = MutableStateFlow(UiState.Loading) val uiState = _uiState.asStateFlow() - private val _navigateToMain = MutableSharedFlow() - val navigateToMain = _navigateToMain.asSharedFlow() - - private val _navigateToLogin = MutableSharedFlow() - val navigateToLogin = _navigateToLogin.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() sealed class UiState { data class Normal(val servers: List) : UiState() @@ -75,7 +72,7 @@ constructor( userId = null } appPreferences.currentServer = server.id - _navigateToLogin.emit(true) + eventsChannel.send(ServerSelectEvent.NavigateToLogin) return@launch } @@ -87,7 +84,12 @@ constructor( appPreferences.currentServer = server.id - _navigateToMain.emit(true) + eventsChannel.send(ServerSelectEvent.NavigateToHome) } } } + +sealed interface ServerSelectEvent { + data object NavigateToHome : ServerSelectEvent + data object NavigateToLogin : ServerSelectEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ShowViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ShowViewModel.kt index bc9e0532..f2836935 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ShowViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/ShowViewModel.kt @@ -8,10 +8,10 @@ import dev.jdtech.jellyfin.models.FindroidSeason import dev.jdtech.jellyfin.models.FindroidShow import dev.jdtech.jellyfin.repository.JellyfinRepository import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.jellyfin.sdk.model.api.BaseItemPerson @@ -27,8 +27,8 @@ constructor( private val _uiState = MutableStateFlow(UiState.Loading) val uiState = _uiState.asStateFlow() - private val _navigateBack = MutableSharedFlow() - val navigateBack = _navigateBack.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() sealed class UiState { data class Normal( @@ -93,7 +93,7 @@ constructor( ) } catch (_: NullPointerException) { // Navigate back because item does not exist (probably because it's been deleted) - _navigateBack.emit(true) + eventsChannel.send(ShowEvent.NavigateBack) } catch (e: Exception) { _uiState.emit(UiState.Error(e)) } @@ -189,3 +189,7 @@ constructor( return dateRange.joinToString(separator = " - ") } } + +sealed interface ShowEvent { + data object NavigateBack : ShowEvent +} diff --git a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/UsersViewModel.kt b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/UsersViewModel.kt index 13ad4f62..26317828 100644 --- a/core/src/main/java/dev/jdtech/jellyfin/viewmodels/UsersViewModel.kt +++ b/core/src/main/java/dev/jdtech/jellyfin/viewmodels/UsersViewModel.kt @@ -7,10 +7,10 @@ import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.database.ServerDatabaseDao import dev.jdtech.jellyfin.models.User import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @@ -31,8 +31,8 @@ constructor( data class Error(val error: Exception) : UiState() } - private val _navigateToMain = MutableSharedFlow() - val navigateToMain = _navigateToMain.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() private var currentServerId: String = "" @@ -77,7 +77,11 @@ constructor( userId = user.id } - _navigateToMain.emit(true) + eventsChannel.send(UsersEvent.NavigateToHome) } } } + +sealed interface UsersEvent { + data object NavigateToHome : UsersEvent +} diff --git a/player/video/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt b/player/video/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt index a7a8cfa3..4ef91f28 100644 --- a/player/video/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt +++ b/player/video/src/main/java/dev/jdtech/jellyfin/viewmodels/PlayerActivityViewModel.kt @@ -27,11 +27,11 @@ import dev.jdtech.jellyfin.utils.bif.BifUtil import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -60,8 +60,8 @@ constructor( ) val uiState = _uiState.asStateFlow() - private val _navigateBack = MutableSharedFlow() - val navigateBack = _navigateBack.asSharedFlow() + private val eventsChannel = Channel() + val eventsChannelFlow = eventsChannel.receiveAsFlow() private val intros: MutableMap = mutableMapOf() @@ -317,7 +317,7 @@ constructor( } ExoPlayer.STATE_ENDED -> { stateString = "ExoPlayer.STATE_ENDED -" - _navigateBack.tryEmit(true) + eventsChannel.trySend(PlayerEvents.NavigateBack) } } Timber.d("Changed player state to $stateString") @@ -366,3 +366,7 @@ constructor( } } } + +sealed interface PlayerEvents { + data object NavigateBack : PlayerEvents +}