Screensaver fixes (#1047)

## Description
Fixes a few issues with the in-app & OS screensavers

- Make sure clock is updating for in-app screensaver
- Fix crash if app is force quit before the OS screensaver starts

### Related issues
Fixes #1045 

### Testing
NVIDIA shield

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Ray 2026-03-06 08:46:47 -05:00 committed by GitHub
parent 83ebd922d9
commit ce44e9593b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 81 additions and 62 deletions

View file

@ -53,6 +53,7 @@ import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
import com.github.damontecres.wholphin.util.DebugLogTree import com.github.damontecres.wholphin.util.DebugLogTree
import com.github.damontecres.wholphin.util.ExceptionHandler import com.github.damontecres.wholphin.util.ExceptionHandler
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -211,6 +212,7 @@ class MainActivity : AppCompatActivity() {
true, true,
appThemeColors = appPreferences.interfacePreferences.appThemeColors, appThemeColors = appPreferences.interfacePreferences.appThemeColors,
) { ) {
ProvideLocalClock {
val requestedDestination = val requestedDestination =
remember(intent) { remember(intent) {
intent?.let(::extractDestination) ?: Destination.Home() intent?.let(::extractDestination) ?: Destination.Home()
@ -229,6 +231,7 @@ class MainActivity : AppCompatActivity() {
} }
} }
} }
}
override fun dispatchKeyEvent(event: KeyEvent): Boolean { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
if (screensaverService.state.value.show) { if (screensaverService.state.value.show) {

View file

@ -96,7 +96,6 @@ fun MainContent(
backdropService.clearBackdrop() backdropService.clearBackdrop()
} }
val current = key.current val current = key.current
ProvideLocalClock {
val preferences = val preferences =
remember(appPreferences) { remember(appPreferences) {
UserPreferences(appPreferences) UserPreferences(appPreferences)
@ -133,7 +132,6 @@ fun MainContent(
} }
} }
} }
}
}, },
) )
val screenSaverState by screensaverService.state.collectAsState() val screenSaverState by screensaverService.state.collectAsState()

View file

@ -5,11 +5,13 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf 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.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import androidx.datastore.core.DataStore
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleRegistry import androidx.lifecycle.LifecycleRegistry
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
@ -18,14 +20,19 @@ import androidx.savedstate.SavedStateRegistry
import androidx.savedstate.SavedStateRegistryController import androidx.savedstate.SavedStateRegistryController
import androidx.savedstate.SavedStateRegistryOwner import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.setViewTreeSavedStateRegistryOwner import androidx.savedstate.setViewTreeSavedStateRegistryOwner
import com.github.damontecres.wholphin.data.ServerRepository
import com.github.damontecres.wholphin.preferences.AppPreferences
import com.github.damontecres.wholphin.preferences.UserPreferences import com.github.damontecres.wholphin.preferences.UserPreferences
import com.github.damontecres.wholphin.services.ScreensaverService import com.github.damontecres.wholphin.services.ScreensaverService
import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.services.UserPreferencesService
import com.github.damontecres.wholphin.ui.components.AppScreensaverContent import com.github.damontecres.wholphin.ui.components.AppScreensaverContent
import com.github.damontecres.wholphin.ui.launchDefault
import com.github.damontecres.wholphin.ui.theme.WholphinTheme import com.github.damontecres.wholphin.ui.theme.WholphinTheme
import com.github.damontecres.wholphin.ui.util.ProvideLocalClock import com.github.damontecres.wholphin.ui.util.ProvideLocalClock
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.first
import org.jellyfin.sdk.model.serializer.toUUIDOrNull
import javax.inject.Inject import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
@ -33,11 +40,14 @@ import kotlin.time.Duration.Companion.milliseconds
class WholphinDreamService : class WholphinDreamService :
DreamService(), DreamService(),
SavedStateRegistryOwner { SavedStateRegistryOwner {
@Inject
lateinit var serverRepository: ServerRepository
@Inject @Inject
lateinit var screensaverService: ScreensaverService lateinit var screensaverService: ScreensaverService
@Inject @Inject
lateinit var userPreferencesService: UserPreferencesService lateinit var preferencesDataStore: DataStore<AppPreferences>
private val lifecycleRegistry = LifecycleRegistry(this) private val lifecycleRegistry = LifecycleRegistry(this)
@ -54,6 +64,12 @@ class WholphinDreamService :
savedStateRegistryController.performRestore(null) savedStateRegistryController.performRestore(null)
lifecycleRegistry.currentState = Lifecycle.State.CREATED lifecycleRegistry.currentState = Lifecycle.State.CREATED
lifecycleScope.launchDefault {
if (serverRepository.current.value == null) {
val prefs = preferencesDataStore.data.first()
serverRepository.restoreSession(prefs.currentServerId.toUUIDOrNull(), prefs.currentUserId.toUUIDOrNull())
}
}
} }
override fun onAttachedToWindow() { override fun onAttachedToWindow() {
@ -64,15 +80,16 @@ class WholphinDreamService :
setViewTreeLifecycleOwner(this@WholphinDreamService) setViewTreeLifecycleOwner(this@WholphinDreamService)
setViewTreeSavedStateRegistryOwner(this@WholphinDreamService) setViewTreeSavedStateRegistryOwner(this@WholphinDreamService)
setContent { setContent {
var prefs by remember { mutableStateOf<UserPreferences?>(null) } val user by serverRepository.currentUser.observeAsState()
if (user != null) {
var prefs by remember { mutableStateOf<AppPreferences?>(null) }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
userPreferencesService.flow.collectLatest { prefs = it } preferencesDataStore.data.collectLatest { prefs = it }
} }
prefs?.let { prefs -> prefs?.let { prefs ->
WholphinTheme(appThemeColors = prefs.appPreferences.interfacePreferences.appThemeColors) { WholphinTheme(appThemeColors = prefs.interfacePreferences.appThemeColors) {
ProvideLocalClock { ProvideLocalClock {
val screensaverPrefs = val screensaverPrefs = prefs.interfacePreferences.screensaverPreference
prefs.appPreferences.interfacePreferences.screensaverPreference
val currentItem by itemFlow.collectAsState(null) val currentItem by itemFlow.collectAsState(null)
AppScreensaverContent( AppScreensaverContent(
currentItem = currentItem, currentItem = currentItem,
@ -85,6 +102,7 @@ class WholphinDreamService :
} }
} }
} }
}
}, },
) )
} }

View file

@ -153,7 +153,7 @@ fun PreferencesContent(
try { try {
System.loadLibrary("mpv") System.loadLibrary("mpv")
System.loadLibrary("player") System.loadLibrary("player")
} catch (ex: Exception) { } catch (ex: UnsatisfiedLinkError) {
Timber.w(ex, "Could not load libmpv") Timber.w(ex, "Could not load libmpv")
showToast(context, "MPV is not supported on this device") showToast(context, "MPV is not supported on this device")
viewModel.preferenceDataStore.updateData { viewModel.preferenceDataStore.updateData {

View file

@ -34,7 +34,7 @@ data class Clock(
fun ProvideLocalClock(content: @Composable () -> Unit) { fun ProvideLocalClock(content: @Composable () -> Unit) {
val clock = remember { Clock() } val clock = remember { Clock() }
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
withContext(Dispatchers.IO) { withContext(Dispatchers.Default) {
while (isActive) { while (isActive) {
val now = LocalDateTime.now() val now = LocalDateTime.now()
val time = TimeFormatter.format(now) val time = TimeFormatter.format(now)