diff --git a/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt b/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt index c1f9da80..34219c58 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/hilt/AppModule.kt @@ -5,6 +5,7 @@ import android.content.Context import android.provider.Settings import androidx.datastore.core.DataStore import com.github.damontecres.wholphin.BuildConfig +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.preferences.AppPreferences import com.github.damontecres.wholphin.preferences.UserPreferences @@ -47,9 +48,11 @@ annotation class IoCoroutineScope object AppModule { @Provides @Singleton - fun clientInfo(): ClientInfo = + fun clientInfo( + @ApplicationContext context: Context, + ): ClientInfo = ClientInfo( - name = "Wholphin", + name = context.getString(R.string.app_name), version = BuildConfig.VERSION_NAME, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt index 579c4e75..11554506 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/preferences/AppPreference.kt @@ -70,7 +70,11 @@ sealed interface AppPreference { }, summarizer = { value -> if (value != null) { - "$value seconds" + WholphinApplication.instance.resources.getQuantityString( + R.plurals.seconds, + value.toInt(), + value.toInt(), + ) } else { null } @@ -95,7 +99,11 @@ sealed interface AppPreference { }, summarizer = { value -> if (value != null) { - "$value seconds" + WholphinApplication.instance.resources.getQuantityString( + R.plurals.seconds, + value.toInt(), + value.toInt(), + ) } else { null } @@ -137,7 +145,14 @@ sealed interface AppPreference { setter = { prefs, value -> prefs.updatePlaybackPreferences { controllerTimeoutMs = value } }, - summarizer = { value -> value?.let { "${value / 1000.0} seconds" } }, + summarizer = { value -> + value?.let { + WholphinApplication.instance.getString( + R.string.decimal_seconds, + value / 1000.0, + ) + } + }, ) val SeekBarSteps = @@ -243,7 +258,7 @@ sealed interface AppPreference { }, summarizer = { value -> if (value == 0L) { - "Disabled" + WholphinApplication.instance.getString(R.string.disabled) } else { "${value}s" } @@ -262,10 +277,16 @@ sealed interface AppPreference { prefs.updatePlaybackPreferences { autoPlayNextDelaySeconds = value } }, summarizer = { value -> - if (value == 0L) { - "Immediate" + if (value == null) { + "" + } else if (value == 0L) { + WholphinApplication.instance.getString(R.string.immediate) } else { - "$value seconds" + WholphinApplication.instance.resources.getQuantityString( + R.plurals.seconds, + value.toInt(), + value.toInt(), + ) } }, ) @@ -284,10 +305,16 @@ sealed interface AppPreference { } }, summarizer = { value -> - if (value == 0L) { - "Disabled" + if (value == null) { + "" + } else if (value == 0L) { + WholphinApplication.instance.getString(R.string.disabled) } else { - "$value hours" + WholphinApplication.instance.resources.getQuantityString( + R.plurals.hours, + value.toInt(), + value.toInt(), + ) } }, ) @@ -485,7 +512,7 @@ sealed interface AppPreference { val SkipCommercials = AppChoicePreference( - title = R.string.skip_comercials_behavior, + title = R.string.skip_commercials_behavior, defaultValue = SkipSegmentBehavior.ASK_TO_SKIP, getter = { it.playbackPreferences.skipCommercials }, setter = { prefs, value -> diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterRow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterRow.kt index 2468f7b1..1c206990 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterRow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/ChapterRow.kt @@ -11,9 +11,11 @@ import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.focus.focusRestorer +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.Chapter @Composable @@ -28,7 +30,7 @@ fun ChapterRow( modifier = modifier, ) { Text( - text = "Chapters", + text = stringResource(R.string.chapters), style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onBackground, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonRow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonRow.kt index 90f0f2ef..63730ca7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonRow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/cards/PersonRow.kt @@ -15,9 +15,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRestorer +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.Person import com.github.damontecres.wholphin.ui.ifElse @@ -34,7 +36,7 @@ fun PersonRow( modifier = modifier, ) { Text( - text = "People", + text = stringResource(R.string.people), style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onBackground, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt index 38f34a07..31bd0245 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt @@ -1,5 +1,6 @@ package com.github.damontecres.wholphin.ui.components +import android.content.Context import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut @@ -19,6 +20,7 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -26,6 +28,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.LibraryDisplayInfoDao import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem @@ -46,6 +49,7 @@ import com.github.damontecres.wholphin.util.GetItemsRequestHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -65,6 +69,7 @@ class CollectionFolderViewModel @Inject constructor( api: ApiClient, + @param:ApplicationContext private val context: Context, private val serverRepository: ServerRepository, private val libraryDisplayInfoDao: LibraryDisplayInfoDao, ) : ItemViewModel(api) { @@ -83,7 +88,7 @@ class CollectionFolderViewModel viewModelScope.launch( LoadingExceptionHandler( loading, - "Error loading collection $itemId", + context.getString(R.string.error_loading_collection, itemId), ) + Dispatchers.IO, ) { if (itemId != null) { @@ -292,7 +297,7 @@ fun CollectionFolderGridContent( showTitle: Boolean = true, positionCallback: ((columns: Int, position: Int) -> Unit)? = null, ) { - val title = item?.name ?: item?.data?.collectionType?.name ?: "Collection" + val title = item?.name ?: item?.data?.collectionType?.name ?: stringResource(R.string.collection) val sortOptions = when (item?.data?.collectionType) { CollectionType.MOVIES -> MovieSortOptions diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/Dialogs.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/Dialogs.kt index 2c639bff..b2363059 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/Dialogs.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/Dialogs.kt @@ -1,5 +1,6 @@ package com.github.damontecres.wholphin.ui.components +import android.content.Context import android.view.KeyEvent import androidx.annotation.StringRes import androidx.compose.foundation.background @@ -462,12 +463,13 @@ fun ConfirmDialogContent( } fun chooseVersionParams( + context: Context, sources: List, onClick: (Int) -> Unit, ): DialogParams = DialogParams( fromLongClick = false, - title = "Play Version", + title = context.getString(R.string.choose_stream, context.getString(R.string.version)), items = sources.filter { it.id.isNotNullOrBlank() }.mapIndexed { index, source -> val videoStream = @@ -485,21 +487,33 @@ fun chooseVersionParams( }, ) +@StringRes +fun resourceFor(type: MediaStreamType): Int = + when (type) { + MediaStreamType.AUDIO -> R.string.audio + MediaStreamType.VIDEO -> R.string.video + MediaStreamType.SUBTITLE -> R.string.subtitles + MediaStreamType.EMBEDDED_IMAGE -> 0 + MediaStreamType.DATA -> 0 + MediaStreamType.LYRIC -> 0 + } + fun chooseStream( + context: Context, streams: List, type: MediaStreamType, onClick: (Int) -> Unit, ): DialogParams = DialogParams( fromLongClick = false, - title = "Choose ${type.serialName}", // TODO + title = context.getString(R.string.choose_stream, context.getString(resourceFor(type))), items = buildList { if (type == MediaStreamType.SUBTITLE) { add( DialogItem( headlineContent = { - Text(text = "None") + Text(text = stringResource(R.string.none)) }, supportingContent = { }, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/ErrorMessage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/ErrorMessage.kt index 14e0fe31..f833e228 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/ErrorMessage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/ErrorMessage.kt @@ -6,12 +6,14 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.ViewModel import androidx.tv.material3.Button import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.detail.DebugViewModel.Companion.sendAppLogs import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel @@ -50,7 +52,7 @@ fun ErrorMessage( modifier = modifier.padding(16.dp), ) { Text( - text = "An error occurred! Press the button to send logs to your server.", + text = stringResource(R.string.compose_error_message_title), color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.titleMedium, ) @@ -60,7 +62,7 @@ fun ErrorMessage( }, ) { Text( - text = "Send Logs", + text = stringResource(R.string.send_app_logs), ) } message?.let { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/LoadingRow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/LoadingRow.kt index da4fa30d..593f2db3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/LoadingRow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/LoadingRow.kt @@ -8,10 +8,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.Cards import com.github.damontecres.wholphin.ui.cards.ItemRow @@ -70,7 +72,7 @@ fun LoadingRow( -> LoadingRowPlaceholder( title = title, - message = "Loading...", + message = stringResource(R.string.loading), modifier = modifier, ) @@ -88,7 +90,7 @@ fun LoadingRow( } else if (showIfEmpty) { LoadingRowPlaceholder( title = title, - message = "No results", + message = stringResource(R.string.no_results), modifier = modifier, ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt index ec193b2f..3c7e438a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedMovie.kt @@ -8,6 +8,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences @@ -16,7 +17,6 @@ import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.main.HomePageContent import com.github.damontecres.wholphin.ui.main.HomeRow -import com.github.damontecres.wholphin.ui.main.HomeSection import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler @@ -113,11 +113,11 @@ class RecommendedMovieViewModel rows.forEach { it.init() } val homeRows = listOf( - HomeRow(HomeSection.RESUME, resumeItems, "Continue Watching"), - HomeRow(HomeSection.LATEST_MEDIA, recentlyReleasedItems, "Recently Released"), - HomeRow(HomeSection.LATEST_MEDIA, recentlyAddedItems, "Recently Added"), - HomeRow(HomeSection.NONE, suggestedItems, "Suggestions"), - HomeRow(HomeSection.NONE, unwatchedTopRatedItems, "Top Rated Unwatched"), + HomeRow(R.string.continue_watching, resumeItems), + HomeRow(R.string.recently_released, recentlyReleasedItems), + HomeRow(R.string.recently_added, recentlyAddedItems), + HomeRow(R.string.suggestions, suggestedItems), + HomeRow(R.string.top_unwatched, unwatchedTopRatedItems), ).filter { it.items.isNotEmpty() } withContext(Dispatchers.Main) { this@RecommendedMovieViewModel.rows.value = homeRows diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt index 589403f2..d6f733a9 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/RecommendedTvShow.kt @@ -8,6 +8,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences @@ -16,7 +17,6 @@ import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.data.RowColumn import com.github.damontecres.wholphin.ui.main.HomePageContent import com.github.damontecres.wholphin.ui.main.HomeRow -import com.github.damontecres.wholphin.ui.main.HomeSection import com.github.damontecres.wholphin.util.ApiRequestPager import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.GetItemsRequestHandler @@ -124,12 +124,12 @@ class RecommendedTvShowViewModel rows.forEach { it.init() } val homeRows = listOf( - HomeRow(HomeSection.RESUME, resumeItems, "Continue Watching"), - HomeRow(HomeSection.NEXT_UP, nextUpItems, "Next Up"), - HomeRow(HomeSection.LATEST_MEDIA, recentlyReleasedItems, "Recently Released"), - HomeRow(HomeSection.LATEST_MEDIA, recentlyAddedItems, "Recently Added"), - HomeRow(HomeSection.NONE, suggestedItems, "Suggestions"), - HomeRow(HomeSection.NONE, unwatchedTopRatedItems, "Top Rated Unwatched"), + HomeRow(R.string.continue_watching, resumeItems), + HomeRow(R.string.next_up, nextUpItems), + HomeRow(R.string.recently_released, recentlyReleasedItems), + HomeRow(R.string.recently_added, recentlyAddedItems), + HomeRow(R.string.suggestions, suggestedItems), + HomeRow(R.string.top_unwatched, unwatchedTopRatedItems), ).filter { it.items.isNotEmpty() } withContext(Dispatchers.Main) { this@RecommendedTvShowViewModel.rows.value = homeRows diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt index 7be5878a..63b904b6 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/data/ItemDetailsDialogInfo.kt @@ -8,10 +8,12 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.tv.material3.MaterialTheme import androidx.tv.material3.ProvideTextStyle import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.components.ScrollableDialog import com.github.damontecres.wholphin.ui.isNotNullOrBlank import com.github.damontecres.wholphin.ui.letNotEmpty @@ -74,24 +76,30 @@ fun MediaSourceInfo( modifier = modifier, ) { Text( - text = "Name: ${source.name}", + text = stringResource(R.string.name) + ": ${source.name}", ) Text( text = "ID: ${source.id}", ) if (showFilePath) { Text( - text = "Path: ${source.path}", + text = stringResource(R.string.path) + ": ${source.path}", ) } source.size?.let { size -> Text( - text = "Size: ${formatBytes(size)}", + text = stringResource(R.string.file_size) + ": ${formatBytes(size)}", ) } source.bitrate?.let { bitrate -> Text( - text = "Bitrate: ${formatBytes(bitrate, byteRateSuffixes)}", + text = + stringResource(R.string.bitrate) + ": ${ + formatBytes( + bitrate, + byteRateSuffixes, + ) + }", ) } source.mediaStreams?.letNotEmpty { streams -> @@ -109,7 +117,7 @@ fun MediaSourceInfo( stream.profile?.let(::add) } Text( - text = "Video: " + data.joinToString(" - "), + text = stringResource(R.string.video) + ": " + data.joinToString(" - "), ) } @@ -121,10 +129,10 @@ fun MediaSourceInfo( stream.channelLayout?.let(::add) stream.bitRate?.let { add(formatBytes(it, byteRateSuffixes)) } if (stream.audioSpatialFormat != AudioSpatialFormat.NONE) add(stream.audioSpatialFormat.serialName) - if (stream.isDefault) add("Default") + if (stream.isDefault) add(stringResource(R.string.default_track)) } Text( - text = "Audio ${index + 1}: " + data.joinToString(" - "), + text = stringResource(R.string.audio) + " ${index + 1}: " + data.joinToString(" - "), ) } @@ -133,12 +141,14 @@ fun MediaSourceInfo( buildList { stream.language?.let { add(languageName(it)) } stream.codec?.let(::add) - if (stream.isDefault) add("Default") - if (stream.isForced) add("Forced") - if (stream.isExternal) add("External") + if (stream.isDefault) add(stringResource(R.string.default_track)) + if (stream.isForced) add(stringResource(R.string.forced_track)) + if (stream.isExternal) add(stringResource(R.string.external_track)) } Text( - text = "Subtitle ${index + 1}: " + data.joinToString(" - "), + text = + stringResource(R.string.subtitle) + " ${index + 1}: " + + data.joinToString(" - "), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt index bea5d281..7fa770ba 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt @@ -42,6 +42,7 @@ import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.key.type +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.tv.material3.Button @@ -298,7 +299,7 @@ fun CardGrid( // focusedIndex = -1 Box(modifier = Modifier.fillMaxSize()) { Text( - text = "No results", + text = stringResource(R.string.no_results), color = MaterialTheme.colorScheme.onBackground, modifier = Modifier.align(Alignment.Center), ) @@ -326,6 +327,8 @@ fun CardGrid( } } } + val context = LocalContext.current + val letters = context.getString(R.string.jump_letters) // Letters val currentLetter = remember(focusedIndex) { @@ -344,10 +347,11 @@ fun CardGrid( null } } - ?: LETTERS[0] + ?: letters[0] } if (showLetterButtons && pager.isNotEmpty()) { AlphabetButtons( + letters = letters, currentLetter = currentLetter, modifier = Modifier.align(Alignment.CenterVertically), letterClicked = { letter -> @@ -404,18 +408,18 @@ fun JumpButton( } } -private const val LETTERS = "#ABCDEFGHIJKLMNOPQRSTUVWXYZ" - @Composable fun AlphabetButtons( + letters: String, currentLetter: Char, letterClicked: (Char) -> Unit, modifier: Modifier = Modifier, ) { + val context = LocalContext.current val scope = rememberCoroutineScope() val listState = rememberLazyListState() - val index = LETTERS.indexOf(currentLetter) + val index = letters.indexOf(currentLetter) LaunchedEffect(currentLetter) { scope.launch(ExceptionHandler()) { val firstVisibleItemIndex = listState.firstVisibleItemIndex @@ -428,19 +432,19 @@ fun AlphabetButtons( } } } - val focusRequesters = remember { List(LETTERS.length) { FocusRequester() } } + val focusRequesters = remember { List(letters.length) { FocusRequester() } } LazyColumn( state = listState, modifier = modifier.focusProperties { onEnter = { - focusRequesters[index.coerceIn(0, LETTERS.length - 1)].tryRequestFocus() + focusRequesters[index.coerceIn(0, letters.length - 1)].tryRequestFocus() } }, ) { items( - LETTERS.length, - key = { LETTERS[it] }, + letters.length, + key = { letters[it] }, ) { index -> val interactionSource = remember { MutableInteractionSource() } val focused by interactionSource.collectIsFocusedAsState() @@ -452,17 +456,17 @@ fun AlphabetButtons( contentPadding = PaddingValues(2.dp), interactionSource = interactionSource, onClick = { - letterClicked.invoke(LETTERS[index]) + letterClicked.invoke(letters[index]) }, ) { val color = - if (!focused && LETTERS[index] == currentLetter) { + if (!focused && letters[index] == currentLetter) { MaterialTheme.colorScheme.tertiary } else { LocalContentColor.current } Text( - text = LETTERS[index].toString(), + text = letters[index].toString(), color = color, ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt index 368df5ee..1716a926 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderLiveTv.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRestorer import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.MutableLiveData @@ -33,6 +34,7 @@ import androidx.tv.material3.TabDefaults import androidx.tv.material3.TabRow import androidx.tv.material3.TabRowDefaults import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter @@ -96,8 +98,8 @@ fun CollectionFolderLiveTv( val tabs = listOf( - TabId("Guide", UUID.randomUUID()), - TabId("DVR Schedule", UUID.randomUUID()), + TabId(stringResource(R.string.tv_guide), UUID.randomUUID()), + TabId(stringResource(R.string.tv_dvr_schedule), UUID.randomUUID()), ) + folders var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt index 426d8b3b..c9cc3e59 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMovie.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRestorer import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.tv.material3.MaterialTheme @@ -29,6 +30,7 @@ import androidx.tv.material3.TabDefaults import androidx.tv.material3.TabRow import androidx.tv.material3.TabRowDefaults import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.preferences.UserPreferences @@ -52,7 +54,13 @@ fun CollectionFolderMovie( val rememberedTabIndex = remember { preferencesViewModel.getRememberedTab(preferences, destination.itemId, 0) } - val tabs = listOf("Recommended", "Library", "Collections", "Genres") + val tabs = + listOf( + stringResource(R.string.recommended), + stringResource(R.string.library), + stringResource(R.string.collections), + stringResource(R.string.genres), + ) var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt index 0e5728ad..bb659e96 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderTv.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRestorer import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.tv.material3.MaterialTheme @@ -29,6 +30,7 @@ import androidx.tv.material3.TabDefaults import androidx.tv.material3.TabRow import androidx.tv.material3.TabRowDefaults import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.preferences.UserPreferences @@ -52,7 +54,12 @@ fun CollectionFolderTv( val rememberedTabIndex = remember { preferencesViewModel.getRememberedTab(preferences, destination.itemId, 0) } - val tabs = listOf("Recommended", "Library", "Genres") + val tabs = + listOf( + stringResource(R.string.recommended), + stringResource(R.string.library), + stringResource(R.string.genres), + ) var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } val focusRequester = remember { FocusRequester() } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt index 2f7c0ede..c55b2a4e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/DetailUtils.kt @@ -1,5 +1,6 @@ package com.github.damontecres.wholphin.ui.detail +import android.content.Context import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.filled.ArrowForward @@ -36,6 +37,7 @@ import kotlin.time.Duration.Companion.seconds * @param onChooseTracks callback to pick a track for the given type of the item */ fun buildMoreDialogItems( + context: Context, item: BaseItem, series: BaseItem?, sourceId: UUID?, @@ -50,7 +52,7 @@ fun buildMoreDialogItems( buildList { add( DialogItem( - "Play", + context.getString(R.string.play), Icons.Default.PlayArrow, iconColor = Color.Green.copy(alpha = .8f), ) { @@ -83,7 +85,7 @@ fun buildMoreDialogItems( series?.let { add( DialogItem( - "Go to series", + context.getString(R.string.go_to_series), Icons.AutoMirrored.Filled.ArrowForward, ) { navigateTo( @@ -100,7 +102,10 @@ fun buildMoreDialogItems( if (sources.size > 1) { add( DialogItem( - "Choose Version", + context.getString( + R.string.choose_stream, + context.getString(R.string.version), + ), R.string.fa_file_video, ) { onChooseVersion.invoke() @@ -116,7 +121,10 @@ fun buildMoreDialogItems( if (audioCount > 1) { add( DialogItem( - "Choose audio", + context.getString( + R.string.choose_stream, + context.getString(R.string.audio), + ), R.string.fa_volume_low, ) { onChooseTracks.invoke(MediaStreamType.AUDIO) @@ -126,7 +134,10 @@ fun buildMoreDialogItems( if (subtitleCount > 0) { add( DialogItem( - "Choose subtitles", + context.getString( + R.string.choose_stream, + context.getString(R.string.subtitles), + ), R.string.fa_closed_captioning, ) { onChooseTracks.invoke(MediaStreamType.SUBTITLE) @@ -138,6 +149,7 @@ fun buildMoreDialogItems( } fun buildMoreDialogItemsForHome( + context: Context, item: BaseItem, seriesId: UUID?, playbackPosition: Duration, @@ -151,7 +163,7 @@ fun buildMoreDialogItemsForHome( val itemId = item.id add( DialogItem( - "Go To", + context.getString(R.string.go_to), Icons.Default.ArrowForward, ) { navigateTo(item.destination()) @@ -161,7 +173,7 @@ fun buildMoreDialogItemsForHome( if (playbackPosition >= 1.seconds) { add( DialogItem( - "Resume", + context.getString(R.string.resume), Icons.Default.PlayArrow, iconColor = Color.Green.copy(alpha = .8f), ) { @@ -175,7 +187,7 @@ fun buildMoreDialogItemsForHome( ) add( DialogItem( - "Restart", + context.getString(R.string.restart), Icons.Default.Refresh, // iconColor = Color.Green.copy(alpha = .8f), ) { @@ -190,7 +202,7 @@ fun buildMoreDialogItemsForHome( } else { add( DialogItem( - "Play", + context.getString(R.string.play), Icons.Default.PlayArrow, iconColor = Color.Green.copy(alpha = .8f), ) { @@ -224,7 +236,7 @@ fun buildMoreDialogItemsForHome( seriesId?.let { add( DialogItem( - "Go to series", + context.getString(R.string.go_to_series), Icons.AutoMirrored.Filled.ArrowForward, ) { navigateTo( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt index 92fa57c9..7365452e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt @@ -21,6 +21,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRestorer import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.tv.material3.MaterialTheme @@ -29,6 +30,7 @@ import androidx.tv.material3.TabDefaults import androidx.tv.material3.TabRow import androidx.tv.material3.TabRowDefaults import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.preferences.UserPreferences @@ -55,7 +57,12 @@ fun FavoritesPage( ) } - val tabs = listOf("Movies", "TV Shows", "Episodes") + val tabs = + listOf( + stringResource(R.string.movies), + stringResource(R.string.tv_shows), + stringResource(R.string.episodes), + ) val tabFocusRequesters = remember { List(tabs.size) { FocusRequester() } } var focusTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } var selectedTabIndex by rememberSaveable { mutableIntStateOf(rememberedTabIndex) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt index 86dfbdbd..aaab1b9c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PersonPage.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shadow +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -33,6 +34,7 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation import coil3.compose.AsyncImage +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect @@ -258,7 +260,7 @@ fun PersonPageContent( } item { LoadingRow( - title = rowTitle("Movies", movies), + title = rowTitle(stringResource(R.string.movies), movies), state = movies, rowIndex = MOVIE_ROW, position = position, @@ -271,7 +273,7 @@ fun PersonPageContent( } item { LoadingRow( - title = rowTitle("Series", series), + title = rowTitle(stringResource(R.string.tv_shows), series), state = series, rowIndex = SERIES_ROW, position = position, @@ -284,7 +286,7 @@ fun PersonPageContent( } item { LoadingRow( - title = rowTitle("Episodes", episodes), + title = rowTitle(stringResource(R.string.episodes), episodes), state = episodes, rowIndex = EPISODE_ROW, position = position, @@ -365,9 +367,14 @@ fun PersonHeader( val age = if (deathdate == null) birthdate.until(LocalDate.now())?.years else null val text = if (age != null) { - "Born: ${formatDate(it)} ($age years old)" + stringResource(R.string.born) + ": ${formatDate(it)} (${ + stringResource( + R.string.years_old, + age, + ) + })" } else { - "Born: ${formatDate(it)}" + stringResource(R.string.born) + ": ${formatDate(it)}" } Text( text = text, @@ -379,7 +386,7 @@ fun PersonHeader( } birthPlace?.let { Text( - text = "Birthplace: $it", + text = stringResource(R.string.birthplace) + ": $it", color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleSmall, maxLines = 1, @@ -390,9 +397,14 @@ fun PersonHeader( val age = birthdate?.until(it)?.years val text = if (age != null) { - "Died: ${formatDate(it)} ($age years old)" + stringResource(R.string.died) + ": ${formatDate(it)} (${ + stringResource( + R.string.years_old, + age, + ) + })" } else { - "Died: ${formatDate(it)}" + stringResource(R.string.died) + ": ${formatDate(it)}" } Text( text = text, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt index 49538cca..5ab05521 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/PlaylistDetails.kt @@ -42,6 +42,8 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -123,6 +125,7 @@ fun PlaylistDetails( modifier: Modifier = Modifier, viewModel: PlaylistViewModel = hiltViewModel(), ) { + val context = LocalContext.current LaunchedEffect(Unit) { viewModel.init(destination.itemId) } @@ -170,13 +173,13 @@ fun PlaylistDetails( items = listOf( DialogItem( - "Go to", + context.getString(R.string.go_to), Icons.Default.ArrowForward, ) { viewModel.navigationManager.navigateTo(item.destination()) }, DialogItem( - "Play from here", + context.getString(R.string.play_from_here), Icons.Default.PlayArrow, ) { viewModel.navigationManager.navigateTo( @@ -282,7 +285,7 @@ fun PlaylistDetailsContent( .padding(horizontal = 16.dp), ) { Text( - text = playlist.name ?: "Playlist", + text = playlist.name ?: stringResource(R.string.playlist), color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.displayMedium, textAlign = TextAlign.Center, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt index ad43c16f..45578f4b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/DvrSchedule.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel @@ -31,6 +32,7 @@ import androidx.tv.material3.ListItemDefaults import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.SlimItemFields import com.github.damontecres.wholphin.ui.components.ErrorMessage @@ -202,7 +204,7 @@ fun DvrScheduleContent( if (activeRecordings.isEmpty() && scheduledRecordings.isEmpty()) { item { Text( - text = "No scheduled recordings", + text = stringResource(R.string.no_scheduled_recordings), style = MaterialTheme.typography.headlineSmall, color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, @@ -213,7 +215,7 @@ fun DvrScheduleContent( if (activeRecordings.isNotEmpty()) { item { Text( - text = "Active", + text = stringResource(R.string.active_recordings), style = MaterialTheme.typography.headlineMedium, color = MaterialTheme.colorScheme.onSurface, modifier = Modifier.fillMaxWidth(), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt index e26dd5da..d69e6eab 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/LiveTvViewModel.kt @@ -1,9 +1,11 @@ package com.github.damontecres.wholphin.ui.detail.livetv +import android.content.Context import androidx.compose.ui.graphics.Color import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisode @@ -16,6 +18,7 @@ import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex @@ -45,6 +48,7 @@ const val MAX_HOURS = 48L class LiveTvViewModel @Inject constructor( + @param:ApplicationContext private val context: Context, val api: ApiClient, val navigationManager: NavigationManager, ) : ViewModel() { @@ -169,7 +173,7 @@ class LiveTvViewModel startHours = it.toFloat(), endHours = (it + 1).toFloat(), duration = 60.seconds, - name = "No data", + name = context.getString(R.string.no_data), subtitle = null, seasonEpisode = null, isRecording = false, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt index a5f6db0b..933b20ef 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/livetv/ProgramDialog.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog @@ -30,6 +31,7 @@ import androidx.tv.material3.Icon import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.ui.components.CircularProgress import com.github.damontecres.wholphin.ui.components.ErrorMessage @@ -165,7 +167,14 @@ fun ProgramDialog( ) } Text( - text = if (isSeriesRecording) "Cancel Series Recording" else "Record Series", + text = + if (isSeriesRecording) { + stringResource( + R.string.cancel_series_recording, + ) + } else { + stringResource(R.string.record_series) + }, ) } } @@ -193,7 +202,16 @@ fun ProgramDialog( ) } Text( - text = if (isRecording) "Cancel Recording" else "Record Program", + text = + if (isRecording) { + stringResource( + R.string.cancel_recording, + ) + } else { + stringResource( + R.string.record_program, + ) + }, ) } } @@ -210,11 +228,11 @@ fun ProgramDialog( ) { Icon( imageVector = Icons.Default.PlayArrow, - contentDescription = "Delete", + contentDescription = stringResource(R.string.delete), tint = Color.Green.copy(alpha = .75f), ) Text( - text = "Watch live", + text = stringResource(R.string.watch_live), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt index 11ce4ba4..5322a9e1 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetails.kt @@ -150,7 +150,7 @@ fun MovieDetails( overviewOnClick = { overviewDialog = ItemDetailsDialogInfo( - title = movie.name ?: "Unknown", + title = movie.name ?: context.getString(R.string.unknown), overview = movie.data.overview, files = movie.data.mediaSources.orEmpty(), ) @@ -162,6 +162,7 @@ fun MovieDetails( title = movie.name + " (${movie.data.productionYear ?: ""})", items = buildMoreDialogItems( + context = context, item = movie, watched = movie.data.userData?.played ?: false, favorite = movie.data.userData?.isFavorite ?: false, @@ -172,7 +173,10 @@ fun MovieDetails( onClickFavorite = viewModel::setFavorite, onChooseVersion = { chooseVersion = - chooseVersionParams(movie.data.mediaSources!!) { idx -> + chooseVersionParams( + context, + movie.data.mediaSources!!, + ) { idx -> val source = movie.data.mediaSources!![idx] viewModel.savePlayVersion( movie, @@ -188,6 +192,7 @@ fun MovieDetails( )?.let { source -> chooseVersion = chooseStream( + context = context, streams = source.mediaStreams.orEmpty(), type = type, onClick = { trackIndex -> diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt index 74faaccf..534fe23e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieDetailsHeader.kt @@ -147,7 +147,7 @@ fun MovieDetailsHeader( ?.joinToString(", ") { it.name!! } ?.let { Text( - text = "Directed by $it", + text = stringResource(R.string.directed_by, it), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.onSurface, ) @@ -183,7 +183,7 @@ fun MovieDetailsHeader( ?.let { if (it.isNotNullOrBlank()) { TitleValueText( - "Subtitles", + stringResource(R.string.subtitles), it, modifier = Modifier.widthIn(max = 200.dp), ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt index a27507ca..7284ba51 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt @@ -1,7 +1,9 @@ package com.github.damontecres.wholphin.ui.detail.movie +import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.ItemPlaybackRepository import com.github.damontecres.wholphin.data.ServerRepository @@ -25,6 +27,7 @@ import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.ThemeSongPlayer import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -43,6 +46,7 @@ class MovieViewModel @Inject constructor( api: ApiClient, + @param:ApplicationContext private val context: Context, private val navigationManager: NavigationManager, val serverRepository: ServerRepository, val itemPlaybackRepository: ItemPlaybackRepository, @@ -79,7 +83,7 @@ class MovieViewModel // TODO would be nice to clean up the trailer name // ?.replace(item.name ?: "", "") // ?.removePrefix(" - ") - ?: "Trailer" + ?: context.getString(R.string.trailer) RemoteTrailer(name, url) } }.orEmpty() diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt index 927a08e8..ec4c4b6f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/FocusedEpisodeFooter.kt @@ -82,7 +82,7 @@ fun FocusedEpisodeFooter( ?.let { if (it.isNotNullOrBlank()) { TitleValueText( - "Subtitles", + stringResource(R.string.subtitles), it, modifier = Modifier.widthIn(max = 160.dp), ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt index fb2f7a04..a71d9423 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesDetails.kt @@ -1,5 +1,6 @@ package com.github.damontecres.wholphin.ui.detail.series +import android.content.Context import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -33,6 +34,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -83,6 +85,7 @@ fun SeriesDetails( modifier: Modifier = Modifier, viewModel: SeriesViewModel = hiltViewModel(), ) { + val context = LocalContext.current LaunchedEffect(Unit) { viewModel.init(preferences, destination.itemId, destination.item, null) } @@ -136,7 +139,8 @@ fun SeriesDetails( onLongClickItem = { season -> seasonDialog = buildDialogForSeason( - season, + context = context, + s = season, onClickItem = { viewModel.navigateTo(it.destination()) }, markPlayed = { played -> viewModel.setSeasonWatched(season.id, played) @@ -146,7 +150,7 @@ fun SeriesDetails( overviewOnClick = { overviewDialog = ItemDetailsDialogInfo( - title = item.name ?: "Unknown", + title = item.name ?: context.getString(R.string.unknown), overview = item.data.overview, files = listOf(), ) @@ -161,7 +165,8 @@ fun SeriesDetails( if (showWatchConfirmation) { ConfirmDialog( title = item.name ?: "", - body = if (played) "Mark entire series as unplayed?" else "Mark entire series as played?", + body = + stringResource(if (played) R.string.mark_entire_series_as_unplayed else R.string.mark_entire_series_as_played), onCancel = { showWatchConfirmation = false }, @@ -343,7 +348,7 @@ fun SeriesDetailsContent( } item { ItemRow( - title = "Seasons", + title = stringResource(R.string.tv_seasons), items = seasons, onClickItem = { position = SEASONS_ROW @@ -444,7 +449,7 @@ fun SeriesDetailsHeader( modifier = modifier, ) { Text( - text = series.name ?: "Unknown", + text = series.name ?: stringResource(R.string.unknown), style = MaterialTheme.typography.displaySmall, modifier = Modifier.fillMaxWidth(), ) @@ -487,6 +492,7 @@ fun SeriesDetailsHeader( } fun buildDialogForSeason( + context: Context, s: BaseItem, onClickItem: (BaseItem) -> Unit, markPlayed: (Boolean) -> Unit, @@ -494,26 +500,26 @@ fun buildDialogForSeason( val items = buildList { add( - DialogItem("Go to", Icons.Default.PlayArrow) { + DialogItem(context.getString(R.string.go_to), Icons.Default.PlayArrow) { onClickItem.invoke(s) }, ) if (s.data.userData?.played == true) { add( - DialogItem("Mark as unplayed", R.string.fa_eye) { + DialogItem(context.getString(R.string.mark_unwatched), R.string.fa_eye) { markPlayed.invoke(false) }, ) } else { add( - DialogItem("Mark as played", R.string.fa_eye_slash) { + DialogItem(context.getString(R.string.mark_watched), R.string.fa_eye_slash) { markPlayed.invoke(true) }, ) } } return DialogParams( - title = s.name ?: "Season", + title = s.name ?: context.getString(R.string.tv_season), fromLongClick = true, items = items, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt index 75ea4555..ef0a0eaf 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverview.kt @@ -13,8 +13,10 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.platform.LocalContext import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.LifecycleStartEffect +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.chooseSource import com.github.damontecres.wholphin.preferences.UserPreferences @@ -72,6 +74,7 @@ fun SeriesOverview( viewModel: SeriesViewModel = hiltViewModel(), initialSeasonEpisode: SeasonEpisodeIds? = null, ) { + val context = LocalContext.current val firstItemFocusRequester = remember { FocusRequester() } val episodeRowFocusRequester = remember { FocusRequester() } @@ -178,7 +181,8 @@ fun SeriesOverview( title = series.name + " - " + ep.data.seasonEpisode, items = buildMoreDialogItems( - ep, + context = context, + item = ep, watched = ep.data.userData?.played ?: false, favorite = ep.data.userData?.isFavorite ?: false, series = series, @@ -208,7 +212,10 @@ fun SeriesOverview( }, onChooseVersion = { chooseVersion = - chooseVersionParams(ep.data.mediaSources!!) { idx -> + chooseVersionParams( + context, + ep.data.mediaSources!!, + ) { idx -> val source = ep.data.mediaSources!![idx] viewModel.savePlayVersion( ep, @@ -224,6 +231,7 @@ fun SeriesOverview( )?.let { source -> chooseVersion = chooseStream( + context = context, streams = source.mediaStreams.orEmpty(), type = type, onClick = { trackIndex -> @@ -313,7 +321,7 @@ fun SeriesOverview( episodeList?.getOrNull(position.episodeRowIndex)?.let { overviewDialog = ItemDetailsDialogInfo( - title = it.name ?: "Unknown", + title = it.name ?: context.getString(R.string.unknown), overview = it.data.overview, files = it.data.mediaSources.orEmpty(), ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt index db9402cf..0629ce6d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesOverviewContent.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.tv.material3.MaterialTheme @@ -42,6 +43,7 @@ import androidx.tv.material3.TabRow import androidx.tv.material3.TabRowDefaults import androidx.tv.material3.Text import coil3.compose.AsyncImage +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ChosenStreams import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.aspectRatioFloat @@ -178,7 +180,9 @@ fun SeriesOverviewContent( .focusRequester(focusRequesters[index]), ) { Text( - text = season.name ?: "Season ${season.data.indexNumber}", + text = + season.name + ?: (stringResource(R.string.tv_season) + " ${season.data.indexNumber}"), modifier = Modifier.padding(8.dp), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt index 2e116b5d..74f2849c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomePage.kt @@ -1,6 +1,7 @@ package com.github.damontecres.wholphin.ui.main import android.widget.Toast +import androidx.annotation.StringRes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -71,10 +72,20 @@ import org.jellyfin.sdk.model.extensions.ticks import kotlin.time.Duration data class HomeRow( - val section: HomeSection, + @param:StringRes val titleRes: Int?, + val title: String?, val items: List, - val title: String? = null, -) +) { + constructor( + @StringRes titleRes: Int, + items: List, + ) : this(titleRes, null, items) + + constructor( + title: String, + items: List, + ) : this(null, title, items) +} @Composable fun HomePage( @@ -121,6 +132,7 @@ fun HomePage( onLongClickItem = { val dialogItems = buildMoreDialogItemsForHome( + context = context, item = it, seriesId = it.data.seriesId, playbackPosition = @@ -242,7 +254,7 @@ fun HomePageContent( itemsIndexed(homeRows) { rowIndex, row -> if (row.items.isNotEmpty()) { ItemRow( - title = row.title ?: stringResource(row.section.nameRes), + title = row.title ?: row.titleRes?.let { stringResource(it) } ?: "", items = row.items, onClickItem = onClickItem, cardOnFocus = { isFocused, index -> diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeSection.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeSection.kt deleted file mode 100644 index 60978114..00000000 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeSection.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.damontecres.wholphin.ui.main - -import androidx.annotation.StringRes -import com.github.damontecres.wholphin.R - -/** - * All possible homesections, "synced" with jellyfin-web. - * - * https://github.com/jellyfin/jellyfin-web/blob/master/src/components/homesections/homesections.js - */ -enum class HomeSection( - val key: String, - @param:StringRes val nameRes: Int, -) { - LATEST_MEDIA("latestmedia", R.string.home_section_latest_media), - LIBRARY_TILES_SMALL("smalllibrarytiles", R.string.home_section_library), - LIBRARY_BUTTONS("librarybuttons", R.string.home_section_library_small), - RESUME("resume", R.string.home_section_resume), - RESUME_AUDIO("resumeaudio", R.string.home_section_resume_audio), - RESUME_BOOK("resumebook", R.string.home_section_resume_book), - ACTIVE_RECORDINGS("activerecordings", R.string.home_section_active_recordings), - NEXT_UP("nextup", R.string.home_section_next_up), - LIVE_TV("livetv", R.string.home_section_livetv), - NONE("none", R.string.home_section_none), - ; - - companion object { - fun fromKey(key: String): HomeSection = entries.firstOrNull { it.key == key } ?: NONE - } -} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt index 3db3f630..806ab9f0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/HomeViewModel.kt @@ -1,8 +1,10 @@ package com.github.damontecres.wholphin.ui.main +import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.NavDrawerItemRepository import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem @@ -15,6 +17,7 @@ import com.github.damontecres.wholphin.util.LoadingExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.supportItemKinds import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch @@ -40,6 +43,7 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( + @param:ApplicationContext private val context: Context, val api: ApiClient, val navigationManager: NavigationManager, val serverRepository: ServerRepository, @@ -86,7 +90,7 @@ class HomeViewModel if (resume.isNotEmpty()) { add( HomeRow( - section = HomeSection.RESUME, + titleRes = R.string.recently_added, items = resume, ), ) @@ -94,7 +98,7 @@ class HomeViewModel if (nextUp.isNotEmpty()) { add( HomeRow( - section = HomeSection.NEXT_UP, + titleRes = R.string.next_up, items = nextUp, ), ) @@ -191,10 +195,10 @@ class HomeViewModel .mapNotNull { view -> val title = if (view.collectionType == CollectionType.LIVETV) { - "Recently Recorded" + context.getString(R.string.recently_recorded) } else { - view.name?.let { "Recently Added in $it" } - } + view.name?.let { context.getString(R.string.recently_added_in, it) } + } ?: context.getString(R.string.recently_added) val viewId = if (view.collectionType == CollectionType.LIVETV) { api.liveTvApi @@ -222,9 +226,8 @@ class HomeViewModel .content .map { BaseItem.from(it, api, true) } HomeRow( - section = HomeSection.LATEST_MEDIA, - items = latest, title = title, + items = latest, ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt index 341f8c86..6d464fa7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/SearchPage.kt @@ -21,6 +21,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.MutableLiveData @@ -28,6 +30,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.Cards @@ -153,6 +156,7 @@ fun SearchPage( modifier: Modifier = Modifier, viewModel: SearchViewModel = hiltViewModel(), ) { + val context = LocalContext.current val movies by viewModel.movies.observeAsState(SearchResult.NoQuery) val collections by viewModel.collections.observeAsState(SearchResult.NoQuery) val series by viewModel.series.observeAsState(SearchResult.NoQuery) @@ -198,7 +202,7 @@ fun SearchPage( } } searchResultRow( - title = "Movies", + title = context.getString(R.string.movies), result = movies, rowIndex = MOVIE_ROW, position = position, @@ -208,7 +212,7 @@ fun SearchPage( modifier = Modifier.fillMaxWidth(), ) searchResultRow( - title = "Collections", + title = context.getString(R.string.collections), result = collections, rowIndex = COLLECTION_ROW, position = position, @@ -218,7 +222,7 @@ fun SearchPage( modifier = Modifier.fillMaxWidth(), ) searchResultRow( - title = "Series", + title = context.getString(R.string.tv_shows), result = series, rowIndex = SERIES_ROW, position = position, @@ -228,7 +232,7 @@ fun SearchPage( modifier = Modifier.fillMaxWidth(), ) searchResultRow( - title = "Episodes", + title = context.getString(R.string.episodes), result = episodes, rowIndex = EPISODE_ROW, position = position, @@ -308,7 +312,7 @@ fun LazyListScope.searchResultRow( SearchResult.Searching -> SearchResultPlaceholder( title = title, - message = "Searching...", + message = stringResource(R.string.searching), modifier = modifier, ) @@ -316,7 +320,7 @@ fun LazyListScope.searchResultRow( if (r.items.isEmpty()) { SearchResultPlaceholder( title = title, - message = "No results", + message = stringResource(R.string.no_results), modifier = modifier, ) } else { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt index bc18b47c..de463e99 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/nav/NavDrawer.kt @@ -324,7 +324,7 @@ fun NavDrawer( val focused by interactionSource.collectIsFocusedAsState() LaunchedEffect(focused) { if (focused) focusedIndex = -2 } IconNavItem( - text = "Search", + text = stringResource(R.string.search), icon = Icons.Default.Search, selected = selectedIndex == -2, interactionSource = interactionSource, @@ -345,7 +345,7 @@ fun NavDrawer( val focused by interactionSource.collectIsFocusedAsState() LaunchedEffect(focused) { if (focused) focusedIndex = -1 } IconNavItem( - text = "Home", + text = stringResource(R.string.home), icon = Icons.Default.Home, selected = selectedIndex == -1, interactionSource = interactionSource, @@ -413,7 +413,7 @@ fun NavDrawer( val focused by interactionSource.collectIsFocusedAsState() LaunchedEffect(focused) { if (focused) focusedIndex = Int.MIN_VALUE } IconNavItem( - text = "Settings", + text = stringResource(R.string.settings), icon = Icons.Default.Settings, selected = false, interactionSource = interactionSource, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt index ab19bbf6..4842f740 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/DownloadSubtitlesDialog.kt @@ -28,6 +28,8 @@ import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.res.pluralStringResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.tv.material3.Icon @@ -35,6 +37,7 @@ import androidx.tv.material3.ListItem import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.components.DialogItem import com.github.damontecres.wholphin.ui.components.DialogItemDivider @@ -58,7 +61,7 @@ fun DownloadSubtitlesContent( SubtitleSearch.Searching -> { Wrapper { Text( - text = "Searching...", + text = stringResource(R.string.searching), style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onSurface, ) @@ -68,7 +71,7 @@ fun DownloadSubtitlesContent( SubtitleSearch.Downloading -> { Wrapper { Text( - text = "Downloading...", + text = stringResource(R.string.downloading), style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onSurface, ) @@ -199,9 +202,15 @@ fun convertRemoteSubtitles( op.providerName?.let(::add) op.threeLetterIsoLanguageName?.let(::add) if (op.forced == true) { - add("Forced") + add(stringResource(R.string.forced_track)) } - add("${abbreviateNumber(op.downloadCount ?: 0)} downloads") + add( + pluralStringResource( + R.plurals.downloads, + op.downloadCount ?: 0, + abbreviateNumber(op.downloadCount ?: 0), + ), + ) } Text( text = strings.joinToString(" - "), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/Models.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/Models.kt index 71374ffc..2811ecc3 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/Models.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/Models.kt @@ -1,5 +1,8 @@ package com.github.damontecres.wholphin.ui.playback +import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.WholphinApplication + data class SubtitleStream( val index: Int, val language: String?, @@ -17,7 +20,8 @@ data class SubtitleStream( language, title, codec, - ).joinToString(" - ").ifBlank { "Unknown" } + ).joinToString(" - ") + .ifBlank { WholphinApplication.instance.getString(R.string.unknown) } } data class AudioStream( diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/NextUpEpisode.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/NextUpEpisode.kt index dc7036da..fc39255e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/NextUpEpisode.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/NextUpEpisode.kt @@ -25,6 +25,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp @@ -34,6 +35,7 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation import coil3.compose.AsyncImage +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.AppColors import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.theme.WholphinTheme @@ -68,7 +70,7 @@ fun NextUpEpisode( .padding(horizontal = 16.dp, vertical = 8.dp), ) { Text( - text = "Up Next...", + text = stringResource(R.string.next_up) + "...", style = MaterialTheme.typography.titleLarge, color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt index 60313567..5df69f4b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackControls.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalView import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.DialogProperties @@ -66,6 +67,7 @@ import com.github.damontecres.wholphin.ui.seekBack import com.github.damontecres.wholphin.ui.seekForward import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler +import com.github.damontecres.wholphin.util.stringRes import kotlinx.coroutines.delay import kotlinx.coroutines.isActive import kotlinx.coroutines.launch @@ -212,7 +214,7 @@ fun PlaybackControls( .padding(end = 32.dp), ) { Text( - text = "Skip ${segment.type.serialName}", + text = stringResource(R.string.skip) + " " + stringResource(segment.type.stringRes), ) } } @@ -332,7 +334,7 @@ fun LeftPlaybackButtons( val options = buildList { addAll(moreButtonOptions.options.keys) - add(if (showDebugInfo) "Hide debug info" else "Show debug info") + add(stringResource(if (showDebugInfo) R.string.hide_debug_info else R.string.show_debug_info)) } BottomDialog( choices = options, @@ -407,7 +409,12 @@ fun RightPlaybackButtons( val currentChoice = subtitleStreams.indexOfFirstOrNull { it.index == subtitleIndex } ?: subtitleStreams.size BottomDialog( - choices = options + listOf("None", "Search & Download"), + choices = + options + + listOf( + stringResource(R.string.none), + stringResource(R.string.search_and_download), + ), currentChoice = currentChoice, onDismissRequest = { onControllerInteraction.invoke() @@ -434,7 +441,12 @@ fun RightPlaybackButtons( ) } if (showOptionsDialog) { - val options = listOf("Audio Track", "Playback Speed", "Video Scale") + val options = + listOf( + stringResource(R.string.audio), + stringResource(R.string.playback_speed), + stringResource(R.string.video_scale), + ) BottomDialog( choices = options, currentChoice = null, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt index d8d338da..912cb214 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackOverlay.kt @@ -38,11 +38,13 @@ import androidx.compose.ui.input.key.type import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.media3.common.Player import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.data.model.Chapter import com.github.damontecres.wholphin.data.model.ItemPlayback @@ -199,7 +201,7 @@ fun PlaybackOverlay( }, ) { Text( - text = "Chapters", + text = stringResource(R.string.chapters), style = MaterialTheme.typography.titleLarge, ) LazyRow( @@ -240,7 +242,7 @@ fun PlaybackOverlay( } if (playlist.hasNext()) { Text( - text = "Queue", + text = stringResource(R.string.queue), style = MaterialTheme.typography.titleLarge, modifier = Modifier @@ -282,7 +284,7 @@ fun PlaybackOverlay( }, ) { Text( - text = "Queue", + text = stringResource(R.string.queue), style = MaterialTheme.typography.titleLarge, ) LazyRow( @@ -489,7 +491,7 @@ fun Controller( when (nextState) { OverlayViewState.CHAPTERS -> Text( - text = "Chapters", + text = stringResource(R.string.chapters), style = MaterialTheme.typography.titleLarge, modifier = Modifier @@ -501,7 +503,7 @@ fun Controller( OverlayViewState.QUEUE -> Text( - text = "Queue", + text = stringResource(R.string.queue), style = MaterialTheme.typography.titleLarge, modifier = Modifier diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt index ec9dc464..caf4f1ee 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackPage.kt @@ -39,6 +39,7 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.unit.dp import androidx.compose.ui.viewinterop.AndroidView @@ -60,6 +61,7 @@ import androidx.tv.material3.Button import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.model.ItemPlayback import com.github.damontecres.wholphin.data.model.Playlist import com.github.damontecres.wholphin.preferences.UserPreferences @@ -72,6 +74,7 @@ import com.github.damontecres.wholphin.ui.tryRequestFocus import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.LoadingState import com.github.damontecres.wholphin.util.seasonEpisode +import com.github.damontecres.wholphin.util.stringRes import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.jellyfin.sdk.model.api.DeviceProfile @@ -411,7 +414,7 @@ fun PlaybackPage( modifier = Modifier.focusRequester(focusRequester), ) { Text( - text = "Skip ${segment.type.serialName}", + text = stringResource(R.string.skip) + " " + stringResource(segment.type.stringRes), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt index d1b523a6..5ed4e19b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlaybackViewModel.kt @@ -19,6 +19,7 @@ import androidx.media3.common.Tracks import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.DefaultRenderersFactory import androidx.media3.exoplayer.ExoPlayer +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.data.ItemPlaybackDao import com.github.damontecres.wholphin.data.ItemPlaybackRepository import com.github.damontecres.wholphin.data.ServerRepository @@ -963,7 +964,7 @@ class PlaybackViewModel if (maxAttempts == 0) { showToast( context, - "Download is taking a long time, you may need to restart playback", + context.getString(R.string.subtitle_download_too_long), ) } else { // Find the new subtitle stream diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt index 62b21a97..2844207b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/preferences/PreferencesContent.kt @@ -97,9 +97,9 @@ fun PreferencesContent( } val screenTitle = when (preferenceScreenOption) { - PreferenceScreenOption.BASIC -> "Preferences" - PreferenceScreenOption.ADVANCED -> "Advanced Preferences" - PreferenceScreenOption.USER_INTERFACE -> "User Interface Preferences" + PreferenceScreenOption.BASIC -> R.string.settings + PreferenceScreenOption.ADVANCED -> R.string.advanced_settings + PreferenceScreenOption.USER_INTERFACE -> R.string.ui_interface } var visible by remember { mutableStateOf(false) } @@ -126,7 +126,7 @@ fun PreferencesContent( ) { stickyHeader { Text( - text = screenTitle, + text = stringResource(screenTitle), style = MaterialTheme.typography.headlineSmall, color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt index c9ff33aa..edb461f2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/InstallUpdatePage.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.input.key.KeyEventType import androidx.compose.ui.input.key.key import androidx.compose.ui.input.key.onKeyEvent import androidx.compose.ui.input.key.type +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.MutableLiveData @@ -42,6 +43,7 @@ import androidx.tv.material3.Button import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.PreviewTvSpec import com.github.damontecres.wholphin.ui.components.ErrorMessage @@ -221,7 +223,7 @@ fun InstallUpdatePageContent( ).padding(16.dp), ) { Text( - text = "Update available", + text = stringResource(R.string.update_available), style = MaterialTheme.typography.displaySmall, color = MaterialTheme.colorScheme.onSurface, ) @@ -234,14 +236,14 @@ fun InstallUpdatePageContent( onClick = onInstallRelease, ) { Text( - text = "Download & Update", + text = stringResource(R.string.download_and_update), ) } Button( onClick = onCancel, ) { Text( - text = "Cancel", + text = stringResource(R.string.cancel), ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/ServerList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/ServerList.kt index 8dadb4fb..a83cfb6d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/ServerList.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/ServerList.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.window.DialogProperties import androidx.tv.material3.Icon @@ -96,7 +97,7 @@ fun ServerList( ListItem( enabled = true, selected = false, - headlineContent = { Text(text = "Add Server") }, + headlineContent = { Text(text = stringResource(R.string.add_server)) }, leadingContent = { Icon( imageVector = Icons.Default.Add, @@ -116,11 +117,14 @@ fun ServerList( title = server.name ?: server.url, dialogItems = listOf( - DialogItem("Switch", R.string.fa_arrow_left_arrow_right) { + DialogItem( + stringResource(R.string.switch_servers), + R.string.fa_arrow_left_arrow_right, + ) { onSwitchServer.invoke(server) }, DialogItem( - "Delete", + stringResource(R.string.delete), Icons.Default.Delete, Color.Red.copy(alpha = .8f), ) { diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt index e840a3e5..0dbce500 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchServerContent.kt @@ -22,6 +22,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp @@ -31,6 +32,7 @@ import androidx.tv.material3.Button import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.components.BasicDialog import com.github.damontecres.wholphin.ui.components.CircularProgress import com.github.damontecres.wholphin.ui.components.EditTextBox @@ -78,7 +80,7 @@ fun SwitchServerContent( ), ) { Text( - text = "Select Server", + text = stringResource(R.string.select_server), style = MaterialTheme.typography.displaySmall, color = MaterialTheme.colorScheme.onSurface, ) @@ -120,13 +122,13 @@ fun SwitchServerContent( ), ) { Text( - text = "Discovered Servers", + text = stringResource(R.string.discovered_servers), style = MaterialTheme.typography.displaySmall, color = MaterialTheme.colorScheme.onSurface, ) if (discoveredServers.isEmpty()) { Text( - text = "Searching...", + text = stringResource(R.string.searching), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface, ) @@ -184,7 +186,7 @@ fun SwitchServerContent( .fillMaxWidth(.4f), ) { Text( - text = "Enter Server URL", + text = stringResource(R.string.enter_server_url), ) EditTextBox( value = url, @@ -227,7 +229,7 @@ fun SwitchServerContent( if (state == LoadingState.Loading) { CircularProgress(Modifier.size(32.dp)) } else { - Text(text = "Submit") + Text(text = stringResource(R.string.submit)) } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt index b8d3c9d3..49e9be8a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/SwitchUserContent.kt @@ -26,6 +26,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.KeyboardType @@ -37,6 +38,7 @@ import androidx.tv.material3.Button import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation +import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.ui.components.BasicDialog import com.github.damontecres.wholphin.ui.components.CircularProgress import com.github.damontecres.wholphin.ui.components.EditTextBox @@ -94,7 +96,7 @@ fun SwitchUserContent( ), ) { Text( - text = "Select User", + text = stringResource(R.string.select_user), style = MaterialTheme.typography.displaySmall, color = MaterialTheme.colorScheme.onSurface, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt index 37bc69c0..c4fb035e 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/setup/UserList.kt @@ -67,7 +67,7 @@ fun UserList( ListItem( enabled = true, selected = false, - headlineContent = { Text(text = "Add User") }, + headlineContent = { Text(text = stringResource(R.string.add_user)) }, leadingContent = { Icon( imageVector = Icons.Default.Add, @@ -84,7 +84,7 @@ fun UserList( ListItem( enabled = true, selected = false, - headlineContent = { Text(text = "Switch servers") }, + headlineContent = { Text(text = stringResource(R.string.switch_servers)) }, leadingContent = { Text( text = stringResource(R.string.fa_arrow_left_arrow_right), @@ -102,11 +102,14 @@ fun UserList( title = user.name ?: user.id.toString(), dialogItems = listOf( - DialogItem("Switch", R.string.fa_arrow_left_arrow_right) { + DialogItem( + stringResource(R.string.switch_user), + R.string.fa_arrow_left_arrow_right, + ) { onSwitchUser.invoke(user) }, DialogItem( - "Delete", + stringResource(R.string.delete), Icons.Default.Delete, Color.Red.copy(alpha = .8f), ) { diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/Constants.kt b/app/src/main/java/com/github/damontecres/wholphin/util/Constants.kt index c6ec4bb5..e447758f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/Constants.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/Constants.kt @@ -1,16 +1,8 @@ package com.github.damontecres.wholphin.util -import com.github.damontecres.wholphin.ui.main.HomeSection import org.jellyfin.sdk.model.api.BaseItemKind import org.jellyfin.sdk.model.api.CollectionType -val supportedHomeSection = - setOf( - HomeSection.LATEST_MEDIA, - HomeSection.NEXT_UP, - HomeSection.RESUME, - ) - val supportItemKinds = setOf( BaseItemKind.MOVIE, diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt b/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt index 3c411724..0afdca90 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/CrashReportSenderFactory.kt @@ -48,7 +48,7 @@ class CrashReportSender : ReportSender { val api = createJellyfin { this.context = context - clientInfo = AppModule.clientInfo() + clientInfo = AppModule.clientInfo(context) deviceInfo = AppModule.deviceInfo(context) apiClientFactory = okHttpFactory socketConnectionFactory = okHttpFactory diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt b/app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt index 7076276a..94595b0a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/Formatting.kt @@ -1,6 +1,7 @@ package com.github.damontecres.wholphin.util import android.os.Build +import androidx.annotation.StringRes import androidx.compose.runtime.Composable import androidx.compose.ui.res.stringResource import com.github.damontecres.wholphin.R @@ -11,6 +12,7 @@ import com.github.damontecres.wholphin.data.model.chooseStream import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.ui.isNotNullOrBlank import org.jellyfin.sdk.model.api.BaseItemDto +import org.jellyfin.sdk.model.api.MediaSegmentType import org.jellyfin.sdk.model.api.MediaStream import org.jellyfin.sdk.model.api.MediaStreamType import java.time.LocalDate @@ -164,3 +166,15 @@ fun formatBytes( } return String.format(Locale.getDefault(), "%.2f%s", count, suffixes[unit]) } + +@get:StringRes +val MediaSegmentType.stringRes: Int + get() = + when (this) { + MediaSegmentType.UNKNOWN -> R.string.unknown + MediaSegmentType.COMMERCIAL -> R.string.commercial + MediaSegmentType.PREVIEW -> R.string.preview + MediaSegmentType.RECAP -> R.string.recap + MediaSegmentType.OUTRO -> R.string.outro + MediaSegmentType.INTRO -> R.string.intro + } diff --git a/app/src/main/res/values/preferences.xml b/app/src/main/res/values/preferences.xml index 6e38778a..1f7ba4d4 100644 --- a/app/src/main/res/values/preferences.xml +++ b/app/src/main/res/values/preferences.xml @@ -1,5 +1,53 @@ + preference.update.threshold + preference.update.lastTimestamp + + Device supports AC3/Dolby Digital + Advanced Settings + Advanced UI + Application Theme + Automatically check for updates + Delay before playing next up + Auto play next up + Check for updates + Applies to TV Series only + + Direct play ASS subtitles + Direct play PGS subtitles + Always downmix to stereo + Use FFmpeg decoder module + Default content scale + Install update + Installed version + Max items on home page rows + Choose the default items to show, others will be hidden + Customize Navigation Drawer Items + Click to switch pages + Switch nav drawer pages on focus + Passout Protection + Play theme music + Playback overrides + Remember selected tabs + Enable rewatching in next up + Seek bar steps + Useful for debugging + Send app logs to current server + Will try to send to last connected server + Send Crash Reports + Settings + Skip back when resuming playback + Skip back + Skip commercials behavior + Skip forward + Skip intro behavior + Skip outro behavior + Skip previews behavior + Skip recap behavior + Update available + URL used to check for app updates + Update URL + Disabled Lowest diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 67fd8735..7e6ac782 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,109 +1,157 @@ - Wholphin + Wholphin + Wholphin - Recently added media - My media - My media (small) - Continue watching - Continue listening - Continue reading - Active recordings - Next up - Live TV - None - Search - Resume - Restart - Play - More + About + Active Recordings + Favorite + Add Server + Add User + Audio + Birthplace + Bitrate + Born + Cancel Recording + Cancel Series Recording + Cancel + Chapters + Choose %1$s + Clear image cache + Collection + Collections + Commercial + Community Rating + An error occurred! Press the button to send logs to your server. + Confirm + Continue watching + Critic Rating + %.1f seconds + Default + Delete + Died + Directed by %1$s + Director + Disabled + Discovered Servers + + Downloading… + Enabled + Enter Server IP or URL including port + Episodes + Error loading collection %1$s + External + Favorites + Size + Forced + Genres + Go to series + Go To + Hide playback controls + Hide debug info + Hide + Home + Immediate + Intro + #ABCDEFGHIJKLMNOPQRSTUVWXYZ + Library + License information + Live TV + Loading… + Mark entire series as played? + Mark entire series as unplayed? Mark as unwatched Mark as watched - Cancel - Save - Wholphin - Interface - Playback - About - Advanced Settings - Advanced UI - Install update - Installed version - Show - Hide - Enabled - Disabled - Skip forward - Skip back - Hide playback controls - Seek bar steps - Max items on home page rows - Enable rewatching in next up - Studios - Video - Audio - Genres - Play theme music - License information + Max bitrate + More like this + More + Movies + Name + Next Up + No data + No results + No scheduled recordings + No update available + None + Outro + Path + People + Play Count + Play from here + Play Show playback debug info - Delay before playing next up - Auto play next up - - - Name - Random - Date Episode Added + Playback Speed + Playback + Playlist + Preview + User Profile Settings + Queue + Recap + Recently added in %1$s + Recently added + Recently Recorded + Recently Released + Recommended + Record Program + Record Series + Unfavorite + Restart + Resume + Save + + Search + Searching… + Select Server + Select User + Show debug info + Show next up + Show + Shuffle + Skip Date Added + Date Episode Added Date Played Date Released - Skip back when resuming playback - preference.update.threshold - preference.update.lastTimestamp - Confirm - Remember selected tabs - Director - Max bitrate - Application Theme - Skip recap behavior - Skip previews behavior - Skip commercials behavior - Skip outro behavior - Skip intro behavior - Check for updates - Automatically check for updates - Update URL - URL used to check for app updates - Updates - No update available - Clear image cache - Shuffle - - Device supports AC3/Dolby Digital - Always downmix to stereo - Playback overrides - Direct play ASS subtitles - Direct play PGS subtitles + Name + Random + Studios + Submit + Download is taking a long time, you may need to restart playback + Subtitle + Subtitles + Suggestions + Switch servers + Switch + Top Rated Unwatched + Trailer Trailers - Passout Protection - More like this - Unfavorite - Favorite - Favorites - Customize Navigation Drawer Items - User Profile Settings - Choose the default items to show, others will be hidden - Applies to TV Series only - Send Crash Reports - Will try to send to last connected server - Send app logs to current server - Useful for debugging - Default content scale - Use FFmpeg decoder module - Switch nav drawer pages on focus - Click to switch pages - Show next up - Next Up - Community Rating - Critic Rating - Play Count + DVR Schedule + Guide + Season + Seasons + TV Shows + Interface + Unknown + Updates + Version + Video Scale + Video + Watch live + %1$d years old + + + %d downloads + %d download + %d downloads + + + %d hours + %d hour + %d hours + + + %d seconds + %d second + %d seconds +