Log while navigating around the app (#195)

Just adds some logging while navigating around the app. If an error or
crash occurs and users want to share their logs, this information is
useful to know what page.

Logs are never sent without explicit user content and they are only
shared with the Jellyfin server the app is currently connected to. [See
here for more
details](https://github.com/damontecres/Wholphin/wiki/Gathering-app-logs#privacy)

Also stops theme music when the app is paused similar to #191
This commit is contained in:
damontecres 2025-11-11 20:10:16 -05:00 committed by GitHub
parent ed2ec2f97e
commit 5e2c75bb70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 40 additions and 3 deletions

View file

@ -38,6 +38,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.acra.ACRA
import org.jellyfin.sdk.model.api.BaseItemDto
import org.jellyfin.sdk.model.extensions.ticks
import timber.log.Timber
@ -380,3 +381,12 @@ fun equalsNotNull(
a: Any?,
b: Any?,
) = a != null && b != null && a == b
fun logTab(
name: String,
tabIndex: Int,
) {
val info = "$tabIndex, $name"
Timber.i("Current tab: $info")
ACRA.errorReporter.putCustomData("tabInfo", info)
}

View file

@ -38,6 +38,7 @@ import com.github.damontecres.wholphin.ui.data.VideoSortOptions
import com.github.damontecres.wholphin.ui.detail.livetv.DvrSchedule
import com.github.damontecres.wholphin.ui.detail.livetv.TvGuideGrid
import com.github.damontecres.wholphin.ui.launchIO
import com.github.damontecres.wholphin.ui.logTab
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.nav.NavigationManager
import com.github.damontecres.wholphin.ui.setValueOnMain
@ -102,6 +103,7 @@ fun CollectionFolderLiveTv(
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
LaunchedEffect(selectedTabIndex) {
logTab("livetv", selectedTabIndex)
viewModel.saveRememberedTab(preferences, destination.itemId, selectedTabIndex)
}
val onClickItem = { item: BaseItem ->

View file

@ -33,6 +33,7 @@ import com.github.damontecres.wholphin.ui.components.RecommendedMovie
import com.github.damontecres.wholphin.ui.components.TabRow
import com.github.damontecres.wholphin.ui.data.MovieSortOptions
import com.github.damontecres.wholphin.ui.data.VideoSortOptions
import com.github.damontecres.wholphin.ui.logTab
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
import com.github.damontecres.wholphin.ui.tryRequestFocus
@ -62,6 +63,7 @@ fun CollectionFolderMovie(
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
LaunchedEffect(selectedTabIndex) {
logTab("movie", selectedTabIndex)
preferencesViewModel.saveRememberedTab(preferences, destination.itemId, selectedTabIndex)
}

View file

@ -32,6 +32,7 @@ import com.github.damontecres.wholphin.ui.components.GenreCardGrid
import com.github.damontecres.wholphin.ui.components.RecommendedTvShow
import com.github.damontecres.wholphin.ui.components.TabRow
import com.github.damontecres.wholphin.ui.data.SeriesSortOptions
import com.github.damontecres.wholphin.ui.logTab
import com.github.damontecres.wholphin.ui.nav.Destination
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
import com.github.damontecres.wholphin.ui.tryRequestFocus
@ -60,6 +61,7 @@ fun CollectionFolderTv(
LaunchedEffect(Unit) { firstTabFocusRequester.tryRequestFocus() }
LaunchedEffect(selectedTabIndex) {
logTab("tv", selectedTabIndex)
preferencesViewModel.saveRememberedTab(preferences, destination.itemId, selectedTabIndex)
}

View file

@ -32,6 +32,7 @@ import com.github.damontecres.wholphin.ui.components.TabRow
import com.github.damontecres.wholphin.ui.data.EpisodeSortOptions
import com.github.damontecres.wholphin.ui.data.MovieSortOptions
import com.github.damontecres.wholphin.ui.data.SeriesSortOptions
import com.github.damontecres.wholphin.ui.logTab
import com.github.damontecres.wholphin.ui.nav.NavDrawerItem
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
import com.github.damontecres.wholphin.ui.tryRequestFocus
@ -63,6 +64,7 @@ fun FavoritesPage(
val focusRequester = remember { FocusRequester() }
LaunchedEffect(selectedTabIndex) {
logTab("favorites", selectedTabIndex)
preferencesViewModel.saveRememberedTab(
preferences,
NavDrawerItem.Favorites.id,

View file

@ -16,6 +16,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableIntStateOf
@ -50,6 +51,7 @@ import com.github.damontecres.wholphin.ui.components.LoadingPage
import com.github.damontecres.wholphin.ui.components.TabRow
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.isNotNullOrBlank
import com.github.damontecres.wholphin.ui.logTab
import com.github.damontecres.wholphin.ui.tryRequestFocus
import com.github.damontecres.wholphin.util.formatDateTime
import kotlin.time.Duration
@ -77,6 +79,9 @@ fun SeriesOverviewContent(
) {
val bringIntoViewRequester = remember { BringIntoViewRequester() }
var selectedTabIndex by rememberSaveable(position) { mutableIntStateOf(position.seasonTabIndex) }
LaunchedEffect(selectedTabIndex) {
logTab("series_overview", selectedTabIndex)
}
val tabRowFocusRequester = remember { FocusRequester() }
val focusedEpisode =

View file

@ -11,7 +11,6 @@ import com.github.damontecres.wholphin.data.model.JellyfinServer
import com.github.damontecres.wholphin.data.model.JellyfinUser
import com.github.damontecres.wholphin.preferences.UserPreferences
import org.jellyfin.sdk.model.api.DeviceProfile
import timber.log.Timber
/**
* This is generally the root composable of the of the app
@ -38,7 +37,6 @@ fun ApplicationContent(
entryProvider = { key ->
key as Destination
val contentKey = "${key}_${server?.id}_${user?.id}"
Timber.d("Navigate: %s", key)
NavEntry(key, contentKey = contentKey) {
if (key.fullScreen) {
DestinationContent(

View file

@ -1,6 +1,8 @@
package com.github.damontecres.wholphin.ui.nav
import androidx.navigation3.runtime.NavKey
import org.acra.ACRA
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -18,6 +20,7 @@ class NavigationManager
*/
fun navigateTo(destination: Destination) {
backStack.add(destination)
log()
}
/**
@ -26,6 +29,7 @@ class NavigationManager
fun navigateToFromDrawer(destination: Destination) {
goToHome()
backStack.add(destination)
log()
}
/**
@ -33,6 +37,7 @@ class NavigationManager
*/
fun goBack() {
backStack.removeLastOrNull()
log()
}
/**
@ -45,6 +50,7 @@ class NavigationManager
if (backStack[0] !is Destination.Home) {
backStack[0] = Destination.Home()
}
log()
}
/**
@ -54,5 +60,12 @@ class NavigationManager
goToHome()
val id = (backStack[0] as Destination.Home).id + 1
backStack[0] = Destination.Home(id)
log()
}
private fun log() {
val dest = backStack.lastOrNull().toString()
Timber.i("Current Destination: %s", dest)
ACRA.errorReporter.putCustomData("destination", dest)
}
}

View file

@ -16,6 +16,7 @@ class PlaybackLifecycleObserver
constructor(
private val navigationManager: NavigationManager,
private val playerFactory: PlayerFactory,
private val themeSongPlayer: ThemeSongPlayer,
) : DefaultLifecycleObserver {
private var wasPlaying: Boolean? = null
@ -36,11 +37,13 @@ class PlaybackLifecycleObserver
wasPlaying = it.isPlaying
it.pause()
}
themeSongPlayer.stop()
}
override fun onStop(owner: LifecycleOwner) {
if (navigationManager.backStack.lastOrNull() is Destination.Playback) {
navigationManager.goBack()
}
themeSongPlayer.stop()
}
}

View file

@ -78,7 +78,6 @@ class ThemeSongPlayer
volumeLevel: ThemeSongVolume,
url: String,
) {
stop()
val volumeLevel =
when (volumeLevel) {
ThemeSongVolume.UNRECOGNIZED,
@ -92,6 +91,7 @@ class ThemeSongPlayer
ThemeSongVolume.HIGHEST -> 75f
}
player.apply {
stop()
volume = volumeLevel
setMediaItem(MediaItem.fromUri(url))
prepare()