mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Apply view options to preview
This commit is contained in:
parent
5dcdde36c3
commit
1ce380f3b5
9 changed files with 275 additions and 26 deletions
|
|
@ -2,10 +2,16 @@
|
|||
|
||||
package com.github.damontecres.wholphin.data.model
|
||||
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.preferences.AppChoicePreference
|
||||
import com.github.damontecres.wholphin.preferences.AppClickablePreference
|
||||
import com.github.damontecres.wholphin.preferences.AppSliderPreference
|
||||
import com.github.damontecres.wholphin.preferences.AppSwitchPreference
|
||||
import com.github.damontecres.wholphin.preferences.PrefContentScale
|
||||
import com.github.damontecres.wholphin.ui.AspectRatio
|
||||
import com.github.damontecres.wholphin.ui.Cards
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionImageType
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptions
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.UseSerializers
|
||||
import org.jellyfin.sdk.model.serializer.UUIDSerializer
|
||||
|
|
@ -16,33 +22,43 @@ sealed class HomeRowConfig {
|
|||
abstract val id: UUID
|
||||
abstract val viewOptions: HomeRowViewOptions
|
||||
|
||||
abstract fun updateViewOptions(viewOptions: HomeRowViewOptions): HomeRowConfig
|
||||
|
||||
@Serializable
|
||||
data class ContinueWatching(
|
||||
override val id: UUID,
|
||||
val combined: Boolean,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
) : HomeRowConfig() {
|
||||
override fun updateViewOptions(viewOptions: HomeRowViewOptions): HomeRowConfig = this.copy(viewOptions = viewOptions)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RecentlyAdded(
|
||||
override val id: UUID,
|
||||
val parentId: UUID,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
) : HomeRowConfig() {
|
||||
override fun updateViewOptions(viewOptions: HomeRowViewOptions): HomeRowConfig = this.copy(viewOptions = viewOptions)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RecentlyReleased(
|
||||
override val id: UUID,
|
||||
val parentId: UUID,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
) : HomeRowConfig() {
|
||||
override fun updateViewOptions(viewOptions: HomeRowViewOptions): HomeRowConfig = this.copy(viewOptions = viewOptions)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Genres(
|
||||
override val id: UUID,
|
||||
val parentId: UUID,
|
||||
override val viewOptions: HomeRowViewOptions,
|
||||
) : HomeRowConfig()
|
||||
) : HomeRowConfig() {
|
||||
override fun updateViewOptions(viewOptions: HomeRowViewOptions): HomeRowConfig = this.copy(viewOptions = viewOptions)
|
||||
}
|
||||
}
|
||||
|
||||
data class HomeRowConfigDisplay(
|
||||
|
|
@ -59,4 +75,101 @@ data class HomeRowViewOptions(
|
|||
val imageType: ViewOptionImageType = ViewOptionImageType.PRIMARY,
|
||||
val showTitles: Boolean = true,
|
||||
val useSeries: Boolean = true,
|
||||
)
|
||||
) {
|
||||
companion object {
|
||||
val ViewOptionsColumns =
|
||||
AppSliderPreference<HomeRowViewOptions>(
|
||||
title = R.string.height,
|
||||
defaultValue = Cards.HEIGHT_2X3_DP.toLong(),
|
||||
min = Cards.HEIGHT_2X3_DP / 2L,
|
||||
max = Cards.HEIGHT_2X3_DP + 64L,
|
||||
interval = 4,
|
||||
getter = { it.heightDp.toLong() },
|
||||
setter = { prefs, value -> prefs.copy(heightDp = value.toInt()) },
|
||||
)
|
||||
val ViewOptionsSpacing =
|
||||
AppSliderPreference<HomeRowViewOptions>(
|
||||
title = R.string.spacing,
|
||||
defaultValue = 16,
|
||||
min = 0,
|
||||
max = 32,
|
||||
interval = 2,
|
||||
getter = { it.spacing.toLong() },
|
||||
setter = { prefs, value -> prefs.copy(spacing = value.toInt()) },
|
||||
)
|
||||
|
||||
val ViewOptionsContentScale =
|
||||
AppChoicePreference<HomeRowViewOptions, PrefContentScale>(
|
||||
title = R.string.global_content_scale,
|
||||
defaultValue = PrefContentScale.FIT,
|
||||
displayValues = R.array.content_scale,
|
||||
getter = { it.contentScale },
|
||||
setter = { viewOptions, value -> viewOptions.copy(contentScale = value) },
|
||||
indexToValue = { PrefContentScale.forNumber(it) },
|
||||
valueToIndex = { it.number },
|
||||
)
|
||||
|
||||
val ViewOptionsAspectRatio =
|
||||
AppChoicePreference<HomeRowViewOptions, AspectRatio>(
|
||||
title = R.string.aspect_ratio,
|
||||
defaultValue = AspectRatio.TALL,
|
||||
displayValues = R.array.aspect_ratios,
|
||||
getter = { it.aspectRatio },
|
||||
setter = { viewOptions, value -> viewOptions.copy(aspectRatio = value) },
|
||||
indexToValue = { AspectRatio.entries[it] },
|
||||
valueToIndex = { it.ordinal },
|
||||
)
|
||||
|
||||
val ViewOptionsShowTitles =
|
||||
AppSwitchPreference<HomeRowViewOptions>(
|
||||
title = R.string.show_titles,
|
||||
defaultValue = true,
|
||||
getter = { it.showTitles },
|
||||
setter = { vo, value -> vo.copy(showTitles = value) },
|
||||
)
|
||||
|
||||
val ViewOptionsUseSeries =
|
||||
AppSwitchPreference<HomeRowViewOptions>(
|
||||
title = R.string.go_to_series, // TODO
|
||||
defaultValue = true,
|
||||
getter = { it.useSeries },
|
||||
setter = { vo, value -> vo.copy(useSeries = value) },
|
||||
)
|
||||
|
||||
val ViewOptionsImageType =
|
||||
AppChoicePreference<HomeRowViewOptions, ViewOptionImageType>(
|
||||
title = R.string.image_type,
|
||||
defaultValue = ViewOptionImageType.PRIMARY,
|
||||
displayValues = R.array.image_types,
|
||||
getter = { it.imageType },
|
||||
setter = { viewOptions, value ->
|
||||
val aspectRatio =
|
||||
when (value) {
|
||||
ViewOptionImageType.PRIMARY -> AspectRatio.TALL
|
||||
ViewOptionImageType.THUMB -> AspectRatio.WIDE
|
||||
}
|
||||
viewOptions.copy(imageType = value, aspectRatio = aspectRatio)
|
||||
},
|
||||
indexToValue = { ViewOptionImageType.entries[it] },
|
||||
valueToIndex = { it.ordinal },
|
||||
)
|
||||
|
||||
val ViewOptionsReset =
|
||||
AppClickablePreference<ViewOptions>(
|
||||
title = R.string.reset,
|
||||
)
|
||||
|
||||
val OPTIONS =
|
||||
listOf(
|
||||
ViewOptionsImageType,
|
||||
ViewOptionsAspectRatio,
|
||||
// TODO
|
||||
// ViewOptionsShowTitles,
|
||||
// ViewOptionsUseSeries,
|
||||
ViewOptionsColumns,
|
||||
ViewOptionsSpacing,
|
||||
ViewOptionsContentScale,
|
||||
ViewOptionsReset,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ fun BannerCard(
|
|||
cardHeight: Dp = 120.dp,
|
||||
aspectRatio: Float = AspectRatios.WIDE,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
imageType: ImageType = ImageType.PRIMARY,
|
||||
imageContentScale: ContentScale = ContentScale.FillBounds,
|
||||
) {
|
||||
val imageUrlService = LocalImageUrlService.current
|
||||
val density = LocalDensity.current
|
||||
|
|
@ -74,11 +76,11 @@ fun BannerCard(
|
|||
}
|
||||
}
|
||||
val imageUrl =
|
||||
remember(item, fillHeight) {
|
||||
remember(item, fillHeight, imageType) {
|
||||
if (item != null) {
|
||||
imageUrlService.getItemImageUrl(
|
||||
item,
|
||||
ImageType.PRIMARY,
|
||||
imageType,
|
||||
fillWidth = null,
|
||||
fillHeight = fillHeight,
|
||||
)
|
||||
|
|
@ -107,7 +109,7 @@ fun BannerCard(
|
|||
AsyncImage(
|
||||
model = imageUrl,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillBounds,
|
||||
contentScale = imageContentScale,
|
||||
onError = { imageError = true },
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ 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.AspectRatios
|
||||
import com.github.damontecres.wholphin.ui.Cards
|
||||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||
|
|
@ -67,6 +66,7 @@ import com.github.damontecres.wholphin.ui.isNotNullOrBlank
|
|||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.playback.isPlayKeyUp
|
||||
import com.github.damontecres.wholphin.ui.playback.playable
|
||||
import com.github.damontecres.wholphin.ui.playback.scale
|
||||
import com.github.damontecres.wholphin.ui.rememberPosition
|
||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||
|
|
@ -308,6 +308,7 @@ fun HomePageContent(
|
|||
|
||||
is HomeRowLoadingState.Success -> {
|
||||
if (row.items.isNotEmpty()) {
|
||||
val viewOptions = row.viewOptions
|
||||
ItemRow(
|
||||
title = row.title,
|
||||
items = row.items,
|
||||
|
|
@ -323,11 +324,14 @@ fun HomePageContent(
|
|||
.focusGroup()
|
||||
.focusRequester(rowFocusRequesters[rowIndex])
|
||||
.animateItem(),
|
||||
horizontalPadding = viewOptions.spacing.dp,
|
||||
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
||||
BannerCard(
|
||||
name = item?.data?.seriesName ?: item?.name,
|
||||
item = item,
|
||||
aspectRatio = AspectRatios.TALL,
|
||||
aspectRatio = viewOptions.aspectRatio.ratio,
|
||||
imageType = viewOptions.imageType.imageType,
|
||||
imageContentScale = viewOptions.contentScale.scale,
|
||||
cornerText = item?.ui?.episdodeUnplayedCornerText,
|
||||
played = item?.data?.userData?.played ?: false,
|
||||
favorite = item?.favorite ?: false,
|
||||
|
|
@ -366,7 +370,7 @@ fun HomePageContent(
|
|||
return@onKeyEvent false
|
||||
},
|
||||
interactionSource = null,
|
||||
cardHeight = Cards.height2x3,
|
||||
cardHeight = viewOptions.heightDp.dp,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
|
|
@ -26,6 +25,7 @@ import androidx.compose.ui.res.stringResource
|
|||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.ListItem
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
|
|
@ -44,6 +44,7 @@ enum class MoveDirection {
|
|||
@Composable
|
||||
fun HomePageRowList(
|
||||
state: HomePageSettingsState,
|
||||
onClick: (Int, HomeRowConfigDisplay) -> Unit,
|
||||
onClickAdd: () -> Unit,
|
||||
onClickMove: (MoveDirection, Int) -> Unit,
|
||||
onClickDelete: (Int) -> Unit,
|
||||
|
|
@ -66,6 +67,7 @@ fun HomePageRowList(
|
|||
deleteAllowed = row.config !is HomeRowConfig.ContinueWatching,
|
||||
onClickMove = { onClickMove.invoke(it, index) },
|
||||
onClickDelete = { onClickDelete.invoke(index) },
|
||||
onClick = { onClick.invoke(index, row) },
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
|
|
@ -92,6 +94,7 @@ fun HomeRowConfigContent(
|
|||
moveUpAllowed: Boolean,
|
||||
moveDownAllowed: Boolean,
|
||||
deleteAllowed: Boolean,
|
||||
onClick: () -> Unit,
|
||||
onClickMove: (MoveDirection) -> Unit,
|
||||
onClickDelete: () -> Unit,
|
||||
modifier: Modifier,
|
||||
|
|
@ -111,15 +114,18 @@ fun HomeRowConfigContent(
|
|||
shape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
) {
|
||||
Text(
|
||||
text = config.title,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier =
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 4.dp),
|
||||
ListItem(
|
||||
selected = false,
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = config.title,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier =
|
||||
Modifier,
|
||||
)
|
||||
},
|
||||
onClick = onClick,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
package com.github.damontecres.wholphin.ui.main.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||
import com.github.damontecres.wholphin.preferences.AppPreference
|
||||
import com.github.damontecres.wholphin.ui.preferences.ComposablePreference
|
||||
|
||||
@Composable
|
||||
fun HomePageRowSettings(
|
||||
title: String,
|
||||
viewOptions: HomeRowViewOptions,
|
||||
onViewOptionsChange: (HomeRowViewOptions) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
defaultViewOptions: HomeRowViewOptions = HomeRowViewOptions(),
|
||||
) {
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
LazyColumn {
|
||||
items(HomeRowViewOptions.OPTIONS) { pref ->
|
||||
pref as AppPreference<HomeRowViewOptions, Any>
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val value = pref.getter.invoke(viewOptions)
|
||||
ComposablePreference(
|
||||
preference = pref,
|
||||
value = value,
|
||||
onNavigate = {},
|
||||
onValueChange = { newValue ->
|
||||
onViewOptionsChange.invoke(pref.setter(viewOptions, newValue))
|
||||
},
|
||||
interactionSource = interactionSource,
|
||||
onClickPreference = { pref ->
|
||||
if (pref == HomeRowViewOptions.ViewOptionsReset) {
|
||||
onViewOptionsChange.invoke(defaultViewOptions)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.background(MaterialTheme.colorScheme.surfaceColorAtElevation(5.dp)),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -159,6 +159,7 @@ class HomePageSettingsViewModel
|
|||
HomeRowLoadingState.Success(
|
||||
title = context.getString(R.string.continue_watching),
|
||||
items = items,
|
||||
viewOptions = row.viewOptions,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -167,6 +168,7 @@ class HomePageSettingsViewModel
|
|||
HomeRowLoadingState.Success(
|
||||
title = context.getString(R.string.continue_watching),
|
||||
items = resume,
|
||||
viewOptions = row.viewOptions,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -175,6 +177,7 @@ class HomePageSettingsViewModel
|
|||
HomeRowLoadingState.Success(
|
||||
title = context.getString(R.string.next_up),
|
||||
items = nextUp,
|
||||
viewOptions = row.viewOptions,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -202,7 +205,13 @@ class HomePageSettingsViewModel
|
|||
val title =
|
||||
name?.let { context.getString(R.string.genres_in, it) }
|
||||
?: context.getString(R.string.genres)
|
||||
listOf(HomeRowLoadingState.Success(title, items))
|
||||
listOf(
|
||||
HomeRowLoadingState.Success(
|
||||
title,
|
||||
items,
|
||||
viewOptions = row.viewOptions,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
is HomeRowConfig.RecentlyAdded -> {
|
||||
|
|
@ -222,7 +231,17 @@ class HomePageSettingsViewModel
|
|||
limit = limit,
|
||||
isPlayed = null, // Server will handle user's preference
|
||||
)
|
||||
latestNextUpService.loadLatest(listOf(LatestData(title, request)))
|
||||
latestNextUpService
|
||||
.loadLatest(listOf(LatestData(title, request)))
|
||||
.let {
|
||||
it.map {
|
||||
if (it is HomeRowLoadingState.Success) {
|
||||
it.copy(viewOptions = row.viewOptions)
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is HomeRowConfig.RecentlyReleased -> {
|
||||
|
|
@ -342,6 +361,39 @@ class HomePageSettingsViewModel
|
|||
fetchRowData()
|
||||
}
|
||||
}
|
||||
|
||||
fun updateViewOptions(
|
||||
rowId: UUID,
|
||||
viewOptions: HomeRowViewOptions,
|
||||
) {
|
||||
viewModelScope.launchIO {
|
||||
_state.update {
|
||||
val index = state.value.rows.indexOfFirst { it.config.id == rowId }
|
||||
val newRowConfig =
|
||||
state.value.rows[index]
|
||||
.config
|
||||
.updateViewOptions(viewOptions)
|
||||
val newRow = state.value.rows[index].copy(config = newRowConfig)
|
||||
it.copy(
|
||||
rows =
|
||||
it.rows.toMutableList().apply {
|
||||
set(index, newRow)
|
||||
},
|
||||
rowData =
|
||||
it.rowData.toMutableList().apply {
|
||||
val row = it.rowData[index]
|
||||
val newRow =
|
||||
if (row is HomeRowLoadingState.Success) {
|
||||
row.copy(viewOptions = viewOptions)
|
||||
} else {
|
||||
row
|
||||
}
|
||||
set(index, newRow)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class HomePageSettingsState(
|
||||
|
|
@ -410,6 +462,9 @@ fun HomePageSettings(
|
|||
onClickAdd = { destination = HomePageSettingsDestination.ChooseLibrary },
|
||||
onClickMove = viewModel::moveRow,
|
||||
onClickDelete = viewModel::deleteRow,
|
||||
onClick = { index, row ->
|
||||
destination = HomePageSettingsDestination.RowSettings(row.config.id)
|
||||
},
|
||||
modifier = destModifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -434,7 +489,17 @@ fun HomePageSettings(
|
|||
}
|
||||
|
||||
is HomePageSettingsDestination.RowSettings -> {
|
||||
TODO()
|
||||
val row =
|
||||
state.rows
|
||||
.first { it.config.id == dest.rowId }
|
||||
HomePageRowSettings(
|
||||
title = row.title,
|
||||
viewOptions = row.config.viewOptions,
|
||||
onViewOptionsChange = {
|
||||
viewModel.updateViewOptions(dest.rowId, it)
|
||||
},
|
||||
modifier = destModifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.github.damontecres.wholphin.ui.main.settings
|
||||
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||
import java.util.UUID
|
||||
|
||||
sealed interface HomePageSettingsDestination {
|
||||
data object RowList : HomePageSettingsDestination
|
||||
|
|
@ -12,6 +12,6 @@ sealed interface HomePageSettingsDestination {
|
|||
) : HomePageSettingsDestination
|
||||
|
||||
data class RowSettings(
|
||||
val row: HomeRowConfigDisplay,
|
||||
val rowId: UUID,
|
||||
) : HomePageSettingsDestination
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.damontecres.wholphin.util
|
||||
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||
|
||||
/**
|
||||
* Generic state for loading something from the API
|
||||
|
|
@ -59,6 +60,7 @@ sealed interface HomeRowLoadingState {
|
|||
data class Success(
|
||||
override val title: String,
|
||||
val items: List<BaseItem?>,
|
||||
val viewOptions: HomeRowViewOptions = HomeRowViewOptions(),
|
||||
) : HomeRowLoadingState
|
||||
|
||||
data class Error(
|
||||
|
|
|
|||
|
|
@ -465,6 +465,7 @@
|
|||
<string name="add_row">Add row</string>
|
||||
<string name="genres_in">Genres in %1$s</string>
|
||||
<string name="recently_released_in">Recently released in %1$s</string>
|
||||
<string name="height">Height</string>
|
||||
|
||||
<string-array name="theme_song_volume">
|
||||
<item>Disabled</item>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue