Use app preferences

This commit is contained in:
Damontecres 2025-10-02 18:05:24 -04:00
parent 3dd78f175b
commit 9e876a85d4
No known key found for this signature in database
18 changed files with 146 additions and 45 deletions

View file

@ -3,6 +3,7 @@ package com.github.damontecres.dolphin
import android.os.Bundle import android.os.Bundle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
@ -57,7 +58,10 @@ class MainActivity : AppCompatActivity() {
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
DolphinTheme(true) { DolphinTheme(true) {
Surface( Surface(
modifier = Modifier.fillMaxSize(), modifier =
Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background),
shape = RectangleShape, shape = RectangleShape,
) { ) {
CoilConfig(serverRepository, okHttpClient, false) CoilConfig(serverRepository, okHttpClient, false)

View file

@ -1,12 +1,12 @@
package com.github.damontecres.dolphin.ui.preferences package com.github.damontecres.dolphin.preferences
import android.content.Context import android.content.Context
import androidx.annotation.ArrayRes import androidx.annotation.ArrayRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.github.damontecres.dolphin.R import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.preferences.AppPreferences
import com.github.damontecres.dolphin.preferences.updatePlaybackPreferences
import com.github.damontecres.dolphin.ui.nav.Destination import com.github.damontecres.dolphin.ui.nav.Destination
import com.github.damontecres.dolphin.ui.preferences.PreferenceGroup
import com.github.damontecres.dolphin.ui.preferences.PreferenceValidation
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.minutes import kotlin.time.Duration.Companion.minutes
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
@ -149,6 +149,32 @@ sealed interface AppPreference<T> {
summarizer = { value -> value?.toString() }, summarizer = { value -> value?.toString() },
) )
val HomePageItems =
AppSliderPreference(
title = R.string.max_homepage_items,
defaultValue = 25,
min = 5,
max = 50,
interval = 1,
getter = { it.homePagePreferences.maxItemsPerRow.toLong() },
setter = { prefs, value ->
prefs.updateHomePagePreferences { maxItemsPerRow = value.toInt() }
},
summarizer = { value -> value?.toString() },
)
val RewatchNextUp =
AppSwitchPreference(
title = R.string.rewatch_next_up,
defaultValue = false,
getter = { it.homePagePreferences.enableRewatchingNextUp },
setter = { prefs, value ->
prefs.updateHomePagePreferences { enableRewatchingNextUp = value }
},
summaryOn = R.string.enabled,
summaryOff = R.string.disabled,
)
// val PlaybackDebugInfo = // val PlaybackDebugInfo =
// AppSwitchPreference( // AppSwitchPreference(
// title = R.string.playback_debug_info, // title = R.string.playback_debug_info,
@ -194,6 +220,26 @@ sealed interface AppPreference<T> {
} }
} }
val basicPreferences =
listOf(
PreferenceGroup(
title = R.string.basic_interface,
preferences =
listOf(
AppPreference.SkipForward,
AppPreference.SkipBack,
AppPreference.ControllerTimeout,
AppPreference.SeekBarSteps,
AppPreference.HomePageItems,
AppPreference.RewatchNextUp,
),
),
)
val uiPreferences = listOf<PreferenceGroup>()
val advancedPreferences = listOf<PreferenceGroup>()
data class AppSwitchPreference( data class AppSwitchPreference(
@get:StringRes override val title: Int, @get:StringRes override val title: Int,
override val defaultValue: Boolean, override val defaultValue: Boolean,

View file

@ -2,7 +2,6 @@ package com.github.damontecres.dolphin.preferences
import androidx.datastore.core.CorruptionException import androidx.datastore.core.CorruptionException
import androidx.datastore.core.Serializer import androidx.datastore.core.Serializer
import com.github.damontecres.dolphin.ui.preferences.AppPreference
import com.google.protobuf.InvalidProtocolBufferException import com.google.protobuf.InvalidProtocolBufferException
import java.io.InputStream import java.io.InputStream
import java.io.OutputStream import java.io.OutputStream
@ -27,6 +26,13 @@ class AppPreferencesSerializer
controllerTimeoutMs = AppPreference.ControllerTimeout.defaultValue controllerTimeoutMs = AppPreference.ControllerTimeout.defaultValue
seekBarSteps = AppPreference.SeekBarSteps.defaultValue.toInt() seekBarSteps = AppPreference.SeekBarSteps.defaultValue.toInt()
}.build() }.build()
homePagePreferences =
HomePagePreferences
.newBuilder()
.apply {
maxItemsPerRow = AppPreference.HomePageItems.defaultValue.toInt()
enableRewatchingNextUp = AppPreference.RewatchNextUp.defaultValue
}.build()
}.build() }.build()
override suspend fun readFrom(input: InputStream): AppPreferences { override suspend fun readFrom(input: InputStream): AppPreferences {
@ -49,3 +55,8 @@ inline fun AppPreferences.updatePlaybackPreferences(block: PlaybackPreferences.B
update { update {
playbackPreferences = playbackPreferences.toBuilder().apply(block).build() playbackPreferences = playbackPreferences.toBuilder().apply(block).build()
} }
inline fun AppPreferences.updateHomePagePreferences(block: HomePagePreferences.Builder.() -> Unit): AppPreferences =
update {
homePagePreferences = homePagePreferences.toBuilder().apply(block).build()
}

View file

@ -18,6 +18,7 @@ import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.input.key.onPreviewKeyEvent import androidx.compose.ui.input.key.onPreviewKeyEvent
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.media3.common.Player
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import org.jellyfin.sdk.model.api.BaseItemDto import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.extensions.ticks import org.jellyfin.sdk.model.extensions.ticks
@ -181,3 +182,7 @@ val BaseItemDto.timeRemaining: Duration?
null null
} }
} }
fun Player.seekBack(amount: Duration) = seekTo((currentPosition - amount.inWholeMilliseconds).coerceAtLeast(0L))
fun Player.seekForward(amount: Duration) = seekTo((currentPosition + amount.inWholeMilliseconds).coerceAtMost(duration))

