Remove dependency on livedata libraries (#1453)
## Description Refactors all usages of `LiveData` into flows in keeping with best practices. This also includes refactoring several `ViewModel`s to combine multiple `LiveData` into a new single `StateFlow`. There are no user facing changes ### Related issues None ### Testing Emulator ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
982d9e6fe8
commit
0b450fc0ff
90 changed files with 2170 additions and 2283 deletions
|
|
@ -2,10 +2,12 @@ package com.github.damontecres.wholphin.services
|
|||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import com.github.damontecres.wholphin.services.hilt.IoCoroutineScope
|
||||
import com.github.damontecres.wholphin.ui.collectLatestIn
|
||||
import com.github.damontecres.wholphin.util.GetEpisodesRequestHandler
|
||||
import com.google.common.cache.CacheBuilder
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
|
|
@ -152,7 +154,7 @@ class DatePlayedInvalidationService
|
|||
private val activity = (context as AppCompatActivity)
|
||||
|
||||
init {
|
||||
serverRepository.current.observe(activity) {
|
||||
serverRepository.current.collectLatestIn(activity.lifecycleScope) {
|
||||
datePlayedService.invalidateAll()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -265,8 +265,8 @@ class HomeSettingsService
|
|||
*/
|
||||
suspend fun createDefault(userId: UUID): HomePageResolvedSettings {
|
||||
Timber.v("Creating default settings")
|
||||
val user = serverRepository.currentUser.value?.takeIf { it.id == userId }
|
||||
val userDto = serverRepository.currentUserDto.value?.takeIf { it.id == userId }
|
||||
val user = serverRepository.currentUser?.takeIf { it.id == userId }
|
||||
val userDto = serverRepository.currentUserDto?.takeIf { it.id == userId }
|
||||
val libraries =
|
||||
if (user != null) {
|
||||
navDrawerService.getFilteredUserLibraries(user, userDto?.tvAccess ?: false)
|
||||
|
|
@ -679,7 +679,7 @@ class HomeSettingsService
|
|||
val genreImages =
|
||||
getGenreImageMap(
|
||||
api = api,
|
||||
userId = serverRepository.currentUser.value?.id,
|
||||
userId = serverRepository.currentUser?.id,
|
||||
scope = scope,
|
||||
imageUrlService = imageUrlService,
|
||||
genres = genreIds,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import androidx.work.WorkManager
|
|||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.ui.collectLatestIn
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
|
|
@ -94,7 +95,7 @@ class LatestNextUpSchedulerService
|
|||
internal var dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
|
||||
init {
|
||||
serverRepository.current.observe(activity) { user ->
|
||||
serverRepository.current.collectLatestIn(activity.lifecycleScope) { user ->
|
||||
Timber.v("New user %s", user?.user?.id)
|
||||
if (user == null) {
|
||||
workManager.cancelUniqueWork(LatestNextUpWorker.WORK_NAME)
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class MediaManagementService
|
|||
return enabled &&
|
||||
item.canDelete &&
|
||||
if (item.type == BaseItemKind.RECORDING) {
|
||||
serverRepository.currentUserDto.value
|
||||
serverRepository.currentUserDto
|
||||
?.policy
|
||||
?.enableLiveTvManagement == true
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class MediaReportService
|
|||
.content.mediaSources
|
||||
val sourcesJson = json.encodeToString(sources)
|
||||
val playbackPrefs = userPreferencesService.getCurrent().appPreferences.playbackPreferences
|
||||
val serverVersion = serverRepository.currentServer.value?.serverVersion
|
||||
val serverVersion = serverRepository.currentServer?.serverVersion
|
||||
val deviceProfile =
|
||||
deviceProfileService.getOrCreateDeviceProfile(playbackPrefs, serverVersion)
|
||||
val deviceProfileJson = json.encodeToString(deviceProfile)
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ class MusicService
|
|||
val items =
|
||||
api.instantMixApi
|
||||
.getInstantMixFromItem(
|
||||
userId = serverRepository.currentUser.value?.id,
|
||||
userId = serverRepository.currentUser?.id,
|
||||
itemId = itemId,
|
||||
limit = 200,
|
||||
fields = DefaultItemFields,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.asFlow
|
||||
import com.github.damontecres.wholphin.data.ServerPreferencesDao
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||
|
|
@ -58,9 +57,8 @@ class NavDrawerService
|
|||
|
||||
init {
|
||||
// Handle updating the nav drawer when the user changes
|
||||
serverRepository.currentUser
|
||||
.asFlow()
|
||||
.combine(serverRepository.currentUserDto.asFlow()) { user, userDto ->
|
||||
serverRepository.currentUserFlow
|
||||
.combine(serverRepository.currentUserDtoFlow) { user, userDto ->
|
||||
Pair(user, userDto)
|
||||
}.onEach { (user, userDto) ->
|
||||
Timber.d("User updated: user=%s, userDto=%s", user?.id, userDto?.id)
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ class PlaylistCreator
|
|||
val request =
|
||||
filter.applyTo(
|
||||
GetItemsRequest(
|
||||
userId = serverRepository.currentUser.value?.id,
|
||||
userId = serverRepository.currentUser?.id,
|
||||
parentId = playlistId,
|
||||
fields = DefaultItemFields,
|
||||
startIndex = startIndex,
|
||||
|
|
@ -324,7 +324,7 @@ class PlaylistCreator
|
|||
name: String,
|
||||
initialItems: List<UUID>,
|
||||
): UUID? =
|
||||
serverRepository.currentUser.value?.let { user ->
|
||||
serverRepository.currentUser?.let { user ->
|
||||
api.playlistsApi
|
||||
.createPlaylist(
|
||||
CreatePlaylistDto(
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class SeerrServerRepository
|
|||
server = seerrServerDao.getServer(url)
|
||||
}
|
||||
server?.server?.let { server ->
|
||||
serverRepository.currentUser.value?.let { jellyfinUser ->
|
||||
serverRepository.currentUser?.let { jellyfinUser ->
|
||||
// TODO test api key
|
||||
val user =
|
||||
SeerrUser(
|
||||
|
|
@ -126,7 +126,7 @@ class SeerrServerRepository
|
|||
server = seerrServerDao.getServer(url)
|
||||
}
|
||||
server?.server?.let { server ->
|
||||
serverRepository.currentUser.value?.let { jellyfinUser ->
|
||||
serverRepository.currentUser?.let { jellyfinUser ->
|
||||
// TODO Need to update server early so that cookies are saved
|
||||
seerrApi.update(server.url, null)
|
||||
val userConfig = seerrLogin(seerrApi.api, authMethod, username, password)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||
import com.github.damontecres.wholphin.ui.collectLatestIn
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.showToast
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
|
|
@ -42,7 +43,7 @@ class ServerEventListener
|
|||
|
||||
init {
|
||||
activity.lifecycle.addObserver(this)
|
||||
serverRepository.current.observe(activity) {
|
||||
serverRepository.current.collectLatestIn(activity.lifecycleScope) {
|
||||
Timber.d("New user/server: %s", it)
|
||||
listenJob?.cancel()
|
||||
if (it != null) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class StreamChoiceService
|
|||
private val serverRepository: ServerRepository,
|
||||
private val playbackLanguageChoiceDao: PlaybackLanguageChoiceDao,
|
||||
) {
|
||||
private val userConfig: UserConfiguration? get() = serverRepository.currentUserDto.value?.configuration
|
||||
private val userConfig: UserConfiguration? get() = serverRepository.currentUserDto?.configuration
|
||||
|
||||
suspend fun updateAudio(
|
||||
dto: BaseItemDto,
|
||||
|
|
@ -58,7 +58,7 @@ class StreamChoiceService
|
|||
) {
|
||||
val seriesId = dto.seriesId
|
||||
if (seriesId != null) {
|
||||
val userId = serverRepository.currentUser.value!!.rowId
|
||||
val userId = serverRepository.currentUser!!.rowId
|
||||
val currentPlc =
|
||||
playbackLanguageChoiceDao.get(userId, seriesId)
|
||||
?: PlaybackLanguageChoice(userId, seriesId, dto.id)
|
||||
|
|
@ -70,7 +70,7 @@ class StreamChoiceService
|
|||
|
||||
suspend fun getPlaybackLanguageChoice(dto: BaseItemDto) =
|
||||
dto.seriesId?.let {
|
||||
playbackLanguageChoiceDao.get(serverRepository.currentUser.value!!.rowId, it)
|
||||
playbackLanguageChoiceDao.get(serverRepository.currentUser!!.rowId, it)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -107,7 +107,13 @@ class StreamChoiceService
|
|||
plc: PlaybackLanguageChoice?,
|
||||
prefs: UserPreferences,
|
||||
): MediaStream? {
|
||||
val plc = plc ?: seriesId?.let { playbackLanguageChoiceDao.get(serverRepository.currentUser.value!!.rowId, it) }
|
||||
val plc =
|
||||
plc ?: seriesId?.let {
|
||||
playbackLanguageChoiceDao.get(
|
||||
serverRepository.currentUser!!.rowId,
|
||||
it,
|
||||
)
|
||||
}
|
||||
return source.mediaStreams?.letNotEmpty { streams ->
|
||||
val candidates = streams.filter { it.type == MediaStreamType.AUDIO }
|
||||
chooseAudioStream(candidates, itemPlayback, plc, prefs)
|
||||
|
|
@ -158,7 +164,7 @@ class StreamChoiceService
|
|||
val plc =
|
||||
plc ?: seriesId?.let {
|
||||
playbackLanguageChoiceDao.get(
|
||||
serverRepository.currentUser.value!!.rowId,
|
||||
serverRepository.currentUser!!.rowId,
|
||||
it,
|
||||
)
|
||||
}
|
||||
|
|
@ -194,7 +200,7 @@ class StreamChoiceService
|
|||
}
|
||||
val itemPlayback =
|
||||
ItemPlayback(
|
||||
userId = serverRepository.currentUser.value!!.rowId,
|
||||
userId = serverRepository.currentUser!!.rowId,
|
||||
itemId = UUID.randomUUID(), // Not used for ONLY_FORCED resolution
|
||||
subtitleIndex = TrackIndex.ONLY_FORCED,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
|
|
@ -52,8 +51,7 @@ class SuggestionService
|
|||
parentId: UUID,
|
||||
itemKind: BaseItemKind,
|
||||
): Flow<SuggestionsResource> {
|
||||
return serverRepository.currentUser
|
||||
.asFlow()
|
||||
return serverRepository.currentUserFlow
|
||||
.flatMapLatest { user ->
|
||||
val userId = user?.id ?: return@flatMapLatest flowOf(SuggestionsResource.Empty)
|
||||
val cachedSuggestions = cache.get(userId, parentId, itemKind)
|
||||
|
|
|
|||
|
|
@ -8,15 +8,14 @@ import androidx.work.Constraints
|
|||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.workDataOf
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.services.hilt.IoDispatcher
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
|
@ -35,6 +34,7 @@ class SuggestionsSchedulerService
|
|||
@param:ActivityContext private val context: Context,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val workManager: WorkManager,
|
||||
@param:IoDispatcher private val dispatcher: CoroutineDispatcher,
|
||||
) {
|
||||
private val activity =
|
||||
(context as? AppCompatActivity)
|
||||
|
|
@ -42,18 +42,18 @@ class SuggestionsSchedulerService
|
|||
"SuggestionsSchedulerService requires an AppCompatActivity context, but received: ${context::class.java.name}",
|
||||
)
|
||||
|
||||
// Exposed for testing
|
||||
internal var dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||
internal var initialDelaySecondsProvider: () -> Long = { 60L + Random.nextLong(0L, 121L) }
|
||||
|
||||
init {
|
||||
serverRepository.current.observe(activity) { user ->
|
||||
Timber.v("New user %s", user?.user?.id)
|
||||
if (user == null) {
|
||||
workManager.cancelUniqueWork(SuggestionsWorker.WORK_NAME)
|
||||
} else {
|
||||
activity.lifecycleScope.launch(dispatcher + ExceptionHandler()) {
|
||||
scheduleWork(user.user.id, user.server.id)
|
||||
activity.lifecycleScope.launch(dispatcher + ExceptionHandler()) {
|
||||
serverRepository.current.collect { user ->
|
||||
Timber.v("New user %s", user?.user?.id)
|
||||
if (user == null) {
|
||||
workManager.cancelUniqueWork(SuggestionsWorker.WORK_NAME)
|
||||
} else {
|
||||
activity.lifecycleScope.launch(dispatcher + ExceptionHandler()) {
|
||||
scheduleWork(user.user.id, user.server.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class UserPreferencesService
|
|||
val flow = preferencesDataStore.data.map { UserPreferences(it) }
|
||||
|
||||
suspend fun getCurrent(): UserPreferences =
|
||||
serverRepository.currentUserDto.value!!.configuration.let { userConfig ->
|
||||
serverRepository.currentUserDto!!.configuration.let { userConfig ->
|
||||
val appPrefs = preferencesDataStore.data.firstOrNull() ?: AppPreferences.getDefaultInstance()
|
||||
UserPreferences(
|
||||
appPrefs,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.content.Context
|
|||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.damontecres.wholphin.BuildConfig
|
||||
import com.github.damontecres.wholphin.data.SeerrServerDao
|
||||
|
|
@ -39,7 +38,7 @@ class UserSwitchListener
|
|||
init {
|
||||
context as AppCompatActivity
|
||||
context.lifecycleScope.launchDefault {
|
||||
serverRepository.currentUser.asFlow().collect { user ->
|
||||
serverRepository.currentUserFlow.collect { user ->
|
||||
Timber.d("New user")
|
||||
seerrServerRepository.clear()
|
||||
homeSettingsService.currentSettings.update { HomePageResolvedSettings.EMPTY }
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ object AppModule {
|
|||
.addInterceptor {
|
||||
val request = it.request()
|
||||
val newRequest =
|
||||
serverRepository.currentUser.value?.accessToken?.let { token ->
|
||||
serverRepository.current.value?.user?.accessToken?.let { token ->
|
||||
request
|
||||
.newBuilder()
|
||||
.addHeader(
|
||||
|
|
@ -170,7 +170,10 @@ object AppModule {
|
|||
appPreference: DataStore<AppPreferences>,
|
||||
@IoCoroutineScope scope: CoroutineScope,
|
||||
) = object : RememberTabManager {
|
||||
fun key(itemId: String) = "${serverRepository.currentServer.value?.id}_${serverRepository.currentUser.value?.id}_$itemId"
|
||||
fun key(itemId: String): String =
|
||||
serverRepository.current.value.let {
|
||||
"${it?.server?.id}_${it?.user?.id}_$itemId"
|
||||
}
|
||||
|
||||
override fun getRememberedTab(
|
||||
preferences: UserPreferences,
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import androidx.work.WorkManager
|
|||
import androidx.work.await
|
||||
import androidx.work.workDataOf
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.ui.collectLatestIn
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
|
|
@ -43,7 +44,7 @@ class TvProviderSchedulerService
|
|||
context.packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
|
||||
|
||||
init {
|
||||
serverRepository.current.observe(activity) { user ->
|
||||
serverRepository.current.collectLatestIn(activity.lifecycleScope) { user ->
|
||||
workManager.cancelUniqueWork(TvProviderWorker.WORK_NAME)
|
||||
if (supportsTvProvider) {
|
||||
if (user != null) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue