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.nav.Destination
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.ExceptionHandler
import dagger.hilt.android.AndroidEntryPoint
@ -211,19 +212,21 @@ class MainActivity : AppCompatActivity() {
true,
appThemeColors = appPreferences.interfacePreferences.appThemeColors,
) {
val requestedDestination =
remember(intent) {
intent?.let(::extractDestination) ?: Destination.Home()
}
MainContent(
backStack = setupNavigationManager.backStack,
navigationManager = navigationManager,
appPreferences = appPreferences,
backdropService = backdropService,
screensaverService = screensaverService,
requestedDestination = requestedDestination,
modifier = Modifier.fillMaxSize(),
)
ProvideLocalClock {
val requestedDestination =
remember(intent) {
intent?.let(::extractDestination) ?: Destination.Home()
}
MainContent(
backStack = setupNavigationManager.backStack,
navigationManager = navigationManager,
appPreferences = appPreferences,
backdropService = backdropService,
screensaverService = screensaverService,
requestedDestination = requestedDestination,
modifier = Modifier.fillMaxSize(),
)
}
}
}
}

View file

@ -96,39 +96,37 @@ fun MainContent(
backdropService.clearBackdrop()
}
val current = key.current
ProvideLocalClock {
val preferences =
remember(appPreferences) {
UserPreferences(appPreferences)
}
var showContent by remember {
mutableStateOf(true)
val preferences =
remember(appPreferences) {
UserPreferences(appPreferences)
}
LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
if (!appPreferences.signInAutomatically) {
showContent = false
}
var showContent by remember {
mutableStateOf(true)
}
LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
if (!appPreferences.signInAutomatically) {
showContent = false
}
}
if (showContent) {
ApplicationContent(
user = current.user,
server = current.server,
startDestination = requestedDestination,
navigationManager = navigationManager,
preferences = preferences,
modifier = Modifier.fillMaxSize(),
if (showContent) {
ApplicationContent(
user = current.user,
server = current.server,
startDestination = requestedDestination,
navigationManager = navigationManager,
preferences = preferences,
modifier = Modifier.fillMaxSize(),
)
} else {
Box(
modifier = Modifier.size(200.dp),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.border,
modifier = Modifier.align(Alignment.Center),
)
} else {
Box(
modifier = Modifier.size(200.dp),
contentAlignment = Alignment.Center,
) {
CircularProgressIndicator(
color = MaterialTheme.colorScheme.border,
modifier = Modifier.align(Alignment.Center),
)
}
}
}
}

View file

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

View file

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

View file

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