View file

@ -39,6 +39,7 @@ import androidx.tv.material3.Text
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import com.github.damontecres.dolphin.data.model.BaseItem import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.preferences.UserPreferences import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.OneTimeLaunchedEffect
import com.github.damontecres.dolphin.ui.cards.BannerCard import com.github.damontecres.dolphin.ui.cards.BannerCard
import com.github.damontecres.dolphin.ui.cards.ItemRow import com.github.damontecres.dolphin.ui.cards.ItemRow
import com.github.damontecres.dolphin.ui.components.CircularProgress import com.github.damontecres.dolphin.ui.components.CircularProgress
@ -67,6 +68,9 @@ fun MainPage(
modifier: Modifier, modifier: Modifier,
viewModel: MainViewModel = hiltViewModel(), viewModel: MainViewModel = hiltViewModel(),
) { ) {
OneTimeLaunchedEffect {
viewModel.init(preferences)
}
val loading by viewModel.loadingState.observeAsState(LoadingState.Loading) val loading by viewModel.loadingState.observeAsState(LoadingState.Loading)
when (val state = loading) { when (val state = loading) {
is LoadingState.Error -> Text(text = "Error: ${state.message}", modifier = modifier) is LoadingState.Error -> Text(text = "Error: ${state.message}", modifier = modifier)

View file

@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.github.damontecres.dolphin.data.model.BaseItem import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.DefaultItemFields import com.github.damontecres.dolphin.ui.DefaultItemFields
import com.github.damontecres.dolphin.ui.isNotNullOrBlank import com.github.damontecres.dolphin.ui.isNotNullOrBlank
import com.github.damontecres.dolphin.util.ExceptionHandler import com.github.damontecres.dolphin.util.ExceptionHandler
@ -34,7 +35,8 @@ class MainViewModel
val loadingState = MutableLiveData<LoadingState>(LoadingState.Loading) val loadingState = MutableLiveData<LoadingState>(LoadingState.Loading)
val homeRows = MutableLiveData<List<HomeRow>>() val homeRows = MutableLiveData<List<HomeRow>>()
init { fun init(preferences: UserPreferences) {
val limit = preferences.appPreferences.homePagePreferences.maxItemsPerRow
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) { viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
val user = api.userApi.getCurrentUser().content val user = api.userApi.getCurrentUser().content
val displayPrefs = val displayPrefs =
@ -74,7 +76,7 @@ class MainViewModel
imageTypeLimit = 1, imageTypeLimit = 1,
parentId = viewId, parentId = viewId,
groupItems = true, groupItems = true,
limit = 25, limit = limit,
) )
val latest = val latest =
api.userLibraryApi api.userLibraryApi
@ -94,6 +96,7 @@ class MainViewModel
GetResumeItemsRequest( GetResumeItemsRequest(
userId = user.id, userId = user.id,
fields = DefaultItemFields, fields = DefaultItemFields,
limit = limit,
// TODO, more params? // TODO, more params?
) )
val items = val items =
@ -116,8 +119,10 @@ class MainViewModel
fields = DefaultItemFields, fields = DefaultItemFields,
imageTypeLimit = 1, imageTypeLimit = 1,
parentId = null, parentId = null,
limit = 25, limit = limit,
enableResumable = false, enableResumable = false,
enableUserData = true,
enableRewatching = preferences.appPreferences.homePagePreferences.enableRewatchingNextUp,
) )
val nextUp = val nextUp =
api.tvShowsApi api.tvShowsApi

View file

@ -109,8 +109,7 @@ fun NavDrawer(
NavigationDrawer( NavigationDrawer(
modifier = modifier =
modifier modifier
.focusRequester(drawerFocusRequester) .focusRequester(drawerFocusRequester),
.background(MaterialTheme.colorScheme.background),
drawerState = drawerState, drawerState = drawerState,
drawerContent = { drawerContent = {
ProvideTextStyle(MaterialTheme.typography.labelMedium) { ProvideTextStyle(MaterialTheme.typography.labelMedium) {

View file

@ -12,14 +12,14 @@ import kotlinx.coroutines.flow.debounce
class ControllerViewState internal constructor( class ControllerViewState internal constructor(
@param:IntRange(from = 0) @param:IntRange(from = 0)
private val hideMilliseconds: Int, private val hideMilliseconds: Long,
val controlsEnabled: Boolean, val controlsEnabled: Boolean,
) { ) {
private val channel = Channel<Int>(CONFLATED) private val channel = Channel<Long>(CONFLATED)
private var _controlsVisible by mutableStateOf(false) private var _controlsVisible by mutableStateOf(false)
val controlsVisible get() = _controlsVisible val controlsVisible get() = _controlsVisible
fun showControls(milliseconds: Int = hideMilliseconds) { fun showControls(milliseconds: Long = hideMilliseconds) {
if (controlsEnabled) { if (controlsEnabled) {
_controlsVisible = true _controlsVisible = true
} }
@ -30,7 +30,7 @@ class ControllerViewState internal constructor(
_controlsVisible = false _controlsVisible = false
} }
fun pulseControls(milliseconds: Int = hideMilliseconds) { fun pulseControls(milliseconds: Long = hideMilliseconds) {
channel.trySend(milliseconds) channel.trySend(milliseconds)
} }
@ -38,7 +38,7 @@ class ControllerViewState internal constructor(
suspend fun observe() { suspend fun observe() {
channel channel
.consumeAsFlow() .consumeAsFlow()
.debounce { it.toLong() } .debounce { it }
.collect { .collect {
_controlsVisible = false _controlsVisible = false
} }

View file

@ -98,7 +98,7 @@ fun PlaybackContent(
val controllerViewState = val controllerViewState =
remember { remember {
ControllerViewState( ControllerViewState(
5_000, preferences.appPreferences.playbackPreferences.controllerTimeoutMs,
true, true,
) )
}.also { }.also {
@ -124,6 +124,8 @@ fun PlaybackContent(
player = player, player = player,
controlsEnabled = true, controlsEnabled = true,
skipWithLeftRight = true, skipWithLeftRight = true,
seekForward = preferences.appPreferences.playbackPreferences.skipForwardMs.milliseconds,
seekBack = preferences.appPreferences.playbackPreferences.skipBackMs.milliseconds,
controllerViewState = controllerViewState, controllerViewState = controllerViewState,
updateSkipIndicator = updateSkipIndicator, updateSkipIndicator = updateSkipIndicator,
) )
@ -199,6 +201,8 @@ fun PlaybackContent(
previousEnabled = previousState.isEnabled, previousEnabled = previousState.isEnabled,
nextEnabled = nextState.isEnabled, nextEnabled = nextState.isEnabled,
seekEnabled = true, seekEnabled = true,
seekForward = preferences.appPreferences.playbackPreferences.skipForwardMs.milliseconds,
seekBack = preferences.appPreferences.playbackPreferences.skipBackMs.milliseconds,
onPlaybackActionClick = { onPlaybackActionClick = {
when (it) { when (it) {
is PlaybackAction.PlaybackSpeed -> { is PlaybackAction.PlaybackSpeed -> {

View file

@ -58,12 +58,15 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import com.github.damontecres.dolphin.R import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.ui.AppColors import com.github.damontecres.dolphin.ui.AppColors
import com.github.damontecres.dolphin.ui.seekBack
import com.github.damontecres.dolphin.ui.seekForward
import com.github.damontecres.dolphin.ui.tryRequestFocus import com.github.damontecres.dolphin.ui.tryRequestFocus
import com.github.damontecres.dolphin.util.ExceptionHandler import com.github.damontecres.dolphin.util.ExceptionHandler
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import kotlin.time.Duration
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
sealed interface PlaybackAction { sealed interface PlaybackAction {
@ -110,6 +113,8 @@ fun PlaybackControls(
playbackSpeed: Float, playbackSpeed: Float,
scale: ContentScale, scale: ContentScale,
seekBarIntervals: Int, seekBarIntervals: Int,
seekBack: Duration,
seekForward: Duration,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
initialFocusRequester: FocusRequester = remember { FocusRequester() }, initialFocusRequester: FocusRequester = remember { FocusRequester() },
seekBarInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() }, seekBarInteractionSource: MutableInteractionSource = remember { MutableInteractionSource() },
@ -127,7 +132,7 @@ fun PlaybackControls(
scope.launch(ExceptionHandler()) { scope.launch(ExceptionHandler()) {
bringIntoViewRequester.bringIntoView() bringIntoViewRequester.bringIntoView()
} }
controllerViewState.pulseControls(Int.MAX_VALUE) controllerViewState.pulseControls(Long.MAX_VALUE)
} }
LaunchedEffect(controllerViewState.controlsVisible) { LaunchedEffect(controllerViewState.controlsVisible) {
if (controllerViewState.controlsVisible) { if (controllerViewState.controlsVisible) {
@ -170,6 +175,8 @@ fun PlaybackControls(
showPlay = showPlay, showPlay = showPlay,
previousEnabled = previousEnabled, previousEnabled = previousEnabled,
nextEnabled = nextEnabled, nextEnabled = nextEnabled,
seekBack = seekBack,
seekForward = seekForward,
modifier = Modifier.align(Alignment.Center), modifier = Modifier.align(Alignment.Center),
) )
RightPlaybackButtons( RightPlaybackButtons(
@ -430,6 +437,8 @@ fun PlaybackButtons(
showPlay: Boolean, showPlay: Boolean,
previousEnabled: Boolean, previousEnabled: Boolean,
nextEnabled: Boolean, nextEnabled: Boolean,
seekBack: Duration,
seekForward: Duration,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
Row( Row(
@ -449,7 +458,7 @@ fun PlaybackButtons(
iconRes = R.drawable.baseline_fast_rewind_24, iconRes = R.drawable.baseline_fast_rewind_24,
onClick = { onClick = {
onControllerInteraction.invoke() onControllerInteraction.invoke()
player.seekBack() player.seekBack(seekBack)
}, },
onControllerInteraction = onControllerInteraction, onControllerInteraction = onControllerInteraction,
) )
@ -466,7 +475,7 @@ fun PlaybackButtons(
iconRes = R.drawable.baseline_fast_forward_24, iconRes = R.drawable.baseline_fast_forward_24,
onClick = { onClick = {
onControllerInteraction.invoke() onControllerInteraction.invoke()
player.seekForward() player.seekForward(seekForward)
}, },
onControllerInteraction = onControllerInteraction, onControllerInteraction = onControllerInteraction,
) )

View file

@ -7,11 +7,16 @@ import androidx.compose.ui.input.key.key
import androidx.compose.ui.input.key.type import androidx.compose.ui.input.key.type
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.media3.common.util.Util import androidx.media3.common.util.Util
import com.github.damontecres.dolphin.ui.seekBack
import com.github.damontecres.dolphin.ui.seekForward
import kotlin.time.Duration
class PlaybackKeyHandler( class PlaybackKeyHandler(
private val player: Player, private val player: Player,
private val controlsEnabled: Boolean, private val controlsEnabled: Boolean,
private val skipWithLeftRight: Boolean, private val skipWithLeftRight: Boolean,
private val seekBack: Duration,
private val seekForward: Duration,
private val controllerViewState: ControllerViewState, private val controllerViewState: ControllerViewState,
private val updateSkipIndicator: (Long) -> Unit, private val updateSkipIndicator: (Long) -> Unit,
) { ) {
@ -24,11 +29,11 @@ class PlaybackKeyHandler(
} else if (isDpad(it)) { } else if (isDpad(it)) {
if (!controllerViewState.controlsVisible) { if (!controllerViewState.controlsVisible) {
if (skipWithLeftRight && it.key == Key.DirectionLeft) { if (skipWithLeftRight && it.key == Key.DirectionLeft) {
updateSkipIndicator(-player.seekBackIncrement) updateSkipIndicator(-seekBack.inWholeMilliseconds)
player.seekBack() player.seekBack(seekBack)
} else if (skipWithLeftRight && it.key == Key.DirectionRight) { } else if (skipWithLeftRight && it.key == Key.DirectionRight) {
player.seekForward() player.seekForward(seekForward)
updateSkipIndicator(player.seekForwardIncrement) updateSkipIndicator(seekForward.inWholeMilliseconds)
} else { } else {
controllerViewState.showControls() controllerViewState.showControls()
} }
@ -54,13 +59,13 @@ class PlaybackKeyHandler(
} }
Key.MediaFastForward, Key.MediaSkipForward -> { Key.MediaFastForward, Key.MediaSkipForward -> {
player.seekForward() player.seekForward(seekForward)
updateSkipIndicator(player.seekForwardIncrement) updateSkipIndicator(seekForward.inWholeMilliseconds)
} }
Key.MediaRewind, Key.MediaSkipBackward -> { Key.MediaRewind, Key.MediaSkipBackward -> {
player.seekBack() player.seekBack(seekBack)
updateSkipIndicator(-player.seekBackIncrement) updateSkipIndicator(-seekBack.inWholeMilliseconds)
} }
Key.MediaNext -> if (player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)) player.seekToNext() Key.MediaNext -> if (player.isCommandAvailable(Player.COMMAND_SEEK_TO_NEXT)) player.seekToNext()

View file

@ -18,6 +18,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.media3.common.Player import androidx.media3.common.Player
import androidx.tv.material3.Text import androidx.tv.material3.Text
import org.jellyfin.sdk.model.api.TrickplayInfo import org.jellyfin.sdk.model.api.TrickplayInfo
import kotlin.time.Duration
@Composable @Composable
fun PlaybackOverlay( fun PlaybackOverlay(
@ -29,6 +30,8 @@ fun PlaybackOverlay(
previousEnabled: Boolean, previousEnabled: Boolean,
nextEnabled: Boolean, nextEnabled: Boolean,
seekEnabled: Boolean, seekEnabled: Boolean,
seekBack: Duration,
seekForward: Duration,
onPlaybackActionClick: (PlaybackAction) -> Unit, onPlaybackActionClick: (PlaybackAction) -> Unit,
onSeekBarChange: (Long) -> Unit, onSeekBarChange: (Long) -> Unit,
showDebugInfo: Boolean, showDebugInfo: Boolean,
@ -121,6 +124,8 @@ fun PlaybackOverlay(
playbackSpeed = playbackSpeed, playbackSpeed = playbackSpeed,
scale = scale, scale = scale,
seekBarIntervals = 16, seekBarIntervals = 16,
seekBack = seekBack,
seekForward = seekForward,
) )
} }
} }

View file

@ -38,6 +38,14 @@ import androidx.tv.material3.Switch
import androidx.tv.material3.Text import androidx.tv.material3.Text
import androidx.tv.material3.surfaceColorAtElevation import androidx.tv.material3.surfaceColorAtElevation
import com.github.damontecres.dolphin.R import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.preferences.AppChoicePreference
import com.github.damontecres.dolphin.preferences.AppClickablePreference
import com.github.damontecres.dolphin.preferences.AppDestinationPreference
import com.github.damontecres.dolphin.preferences.AppMultiChoicePreference
import com.github.damontecres.dolphin.preferences.AppPreference
import com.github.damontecres.dolphin.preferences.AppSliderPreference
import com.github.damontecres.dolphin.preferences.AppStringPreference
import com.github.damontecres.dolphin.preferences.AppSwitchPreference
import com.github.damontecres.dolphin.ui.components.DialogItem import com.github.damontecres.dolphin.ui.components.DialogItem
import com.github.damontecres.dolphin.ui.components.DialogParams import com.github.damontecres.dolphin.ui.components.DialogParams
import com.github.damontecres.dolphin.ui.components.DialogPopup import com.github.damontecres.dolphin.ui.components.DialogPopup

View file

@ -1,6 +1,7 @@
package com.github.damontecres.dolphin.ui.preferences package com.github.damontecres.dolphin.ui.preferences
import androidx.annotation.StringRes import androidx.annotation.StringRes
import com.github.damontecres.dolphin.preferences.AppPreference
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
/** /**

View file

@ -38,7 +38,11 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import androidx.tv.material3.surfaceColorAtElevation import androidx.tv.material3.surfaceColorAtElevation
import com.github.damontecres.dolphin.R import com.github.damontecres.dolphin.R
import com.github.damontecres.dolphin.preferences.AppPreference
import com.github.damontecres.dolphin.preferences.AppPreferences import com.github.damontecres.dolphin.preferences.AppPreferences
import com.github.damontecres.dolphin.preferences.advancedPreferences
import com.github.damontecres.dolphin.preferences.basicPreferences
import com.github.damontecres.dolphin.preferences.uiPreferences
import com.github.damontecres.dolphin.ui.ifElse import com.github.damontecres.dolphin.ui.ifElse
import com.github.damontecres.dolphin.ui.nav.NavigationManager import com.github.damontecres.dolphin.ui.nav.NavigationManager
import com.github.damontecres.dolphin.ui.playOnClickSound import com.github.damontecres.dolphin.ui.playOnClickSound
@ -47,24 +51,6 @@ import com.github.damontecres.dolphin.ui.tryRequestFocus
import com.github.damontecres.dolphin.util.ExceptionHandler import com.github.damontecres.dolphin.util.ExceptionHandler
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
val basicPreferences =
listOf(
PreferenceGroup(
title = R.string.basic_interface,
preferences =
listOf(
AppPreference.SkipForward,
AppPreference.SkipBack,
AppPreference.ControllerTimeout,
AppPreference.SeekBarSteps,
),
),
)
val uiPreferences = listOf<PreferenceGroup>()
val advancedPreferences = listOf<PreferenceGroup>()
data class Release( data class Release(
val version: String, val version: String,
) )

View file

@ -23,6 +23,7 @@ import androidx.compose.ui.unit.dp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.ProvideTextStyle import androidx.tv.material3.ProvideTextStyle
import androidx.tv.material3.contentColorFor import androidx.tv.material3.contentColorFor
import com.github.damontecres.dolphin.preferences.AppSliderPreference
import com.github.damontecres.dolphin.ui.components.SliderBar import com.github.damontecres.dolphin.ui.components.SliderBar
@Composable @Composable

View file

@ -10,10 +10,16 @@ message PlaybackPreferences {
int32 seek_bar_steps = 4; int32 seek_bar_steps = 4;
} }
message HomePagePreferences{
int32 max_items_per_row = 1;
bool enable_rewatching_next_up = 2;
}
message AppPreferences { message AppPreferences {
// The currently signed in server and user IDs, mostly for restoring a session // The currently signed in server and user IDs, mostly for restoring a session
string current_server_id = 1; string current_server_id = 1;
string current_user_id = 2; string current_user_id = 2;
PlaybackPreferences playback_preferences = 3; PlaybackPreferences playback_preferences = 3;
HomePagePreferences home_page_preferences = 4;
} }

View file

@ -36,4 +36,6 @@
<string name="skip_back_preference">Skip back</string> <string name="skip_back_preference">Skip back</string>
<string name="hide_controller_timeout">Hide playback controls</string> <string name="hide_controller_timeout">Hide playback controls</string>
<string name="seek_bar_steps">Seek bar steps</string> <string name="seek_bar_steps">Seek bar steps</string>
<string name="max_homepage_items">Max items on home page rows</string>
<string name="rewatch_next_up">Enable rewatching in next up</string>
</resources> </resources>