Adds option to dim the screensaver (#1834)
## Description Adds a new sceensaver setting to add dimming. A larger value is darker This works with or without the in-app screensaver. If dimming is enabled, the screen be will be dimmed after inactivity. If both dimming and the in-app screensaver are enabled, the screensaver will show dimmed after inactivity. ### Related issues Closes #1179 ### Testing Emulator ## Screenshots N/A, it's just darker ## AI or LLM usage None
This commit is contained in:
parent
197af604d6
commit
c4264a3263
8 changed files with 144 additions and 9 deletions
|
|
@ -17,6 +17,7 @@ import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
|
@ -136,12 +137,26 @@ fun MainContent(
|
||||||
val screenSaverState by screensaverService.state.collectAsState()
|
val screenSaverState by screensaverService.state.collectAsState()
|
||||||
if (screenSaverState.enabled || screenSaverState.enabledTemp) {
|
if (screenSaverState.enabled || screenSaverState.enabledTemp) {
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
screenSaverState.show,
|
visible = screenSaverState.show,
|
||||||
Modifier.fillMaxSize(),
|
enter = ScreensaverService.enterAnimation,
|
||||||
|
exit = ScreensaverService.exitAnimation,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
) {
|
) {
|
||||||
AppScreensaver(userPreferences.appPreferences, Modifier.fillMaxSize())
|
AppScreensaver(userPreferences.appPreferences, Modifier.fillMaxSize())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = screenSaverState.showDim || (screenSaverState.dimEnabled && screenSaverState.show),
|
||||||
|
enter = ScreensaverService.enterAnimation,
|
||||||
|
exit = ScreensaverService.exitAnimation,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
val alpha =
|
||||||
|
userPreferences.appPreferences.interfacePreferences.screensaverPreference.dimPercent / 100f
|
||||||
|
Box(
|
||||||
|
Modifier.background(Color.Black.copy(alpha = alpha)),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.datastore.core.DataStore
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
|
@ -94,6 +95,14 @@ class WholphinDreamService :
|
||||||
preferencesDataStore.data.collectLatest { prefs = it }
|
preferencesDataStore.data.collectLatest { prefs = it }
|
||||||
}
|
}
|
||||||
prefs?.let { prefs ->
|
prefs?.let { prefs ->
|
||||||
|
val alpha =
|
||||||
|
prefs.interfacePreferences.screensaverPreference.run {
|
||||||
|
if (dimEnabled) {
|
||||||
|
dimPercent / 100f
|
||||||
|
} else {
|
||||||
|
1f
|
||||||
|
}
|
||||||
|
}
|
||||||
CoilConfig(
|
CoilConfig(
|
||||||
prefs = prefs,
|
prefs = prefs,
|
||||||
okHttpClient = okHttpClient,
|
okHttpClient = okHttpClient,
|
||||||
|
|
@ -109,7 +118,10 @@ class WholphinDreamService :
|
||||||
showClock = screensaverPrefs.showClock,
|
showClock = screensaverPrefs.showClock,
|
||||||
duration = screensaverPrefs.duration.milliseconds,
|
duration = screensaverPrefs.duration.milliseconds,
|
||||||
animate = screensaverPrefs.animate,
|
animate = screensaverPrefs.animate,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.alpha(alpha = alpha),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1338,6 +1338,14 @@ val screensaverPreferences =
|
||||||
ScreensaverPreference.Start,
|
ScreensaverPreference.Start,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
PreferenceGroup(
|
||||||
|
title = R.string.dim_screen,
|
||||||
|
preferences =
|
||||||
|
listOf(
|
||||||
|
ScreensaverPreference.DimToggle,
|
||||||
|
ScreensaverPreference.DimPercentage,
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
data class AppSwitchPreference<Pref>(
|
data class AppSwitchPreference<Pref>(
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,37 @@ object ScreensaverPreference {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val DimToggle =
|
||||||
|
AppSwitchPreference<AppPreferences>(
|
||||||
|
title = R.string.dim_after_delay,
|
||||||
|
defaultValue = AppPreference.ShowClock.defaultValue,
|
||||||
|
getter = { it.interfacePreferences.screensaverPreference.dimEnabled },
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updateScreensaverPreferences { dimEnabled = value }
|
||||||
|
},
|
||||||
|
summaryOn = R.string.yes,
|
||||||
|
summaryOff = R.string.no,
|
||||||
|
)
|
||||||
|
|
||||||
|
val DimPercentage =
|
||||||
|
AppSliderPreference<AppPreferences>(
|
||||||
|
title = R.string.dim_percentage,
|
||||||
|
defaultValue = 75,
|
||||||
|
min = 0,
|
||||||
|
max = 95,
|
||||||
|
interval = 5,
|
||||||
|
getter = {
|
||||||
|
it.interfacePreferences.screensaverPreference.dimPercent
|
||||||
|
.toLong()
|
||||||
|
},
|
||||||
|
setter = { prefs, value ->
|
||||||
|
prefs.updateScreensaverPreferences { dimPercent = value.toInt() }
|
||||||
|
},
|
||||||
|
summarizer = { value ->
|
||||||
|
value?.let { "$value%" }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
val Start =
|
val Start =
|
||||||
AppClickablePreference<AppPreferences>(
|
AppClickablePreference<AppPreferences>(
|
||||||
title = R.string.start_screensaver,
|
title = R.string.start_screensaver,
|
||||||
|
|
|
||||||
|
|
@ -418,5 +418,13 @@ class AppUpgradeHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previous.isEqualOrBefore(Version.fromString("1.0.6-0-g0"))) {
|
||||||
|
appPreferences.updateData {
|
||||||
|
it.updateScreensaverPreferences {
|
||||||
|
dimPercent = ScreensaverPreference.DimPercentage.defaultValue.toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
package com.github.damontecres.wholphin.services
|
package com.github.damontecres.wholphin.services
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import coil3.imageLoader
|
import coil3.imageLoader
|
||||||
import coil3.request.ImageRequest
|
import coil3.request.ImageRequest
|
||||||
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
|
import com.github.damontecres.wholphin.services.hilt.DefaultCoroutineScope
|
||||||
|
|
@ -57,6 +60,7 @@ class ScreensaverService
|
||||||
val keepScreenOn = MutableStateFlow(false)
|
val keepScreenOn = MutableStateFlow(false)
|
||||||
|
|
||||||
private var waitJob: Job? = null
|
private var waitJob: Job? = null
|
||||||
|
private var dimJob: Job? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
userPreferencesService.flow
|
userPreferencesService.flow
|
||||||
|
|
@ -65,7 +69,14 @@ class ScreensaverService
|
||||||
val enabled =
|
val enabled =
|
||||||
prefs.appPreferences.interfacePreferences.screensaverPreference.enabled
|
prefs.appPreferences.interfacePreferences.screensaverPreference.enabled
|
||||||
keepScreenOnInternal(enabled)
|
keepScreenOnInternal(enabled)
|
||||||
ScreensaverState(enabled, false, false, false)
|
ScreensaverState(
|
||||||
|
enabled = enabled,
|
||||||
|
enabledTemp = false,
|
||||||
|
active = false,
|
||||||
|
paused = false,
|
||||||
|
dimEnabled = prefs.appPreferences.interfacePreferences.screensaverPreference.dimEnabled,
|
||||||
|
dimActive = false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.launchIn(scope)
|
}.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
@ -74,12 +85,16 @@ class ScreensaverService
|
||||||
* Reset the timer before showing the in-app screensaver
|
* Reset the timer before showing the in-app screensaver
|
||||||
*/
|
*/
|
||||||
fun pulse() {
|
fun pulse() {
|
||||||
|
Timber.v("state=%s", _state.value)
|
||||||
waitJob?.cancel()
|
waitJob?.cancel()
|
||||||
if (_state.value.enabled) {
|
if (_state.value.enabled) {
|
||||||
// Timber.v("pulse")
|
// Timber.v("pulse")
|
||||||
_state.update {
|
_state.update {
|
||||||
if (!it.active) {
|
if (!it.active) {
|
||||||
it.copy(active = false)
|
it.copy(
|
||||||
|
active = false,
|
||||||
|
dimActive = false,
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
it
|
it
|
||||||
}
|
}
|
||||||
|
|
@ -94,7 +109,30 @@ class ScreensaverService
|
||||||
.appPreferences.interfacePreferences.screensaverPreference.startDelay.milliseconds
|
.appPreferences.interfacePreferences.screensaverPreference.startDelay.milliseconds
|
||||||
delay(startDelay)
|
delay(startDelay)
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(active = true)
|
it.copy(
|
||||||
|
active = true,
|
||||||
|
dimActive = it.dimEnabled,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dimJob?.cancel()
|
||||||
|
if (_state.value.dimEnabled) {
|
||||||
|
_state.update {
|
||||||
|
it.copy(dimActive = false)
|
||||||
|
}
|
||||||
|
if (!_state.value.paused) {
|
||||||
|
dimJob =
|
||||||
|
scope.launch(ExceptionHandler()) {
|
||||||
|
val startDelay =
|
||||||
|
userPreferencesService
|
||||||
|
.getCurrent()
|
||||||
|
.appPreferences.interfacePreferences.screensaverPreference.startDelay.milliseconds
|
||||||
|
delay(startDelay)
|
||||||
|
Timber.v("state dim=%s", _state.value)
|
||||||
|
_state.update {
|
||||||
|
it.copy(dimActive = !_state.value.paused)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -109,6 +147,7 @@ class ScreensaverService
|
||||||
it.copy(
|
it.copy(
|
||||||
enabledTemp = true,
|
enabledTemp = true,
|
||||||
active = true,
|
active = true,
|
||||||
|
dimActive = true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,9 +160,13 @@ class ScreensaverService
|
||||||
it.copy(
|
it.copy(
|
||||||
enabledTemp = false,
|
enabledTemp = false,
|
||||||
active = false,
|
active = false,
|
||||||
|
dimActive = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (cancelJob) waitJob?.cancel()
|
if (cancelJob) {
|
||||||
|
waitJob?.cancel()
|
||||||
|
dimJob?.cancel()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -131,12 +174,16 @@ class ScreensaverService
|
||||||
*/
|
*/
|
||||||
fun keepScreenOn(keep: Boolean) {
|
fun keepScreenOn(keep: Boolean) {
|
||||||
scope.launchDefault {
|
scope.launchDefault {
|
||||||
val screensaverEnabled = _state.value.enabled
|
val screensaverEnabled = _state.value.run { enabled || dimEnabled }
|
||||||
Timber.d("Keep screen on: %s, screensaverEnabled=%s", keep, screensaverEnabled)
|
Timber.d("Keep screen on: %s, screensaverEnabled=%s", keep, screensaverEnabled)
|
||||||
if (screensaverEnabled) {
|
if (screensaverEnabled) {
|
||||||
// Page is requesting to keep screen on, so we don't wait to show the screensaver
|
// Page is requesting to keep screen on, so we don't wait to show the screensaver
|
||||||
_state.update {
|
_state.update {
|
||||||
it.copy(active = false, paused = keep)
|
it.copy(
|
||||||
|
active = false,
|
||||||
|
dimActive = false,
|
||||||
|
paused = keep,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
if (!keep) {
|
if (!keep) {
|
||||||
pulse()
|
pulse()
|
||||||
|
|
@ -245,6 +292,11 @@ class ScreensaverService
|
||||||
)
|
)
|
||||||
return ApiRequestPager(api, request, GetItemsRequestHandler, scope).init()
|
return ApiRequestPager(api, request, GetItemsRequestHandler, scope).init()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val enterAnimation = fadeIn(animationSpec = tween(durationMillis = 1000))
|
||||||
|
val exitAnimation = fadeOut(animationSpec = tween(durationMillis = 500))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ScreensaverState(
|
data class ScreensaverState(
|
||||||
|
|
@ -252,6 +304,10 @@ data class ScreensaverState(
|
||||||
val enabledTemp: Boolean,
|
val enabledTemp: Boolean,
|
||||||
val active: Boolean,
|
val active: Boolean,
|
||||||
val paused: Boolean,
|
val paused: Boolean,
|
||||||
|
val dimEnabled: Boolean = false,
|
||||||
|
val dimActive: Boolean = false,
|
||||||
) {
|
) {
|
||||||
val show get() = (enabled || enabledTemp) && active && !paused
|
val show get() = (enabled || enabledTemp) && active && !paused
|
||||||
|
|
||||||
|
val showDim get() = dimEnabled && dimActive && !paused
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,8 @@ message ScreensaverPreferences{
|
||||||
bool enabled = 5;
|
bool enabled = 5;
|
||||||
repeated string item_types = 6;
|
repeated string item_types = 6;
|
||||||
bool show_clock = 7;
|
bool show_clock = 7;
|
||||||
|
bool dim_enabled = 8;
|
||||||
|
int32 dim_percent = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message SearchPreferences {
|
message SearchPreferences {
|
||||||
|
|
|
||||||
|
|
@ -910,4 +910,7 @@
|
||||||
<string name="audio_books">Audio books</string>
|
<string name="audio_books">Audio books</string>
|
||||||
<string name="server_version_not_supported">Server version not supported</string>
|
<string name="server_version_not_supported">Server version not supported</string>
|
||||||
<string name="appears_on">Appears on</string>
|
<string name="appears_on">Appears on</string>
|
||||||
|
<string name="dim_after_delay">Dim screen after delay</string>
|
||||||
|
<string name="dim_percentage">Dim percentage</string>
|
||||||
|
<string name="dim_screen">Dim screen</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue