Support configuring interface language per user (#1406)
## Description Adds a setting to configure the app's language/locale. It is configured per user. This settings has no impact on audio or subtitles language choices. Only languages Wholphin has translations for can be used. If you would like to contribute new translations, check out https://translate.codeberg.org/projects/wholphin/! Dev notes: This doesn't exactly follow Android's recommendations because the [guidelines are for per-app](https://developer.android.com/guide/topics/resources/app-languages) and not users within the app. ### Related issues Closes #513 Closes #669 ### Testing - Emulator API 34 - Nvidia shield API 30 - Fire TV OS 7 API 28 ## Screenshots <img width="1280" height="720" alt="language Large" src="https://github.com/user-attachments/assets/bf1c0962-8a36-4012-aed9-eab564fc1114" /> ## AI or LLM usage None
This commit is contained in:
parent
625cdcb4a3
commit
903c45dfb8
43 changed files with 1501 additions and 418 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.StringRes
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
|
|
@ -19,6 +20,11 @@ import com.github.damontecres.wholphin.ui.main.settings.favoriteOptions
|
|||
import com.github.damontecres.wholphin.ui.playback.getTypeFor
|
||||
import com.github.damontecres.wholphin.ui.toBaseItems
|
||||
import com.github.damontecres.wholphin.ui.toServerString
|
||||
import com.github.damontecres.wholphin.ui.util.ResArgStringProvider
|
||||
import com.github.damontecres.wholphin.ui.util.ResProviderStringProvider
|
||||
import com.github.damontecres.wholphin.ui.util.ResStringProvider
|
||||
import com.github.damontecres.wholphin.ui.util.StringProvider
|
||||
import com.github.damontecres.wholphin.ui.util.StringStringProvider
|
||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||
import com.github.damontecres.wholphin.util.GetGenresRequestHandler
|
||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||
|
|
@ -275,11 +281,11 @@ class HomeSettingsService
|
|||
libraries
|
||||
.mapIndexed { index, it ->
|
||||
val parentId = it.itemId
|
||||
val title = getRecentlyAddedTitle(context, it)
|
||||
val title = getRecentlyAddedTitle(it)
|
||||
if (it.collectionType == CollectionType.LIVETV) {
|
||||
HomeRowConfigDisplay(
|
||||
id = index,
|
||||
title = context.getString(R.string.live_tv),
|
||||
title = ResStringProvider(R.string.live_tv),
|
||||
config = HomeRowConfig.TvPrograms(),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -295,7 +301,7 @@ class HomeSettingsService
|
|||
listOf(
|
||||
HomeRowConfigDisplay(
|
||||
id = includedIds.size + 1,
|
||||
title = context.getString(R.string.combine_continue_next),
|
||||
title = ResStringProvider(R.string.combine_continue_next),
|
||||
config = HomeRowConfig.ContinueWatchingCombined(),
|
||||
),
|
||||
)
|
||||
|
|
@ -303,12 +309,12 @@ class HomeSettingsService
|
|||
listOf(
|
||||
HomeRowConfigDisplay(
|
||||
id = includedIds.size + 1,
|
||||
title = context.getString(R.string.continue_watching),
|
||||
title = ResStringProvider(R.string.continue_watching),
|
||||
config = HomeRowConfig.ContinueWatching(),
|
||||
),
|
||||
HomeRowConfigDisplay(
|
||||
id = includedIds.size + 2,
|
||||
title = context.getString(R.string.next_up),
|
||||
title = ResStringProvider(R.string.next_up),
|
||||
config = HomeRowConfig.NextUp(),
|
||||
),
|
||||
)
|
||||
|
|
@ -355,7 +361,7 @@ class HomeSettingsService
|
|||
HomeSectionType.ACTIVE_RECORDINGS -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id++,
|
||||
title = context.getString(R.string.active_recordings),
|
||||
title = ResStringProvider(R.string.active_recordings),
|
||||
config = HomeRowConfig.Recordings(),
|
||||
)
|
||||
}
|
||||
|
|
@ -363,7 +369,7 @@ class HomeSettingsService
|
|||
HomeSectionType.RESUME -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id++,
|
||||
title = context.getString(R.string.continue_watching),
|
||||
title = ResStringProvider(R.string.continue_watching),
|
||||
config = HomeRowConfig.ContinueWatching(),
|
||||
)
|
||||
}
|
||||
|
|
@ -371,7 +377,7 @@ class HomeSettingsService
|
|||
HomeSectionType.NEXT_UP -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id++,
|
||||
title = context.getString(R.string.next_up),
|
||||
title = ResStringProvider(R.string.next_up),
|
||||
config = HomeRowConfig.NextUp(),
|
||||
)
|
||||
}
|
||||
|
|
@ -380,7 +386,7 @@ class HomeSettingsService
|
|||
if (userDto.tvAccess) {
|
||||
HomeRowConfigDisplay(
|
||||
id = id++,
|
||||
title = context.getString(R.string.live_tv),
|
||||
title = ResStringProvider(R.string.live_tv),
|
||||
config = HomeRowConfig.TvPrograms(),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -413,7 +419,7 @@ class HomeSettingsService
|
|||
HomeRowConfigDisplay(
|
||||
id = id++,
|
||||
title =
|
||||
context.getString(
|
||||
ResArgStringProvider(
|
||||
R.string.recently_added_in,
|
||||
it.name ?: "",
|
||||
),
|
||||
|
|
@ -441,7 +447,7 @@ class HomeSettingsService
|
|||
): HomeRowConfigDisplay =
|
||||
when (config) {
|
||||
is HomeRowConfig.ByParent -> {
|
||||
val name = getItemName(config.parentId) ?: ""
|
||||
val name = getItemName(null, config.parentId)
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
name,
|
||||
|
|
@ -452,7 +458,7 @@ class HomeSettingsService
|
|||
is HomeRowConfig.ContinueWatching -> {
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.continue_watching),
|
||||
ResStringProvider(R.string.continue_watching),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
|
@ -460,64 +466,64 @@ class HomeSettingsService
|
|||
is HomeRowConfig.ContinueWatchingCombined -> {
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.combine_continue_next),
|
||||
ResStringProvider(R.string.combine_continue_next),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.Genres -> {
|
||||
val name = getItemName(config.parentId) ?: ""
|
||||
val title = getItemName(R.string.genres_in, config.parentId)
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.genres_in, name),
|
||||
title,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.Studios -> {
|
||||
val name = getItemName(config.parentId) ?: ""
|
||||
val title = getItemName(R.string.studios_in, config.parentId)
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.studios_in, name),
|
||||
title,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.GetItems -> {
|
||||
HomeRowConfigDisplay(id, config.name, config)
|
||||
HomeRowConfigDisplay(id, StringStringProvider(config.name), config)
|
||||
}
|
||||
|
||||
is HomeRowConfig.NextUp -> {
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.next_up),
|
||||
ResStringProvider(R.string.next_up),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.RecentlyAdded -> {
|
||||
val name = getItemName(config.parentId) ?: ""
|
||||
val title = getItemName(R.string.recently_added_in, config.parentId)
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.recently_added_in, name),
|
||||
title,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.RecentlyReleased -> {
|
||||
val name = getItemName(config.parentId) ?: ""
|
||||
val title = getItemName(R.string.recently_released_in, config.parentId)
|
||||
HomeRowConfigDisplay(
|
||||
id,
|
||||
context.getString(R.string.recently_released_in, name),
|
||||
title,
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.Favorite -> {
|
||||
val name =
|
||||
context.getString(
|
||||
ResProviderStringProvider(
|
||||
R.string.favorite_items,
|
||||
context.getString(favoriteOptions[config.kind]!!),
|
||||
ResStringProvider(favoriteOptions[config.kind]!!),
|
||||
)
|
||||
HomeRowConfigDisplay(id, name, config)
|
||||
}
|
||||
|
|
@ -525,7 +531,7 @@ class HomeSettingsService
|
|||
is HomeRowConfig.Recordings -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id,
|
||||
title = context.getString(R.string.active_recordings),
|
||||
title = ResStringProvider(R.string.active_recordings),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
|
@ -533,7 +539,7 @@ class HomeSettingsService
|
|||
is HomeRowConfig.TvPrograms -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id,
|
||||
title = context.getString(R.string.live_tv),
|
||||
title = ResStringProvider(R.string.live_tv),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
|
@ -541,29 +547,40 @@ class HomeSettingsService
|
|||
is HomeRowConfig.TvChannels -> {
|
||||
HomeRowConfigDisplay(
|
||||
id = id,
|
||||
title = context.getString(R.string.channels),
|
||||
title = ResStringProvider(R.string.channels),
|
||||
config,
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.Suggestions -> {
|
||||
val name = getItemName(config.parentId) ?: ""
|
||||
val title = getItemName(R.string.suggestions_for, config.parentId)
|
||||
HomeRowConfigDisplay(
|
||||
id = id,
|
||||
title = context.getString(R.string.suggestions_for, name),
|
||||
title = title,
|
||||
config,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getItemName(itemId: UUID): String? =
|
||||
private suspend fun getItemName(
|
||||
@StringRes stringRes: Int?,
|
||||
itemId: UUID,
|
||||
default: StringProvider = StringStringProvider(""),
|
||||
): StringProvider =
|
||||
try {
|
||||
api.userLibraryApi
|
||||
.getItem(itemId = itemId)
|
||||
.content.name
|
||||
?.let {
|
||||
if (stringRes == null) {
|
||||
StringStringProvider(it)
|
||||
} else {
|
||||
ResArgStringProvider(stringRes, it)
|
||||
}
|
||||
} ?: default
|
||||
} catch (ex: Exception) {
|
||||
Timber.e(ex, "Could not get name for %s", itemId)
|
||||
context.getString(R.string.unknown)
|
||||
ResStringProvider(R.string.unknown)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -590,7 +607,7 @@ class HomeSettingsService
|
|||
)
|
||||
|
||||
Success(
|
||||
title = context.getString(R.string.continue_watching),
|
||||
title = ResStringProvider(R.string.continue_watching),
|
||||
items = resume,
|
||||
viewOptions = row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -609,7 +626,7 @@ class HomeSettingsService
|
|||
)
|
||||
|
||||
Success(
|
||||
title = context.getString(R.string.next_up),
|
||||
title = ResStringProvider(R.string.next_up),
|
||||
items = nextUp,
|
||||
viewOptions = row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -635,7 +652,7 @@ class HomeSettingsService
|
|||
)
|
||||
|
||||
Success(
|
||||
title = context.getString(R.string.continue_watching),
|
||||
title = ResStringProvider(R.string.continue_watching),
|
||||
items =
|
||||
latestNextUpService
|
||||
.buildCombined(
|
||||
|
|
@ -676,8 +693,8 @@ class HomeSettingsService
|
|||
.firstOrNull { it.itemId == row.parentId }
|
||||
|
||||
val title =
|
||||
library?.name?.let { context.getString(R.string.genres_in, it) }
|
||||
?: context.getString(R.string.genres)
|
||||
library?.name?.let { ResArgStringProvider(R.string.genres_in, it) }
|
||||
?: ResStringProvider(R.string.genres)
|
||||
val genres =
|
||||
items.map {
|
||||
BaseItem(
|
||||
|
|
@ -723,8 +740,8 @@ class HomeSettingsService
|
|||
libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
val title =
|
||||
library?.name?.let { context.getString(R.string.studios_in, it) }
|
||||
?: context.getString(R.string.studios)
|
||||
library?.name?.let { ResArgStringProvider(R.string.studios_in, it) }
|
||||
?: ResStringProvider(R.string.studios)
|
||||
val studios =
|
||||
items.map {
|
||||
val imageUrl =
|
||||
|
|
@ -762,7 +779,7 @@ class HomeSettingsService
|
|||
val library =
|
||||
libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
val title = getRecentlyAddedTitle(context, library)
|
||||
val title = getRecentlyAddedTitle(library)
|
||||
val request =
|
||||
GetLatestMediaRequest(
|
||||
fields = SlimItemFields,
|
||||
|
|
@ -795,8 +812,8 @@ class HomeSettingsService
|
|||
?.name
|
||||
val title =
|
||||
name?.let {
|
||||
context.getString(R.string.recently_released_in, it)
|
||||
} ?: context.getString(R.string.recently_released)
|
||||
ResArgStringProvider(R.string.recently_released_in, it)
|
||||
} ?: ResStringProvider(R.string.recently_released)
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
parentId = row.parentId,
|
||||
|
|
@ -853,7 +870,8 @@ class HomeSettingsService
|
|||
fields = DefaultItemFields,
|
||||
)
|
||||
|
||||
val name = getItemName(row.parentId)
|
||||
val title =
|
||||
getItemName(null, row.parentId, ResStringProvider(R.string.collection))
|
||||
if (usePaging) {
|
||||
ApiRequestPager(
|
||||
api,
|
||||
|
|
@ -869,7 +887,7 @@ class HomeSettingsService
|
|||
.map { BaseItem(it, row.viewOptions.useSeries) }
|
||||
}.let {
|
||||
Success(
|
||||
name ?: context.getString(R.string.collection),
|
||||
title,
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -906,7 +924,7 @@ class HomeSettingsService
|
|||
.map { BaseItem(it, row.viewOptions.useSeries) }
|
||||
}.let {
|
||||
Success(
|
||||
row.name,
|
||||
StringStringProvider(row.name),
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -916,9 +934,9 @@ class HomeSettingsService
|
|||
|
||||
is HomeRowConfig.Favorite -> {
|
||||
val title =
|
||||
context.getString(
|
||||
ResProviderStringProvider(
|
||||
R.string.favorite_items,
|
||||
context.getString(favoriteOptions[row.kind]!!),
|
||||
ResStringProvider(favoriteOptions[row.kind]!!),
|
||||
)
|
||||
if (row.kind == BaseItemKind.PERSON) {
|
||||
val request =
|
||||
|
|
@ -1001,7 +1019,7 @@ class HomeSettingsService
|
|||
.map { BaseItem(it, row.viewOptions.useSeries) }
|
||||
}.let {
|
||||
Success(
|
||||
context.getString(R.string.active_recordings),
|
||||
ResStringProvider(R.string.active_recordings),
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -1027,7 +1045,7 @@ class HomeSettingsService
|
|||
.map { BaseItem(it, row.viewOptions.useSeries) }
|
||||
.let {
|
||||
Success(
|
||||
context.getString(R.string.live_tv),
|
||||
ResStringProvider(R.string.live_tv),
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -1057,7 +1075,7 @@ class HomeSettingsService
|
|||
.toBaseItems(api, row.viewOptions.useSeries)
|
||||
}.let {
|
||||
Success(
|
||||
context.getString(R.string.channels),
|
||||
ResStringProvider(R.string.channels),
|
||||
it,
|
||||
row.viewOptions,
|
||||
rowType = row,
|
||||
|
|
@ -1070,7 +1088,7 @@ class HomeSettingsService
|
|||
api.userLibraryApi
|
||||
.getItem(itemId = row.parentId)
|
||||
.content
|
||||
val title = context.getString(R.string.suggestions_for, library.name ?: "")
|
||||
val title = ResArgStringProvider(R.string.suggestions_for, library.name ?: "")
|
||||
val itemKind = SuggestionsWorker.getTypeForCollection(library.collectionType)
|
||||
val suggestions =
|
||||
itemKind?.let {
|
||||
|
|
@ -1111,7 +1129,7 @@ class HomeSettingsService
|
|||
*/
|
||||
data class HomeRowConfigDisplay(
|
||||
val id: Int,
|
||||
val title: String,
|
||||
val title: StringProvider,
|
||||
val config: HomeRowConfig,
|
||||
)
|
||||
|
||||
|
|
@ -1154,13 +1172,10 @@ class UnsupportedHomeSettingsVersionException(
|
|||
val maxSupportedVersion: Int = SUPPORTED_HOME_PAGE_SETTINGS_VERSION,
|
||||
) : Exception("Unsupported version $unsupportedVersion, max supported is $maxSupportedVersion")
|
||||
|
||||
fun getRecentlyAddedTitle(
|
||||
context: Context,
|
||||
library: Library?,
|
||||
): String =
|
||||
fun getRecentlyAddedTitle(library: Library?): StringProvider =
|
||||
if (library?.isRecordingFolder == true) {
|
||||
context.getString(R.string.recently_recorded)
|
||||
ResStringProvider(R.string.recently_recorded)
|
||||
} else {
|
||||
library?.name?.let { context.getString(R.string.recently_added_in, it) }
|
||||
?: context.getString(R.string.recently_added)
|
||||
library?.name?.let { ResArgStringProvider(R.string.recently_added_in, it) }
|
||||
?: ResStringProvider(R.string.recently_added)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.asFlow
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.github.damontecres.wholphin.BuildConfig
|
||||
import com.github.damontecres.wholphin.api.seerr.SeerrApiClient
|
||||
import com.github.damontecres.wholphin.api.seerr.model.AuthJellyfinPostRequest
|
||||
import com.github.damontecres.wholphin.api.seerr.model.AuthLocalPostRequest
|
||||
|
|
@ -12,28 +7,22 @@ import com.github.damontecres.wholphin.api.seerr.model.PublicSettings
|
|||
import com.github.damontecres.wholphin.api.seerr.model.User
|
||||
import com.github.damontecres.wholphin.data.SeerrServerDao
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||
import com.github.damontecres.wholphin.data.model.SeerrAuthMethod
|
||||
import com.github.damontecres.wholphin.data.model.SeerrPermission
|
||||
import com.github.damontecres.wholphin.data.model.SeerrServer
|
||||
import com.github.damontecres.wholphin.data.model.SeerrUser
|
||||
import com.github.damontecres.wholphin.data.model.hasPermission
|
||||
import com.github.damontecres.wholphin.services.hilt.StandardOkHttpClient
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.setup.seerr.createSeerrApiUrl
|
||||
import com.github.damontecres.wholphin.util.LoadingState
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import okhttp3.OkHttpClient
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
|
@ -140,7 +129,7 @@ class SeerrServerRepository
|
|||
serverRepository.currentUser.value?.let { jellyfinUser ->
|
||||
// TODO Need to update server early so that cookies are saved
|
||||
seerrApi.update(server.url, null)
|
||||
val userConfig = login(seerrApi.api, authMethod, username, password)
|
||||
val userConfig = seerrLogin(seerrApi.api, authMethod, username, password)
|
||||
|
||||
val user =
|
||||
SeerrUser(
|
||||
|
|
@ -174,7 +163,7 @@ class SeerrServerRepository
|
|||
.readTimeout(6.seconds)
|
||||
.build(),
|
||||
)
|
||||
login(api, authMethod, username, passwordOrApiKey)
|
||||
seerrLogin(api, authMethod, username, passwordOrApiKey)
|
||||
return LoadingState.Success
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +216,7 @@ data class CurrentSeerr(
|
|||
config.hasPermission(SeerrPermission.REQUEST_4K_TV)
|
||||
}
|
||||
|
||||
private suspend fun login(
|
||||
suspend fun seerrLogin(
|
||||
client: SeerrApiClient,
|
||||
authMethod: SeerrAuthMethod,
|
||||
username: String?,
|
||||
|
|
@ -259,84 +248,6 @@ private suspend fun login(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for JF user switching in the app to also switch the Seerr user/server
|
||||
*/
|
||||
@ActivityScoped
|
||||
class UserSwitchListener
|
||||
@Inject
|
||||
constructor(
|
||||
@param:ActivityContext private val context: Context,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val seerrServerRepository: SeerrServerRepository,
|
||||
private val seerrServerDao: SeerrServerDao,
|
||||
private val seerrApi: SeerrApi,
|
||||
private val homeSettingsService: HomeSettingsService,
|
||||
) {
|
||||
init {
|
||||
context as AppCompatActivity
|
||||
context.lifecycleScope.launchIO {
|
||||
serverRepository.currentUser.asFlow().collect { user ->
|
||||
Timber.d("New user")
|
||||
seerrServerRepository.clear()
|
||||
homeSettingsService.currentSettings.update { HomePageResolvedSettings.EMPTY }
|
||||
if (user != null) {
|
||||
switchUser(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun switchUser(user: JellyfinUser) =
|
||||
supervisorScope {
|
||||
// Check for home settings
|
||||
launchIO {
|
||||
homeSettingsService.loadCurrentSettings(user.id)
|
||||
}
|
||||
if (BuildConfig.DISCOVER_ENABLED) {
|
||||
// Check for seerr server
|
||||
launchIO {
|
||||
seerrServerDao
|
||||
.getUsersByJellyfinUser(user.rowId)
|
||||
.lastOrNull()
|
||||
?.let { seerrUser ->
|
||||
val server =
|
||||
seerrServerDao.getServer(seerrUser.serverId)?.server
|
||||
if (server != null) {
|
||||
Timber.i("Found a seerr user & server")
|
||||
try {
|
||||
seerrApi.update(server.url, seerrUser.credential)
|
||||
val userConfig =
|
||||
if (seerrUser.authMethod != SeerrAuthMethod.API_KEY) {
|
||||
login(
|
||||
seerrApi.api,
|
||||
seerrUser.authMethod,
|
||||
seerrUser.username,
|
||||
seerrUser.password,
|
||||
)
|
||||
} else {
|
||||
seerrApi.api.usersApi.authMeGet()
|
||||
}
|
||||
seerrServerRepository.set(
|
||||
server,
|
||||
seerrUser,
|
||||
userConfig,
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
Timber.w(
|
||||
ex,
|
||||
"Error logging into %s",
|
||||
server.url,
|
||||
)
|
||||
seerrServerRepository.error(server, seerrUser, ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun CurrentSeerr?.imageUrlBuilder(
|
||||
imageType: ImageType,
|
||||
path: String?,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
package com.github.damontecres.wholphin.services
|
||||
|
||||
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
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||
import com.github.damontecres.wholphin.data.model.SeerrAuthMethod
|
||||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import dagger.hilt.android.qualifiers.ActivityContext
|
||||
import dagger.hilt.android.scopes.ActivityScoped
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Listens for JF user switching in the app to also switch other settings like Seerr user/server
|
||||
*/
|
||||
@ActivityScoped
|
||||
class UserSwitchListener
|
||||
@Inject
|
||||
constructor(
|
||||
@param:ActivityContext private val context: Context,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val seerrServerRepository: SeerrServerRepository,
|
||||
private val seerrServerDao: SeerrServerDao,
|
||||
private val seerrApi: SeerrApi,
|
||||
private val homeSettingsService: HomeSettingsService,
|
||||
) {
|
||||
init {
|
||||
context as AppCompatActivity
|
||||
context.lifecycleScope.launchDefault {
|
||||
serverRepository.currentUser.asFlow().collect { user ->
|
||||
Timber.d("New user")
|
||||
seerrServerRepository.clear()
|
||||
homeSettingsService.currentSettings.update { HomePageResolvedSettings.EMPTY }
|
||||
if (user != null) {
|
||||
switchUser(user)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun switchUser(user: JellyfinUser) =
|
||||
supervisorScope {
|
||||
// Switch the locale to either the user's choice or the system default (empty)
|
||||
val localeList =
|
||||
user.uiLanguage?.let { LocaleListCompat.forLanguageTags(it) }
|
||||
?: LocaleListCompat.getEmptyLocaleList()
|
||||
Timber.i("Switching locale to %s", localeList)
|
||||
withContext(Dispatchers.Main) {
|
||||
AppCompatDelegate.setApplicationLocales(localeList)
|
||||
}
|
||||
|
||||
// Check for home settings
|
||||
launchIO {
|
||||
homeSettingsService.loadCurrentSettings(user.id)
|
||||
}
|
||||
if (BuildConfig.DISCOVER_ENABLED) {
|
||||
// Check for seerr server
|
||||
launchIO {
|
||||
seerrServerDao
|
||||
.getUsersByJellyfinUser(user.rowId)
|
||||
.lastOrNull()
|
||||
?.let { seerrUser ->
|
||||
val server =
|
||||
seerrServerDao.getServer(seerrUser.serverId)?.server
|
||||
if (server != null) {
|
||||
Timber.i("Found a seerr user & server")
|
||||
try {
|
||||
seerrApi.update(server.url, seerrUser.credential)
|
||||
val userConfig =
|
||||
if (seerrUser.authMethod != SeerrAuthMethod.API_KEY) {
|
||||
seerrLogin(
|
||||
seerrApi.api,
|
||||
seerrUser.authMethod,
|
||||
seerrUser.username,
|
||||
seerrUser.password,
|
||||
)
|
||||
} else {
|
||||
seerrApi.api.usersApi.authMeGet()
|
||||
}
|
||||
seerrServerRepository.set(
|
||||
server,
|
||||
seerrUser,
|
||||
userConfig,
|
||||
)
|
||||
} catch (ex: Exception) {
|
||||
Timber.w(
|
||||
ex,
|
||||
"Error logging into %s",
|
||||
server.url,
|
||||
)
|
||||
seerrServerRepository.error(server, seerrUser, ex)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue