Preferences update, play theme song volume

This commit is contained in:
Damontecres 2025-10-06 12:34:27 -04:00
parent 3a255fcd8a
commit 628771211d
No known key found for this signature in database
8 changed files with 206 additions and 37 deletions

View file

@ -176,15 +176,16 @@ sealed interface AppPreference<T> {
) )
val PlayThemeMusic = val PlayThemeMusic =
AppSwitchPreference( AppChoicePreference<ThemeSongVolume>(
title = R.string.play_theme_music, title = R.string.play_theme_music,
defaultValue = true, defaultValue = ThemeSongVolume.MEDIUM,
getter = { it.interfacePreferences.playThemeSongs }, getter = { it.interfacePreferences.playThemeSongs },
setter = { prefs, value -> setter = { prefs, value ->
prefs.updateInterfacePreferences { playThemeSongs = value } prefs.updateInterfacePreferences { playThemeSongs = value }
}, },
summaryOn = R.string.enabled, displayValues = R.array.theme_song_volume,
summaryOff = R.string.disabled, indexToValue = { ThemeSongVolume.forNumber(it) },
valueToIndex = { it.number },
) )
val PlaybackDebugInfo = val PlaybackDebugInfo =
@ -199,6 +200,12 @@ sealed interface AppPreference<T> {
summaryOff = R.string.hide, summaryOff = R.string.hide,
) )
val InstalledVersion =
AppClickablePreference(
title = R.string.installed_version,
getter = { },
setter = { prefs, _ -> prefs },
)
// val AutoCheckForUpdates = // val AutoCheckForUpdates =
// AppSwitchPreference( // AppSwitchPreference(
// title = R.string.check_for_updates, // title = R.string.check_for_updates,
@ -237,17 +244,29 @@ val basicPreferences =
title = R.string.basic_interface, title = R.string.basic_interface,
preferences = preferences =
listOf( listOf(
AppPreference.SkipForward,
AppPreference.SkipBack,
AppPreference.ControllerTimeout,
AppPreference.SeekBarSteps,
AppPreference.HomePageItems, AppPreference.HomePageItems,
AppPreference.RewatchNextUp, AppPreference.RewatchNextUp,
AppPreference.PlayThemeMusic, AppPreference.PlayThemeMusic,
AppPreference.OssLicenseInfo, ),
),
PreferenceGroup(
title = R.string.playback,
preferences =
listOf(
AppPreference.SkipForward,
AppPreference.SkipBack,
AppPreference.ControllerTimeout,
AppPreference.PlaybackDebugInfo, AppPreference.PlaybackDebugInfo,
), ),
), ),
PreferenceGroup(
title = R.string.about,
preferences =
listOf(
AppPreference.InstalledVersion,
AppPreference.OssLicenseInfo,
),
),
) )
val uiPreferences = listOf<PreferenceGroup>() val uiPreferences = listOf<PreferenceGroup>()

View file

@ -66,7 +66,7 @@ fun SeriesDetails(
viewModel: SeriesViewModel = hiltViewModel(), viewModel: SeriesViewModel = hiltViewModel(),
) { ) {
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.init(destination.itemId, destination.item, null, null) viewModel.init(preferences, destination.itemId, destination.item, null, null)
} }
val loading by viewModel.loading.observeAsState(LoadingState.Loading) val loading by viewModel.loading.observeAsState(LoadingState.Loading)

View file

@ -14,6 +14,8 @@ import com.github.damontecres.dolphin.data.model.BaseItem
import com.github.damontecres.dolphin.data.model.Person import com.github.damontecres.dolphin.data.model.Person
import com.github.damontecres.dolphin.data.model.Video import com.github.damontecres.dolphin.data.model.Video
import com.github.damontecres.dolphin.hilt.AuthOkHttpClient import com.github.damontecres.dolphin.hilt.AuthOkHttpClient
import com.github.damontecres.dolphin.preferences.ThemeSongVolume
import com.github.damontecres.dolphin.preferences.UserPreferences
import com.github.damontecres.dolphin.ui.letNotEmpty import com.github.damontecres.dolphin.ui.letNotEmpty
import com.github.damontecres.dolphin.util.ApiRequestPager import com.github.damontecres.dolphin.util.ApiRequestPager
import com.github.damontecres.dolphin.util.ExceptionHandler import com.github.damontecres.dolphin.util.ExceptionHandler
@ -59,6 +61,7 @@ class SeriesViewModel
val people = MutableLiveData<List<Person>>(listOf()) val people = MutableLiveData<List<Person>>(listOf())
fun init( fun init(
prefs: UserPreferences,
itemId: UUID, itemId: UUID,
potential: BaseItem?, potential: BaseItem?,
season: Int?, season: Int?,
@ -89,7 +92,7 @@ class SeriesViewModel
people.map { Person.fromDto(it, api) } people.map { Person.fromDto(it, api) }
}.orEmpty() }.orEmpty()
} }
maybePlayThemeSong() maybePlayThemeSong(prefs.appPreferences.interfacePreferences.playThemeSongs)
} else { } else {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
seasons.value = ItemListAndMapping.empty() seasons.value = ItemListAndMapping.empty()
@ -101,8 +104,19 @@ class SeriesViewModel
} }
@OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
private fun maybePlayThemeSong() { private fun maybePlayThemeSong(playThemeSongs: ThemeSongVolume) {
// TODO user preference to enable/disable this val volume =
when (playThemeSongs) {
ThemeSongVolume.UNRECOGNIZED,
ThemeSongVolume.DISABLED,
-> return
ThemeSongVolume.LOWEST -> .1f
ThemeSongVolume.LOW -> .25f
ThemeSongVolume.MEDIUM -> .5f
ThemeSongVolume.HIGH -> .75f
ThemeSongVolume.HIGHEST -> 1f
}
viewModelScope.launch(ExceptionHandler()) { viewModelScope.launch(ExceptionHandler()) {
val themeSongs = api.libraryApi.getThemeSongs(seriesId).content val themeSongs = api.libraryApi.getThemeSongs(seriesId).content
themeSongs.items.firstOrNull()?.let { theme -> themeSongs.items.firstOrNull()?.let { theme ->
@ -123,7 +137,7 @@ class SeriesViewModel
), ),
).build() ).build()
.apply { .apply {
volume = .1f this.volume = volume
playWhenReady = true playWhenReady = true
this@SeriesViewModel.player = this this@SeriesViewModel.player = this
} }

View file

@ -57,6 +57,7 @@ fun SeriesOverview(
OneTimeLaunchedEffect { OneTimeLaunchedEffect {
Timber.v("SeriesDetailParent: itemId=${destination.itemId}, initialSeasonEpisode=$initialSeasonEpisode") Timber.v("SeriesDetailParent: itemId=${destination.itemId}, initialSeasonEpisode=$initialSeasonEpisode")
viewModel.init( viewModel.init(
preferences,
destination.itemId, destination.itemId,
destination.item, destination.item,
initialSeasonEpisode?.season, initialSeasonEpisode?.season,

View file

@ -20,6 +20,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
@ -76,7 +77,8 @@ fun PreferencesContent(
} }
val movementSounds = true val movementSounds = true
val installedVersion = remember { "v1.0.0" } val installedVersion =
remember { context.packageManager.getPackageInfo(context.packageName, 0).versionName }
var updateVersion by remember { mutableStateOf<Release?>(null) } var updateVersion by remember { mutableStateOf<Release?>(null) }
val updateAvailable = false val updateAvailable = false
// remember(updateVersion) { updateVersion?.version?.isGreaterThan(installedVersion) == true } // remember(updateVersion) { updateVersion?.version?.isGreaterThan(installedVersion) == true }
@ -188,27 +190,27 @@ fun PreferencesContent(
} }
} }
when (pref) { when (pref) {
// AppPreference.InstalledVersion -> { AppPreference.InstalledVersion -> {
// var clickCount by remember { mutableIntStateOf(0) } var clickCount by remember { mutableIntStateOf(0) }
// ClickPreference( ClickPreference(
// title = stringResource(R.string.installed_version), title = stringResource(R.string.installed_version),
// onClick = { onClick = {
// if (movementSounds) playOnClickSound(context) if (movementSounds) playOnClickSound(context)
// if (clickCount++ >= 2) { if (clickCount++ >= 2) {
// clickCount = 0 clickCount = 0
// // navigationManager.navigateTo(Destination.Debug) // navigationManager.navigateTo(Destination.Debug)
// } }
// }, },
// summary = installedVersion.toString(), summary = installedVersion.toString(),
// interactionSource = interactionSource, interactionSource = interactionSource,
// modifier = modifier =
// Modifier Modifier
// .ifElse( .ifElse(
// groupIndex == focusedIndex.first && prefIndex == focusedIndex.second, groupIndex == focusedIndex.first && prefIndex == focusedIndex.second,
// Modifier.focusRequester(focusRequester), Modifier.focusRequester(focusRequester),
// ), ),
// ) )
// } }
// AppPreference.Update -> { // AppPreference.Update -> {
// ClickPreference( // ClickPreference(

View file

@ -0,0 +1,112 @@
package com.github.damontecres.dolphin.util
import kotlinx.serialization.Serializable
@Serializable
data class Version(
val major: Int,
val minor: Int,
val patch: Int,
val numCommits: Int? = null,
val hash: String? = null,
) {
/**
* Is this version at least the given version
*/
fun isAtLeast(version: Version): Boolean {
if (this.major > version.major) {
return true
} else if (this.major == version.major) {
if (this.minor > version.minor) {
return true
} else if (this.minor == version.minor) {
if (this.patch > version.patch) {
return true
} else if (this.patch == version.patch) {
if (this.compareNumCommits(version) >= 0) {
return true
}
}
}
}
return false
}
/**
* Is this greater than the given version (and not equal to!)
*/
fun isGreaterThan(version: Version): Boolean {
if (this.major > version.major) {
return true
} else if (this.major == version.major) {
if (this.minor > version.minor) {
return true
} else if (this.minor == version.minor) {
if (this.patch > version.patch) {
return true
} else if (this.patch == version.patch) {
if (this.compareNumCommits(version) > 0) {
return true
}
}
}
}
return false
}
/**
* Is this less than the given version (and not equal to!)
*/
fun isLessThan(version: Version): Boolean = this != version && isEqualOrBefore(version)
/**
* Is this equal to or before the specified version
*/
fun isEqualOrBefore(version: Version): Boolean = !isGreaterThan(version)
private fun compareNumCommits(version: Version): Int = (this.numCommits ?: 0) - (version.numCommits ?: 0)
override fun toString(): String =
if (numCommits != null && hash != null) {
"v$major.$minor.$patch-$numCommits-g$hash"
} else {
"v$major.$minor.$patch"
}
companion object {
private val VERSION_REGEX = Regex("v?(\\d+)\\.(\\d+)\\.(\\d+)(-(\\d+)-g([a-zA-Z0-9]+))?")
/**
* Parse a version string throwing if it is invalid
*/
fun fromString(version: String): Version {
val v = tryFromString(version)
if (v == null) {
throw IllegalArgumentException(version)
} else {
return v
}
}
/**
* Attempt to parse a version string or else return null
*/
fun tryFromString(version: String?): Version? {
if (version == null) {
return null
}
val m = VERSION_REGEX.matchEntire(version)
return if (m == null) {
null
} else {
val major = m.groups[1]!!.value.toInt()
val minor = m.groups[2]!!.value.toInt()
val patch = m.groups[3]!!.value.toInt()
// group 4 is the optional commit info
val numCommits = m.groups[5]?.value?.toInt()
val hash = m.groups[6]?.value
Version(major, minor, patch, numCommits, hash)
}
}
}
}

View file

@ -16,8 +16,17 @@ message HomePagePreferences{
bool enable_rewatching_next_up = 2; bool enable_rewatching_next_up = 2;
} }
enum ThemeSongVolume {
DISABLED = 0;
LOWEST = 1;
LOW = 2;
MEDIUM = 3;
HIGH = 4;
HIGHEST = 5;
}
message InterfacePreferences { message InterfacePreferences {
bool play_theme_songs = 1; ThemeSongVolume play_theme_songs = 1;
} }
message AppPreferences { message AppPreferences {

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="theme_song_volume">
<item>Disabled</item>
<item>Lowest</item>
<item>Low</item>
<item>Medium</item>
<item>High</item>
<item>Full</item>
</string-array>
</resources>