diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt index 4b3c248f..5ca32695 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/BaseItem.kt @@ -12,6 +12,7 @@ import com.github.damontecres.wholphin.ui.detail.series.SeasonEpisodeIds import com.github.damontecres.wholphin.ui.dot import com.github.damontecres.wholphin.ui.formatDateTime import com.github.damontecres.wholphin.ui.getDateFormatter +import com.github.damontecres.wholphin.ui.joinNotBlank import com.github.damontecres.wholphin.ui.nav.Destination import com.github.damontecres.wholphin.ui.playback.playable import com.github.damontecres.wholphin.ui.roundMinutes @@ -57,14 +58,14 @@ data class BaseItem( val title get() = if (type == BaseItemKind.EPISODE) data.seriesName else name - val subtitle - get() = - when (type) { - BaseItemKind.EPISODE -> data.seasonEpisode + " - " + name - BaseItemKind.SERIES -> data.seriesProductionYears - BaseItemKind.AUDIO -> listOfNotNull(data.album, artistsString).joinToString(" - ") - else -> data.productionYear?.toString() - } + val subtitle: String? by lazy { + when (type) { + BaseItemKind.EPISODE -> listOf(data.seasonEpisode, name).joinNotBlank(" - ") + BaseItemKind.SERIES -> data.seriesProductionYears + BaseItemKind.AUDIO -> listOf(data.album, artistsString).joinNotBlank(" - ") + else -> data.productionYear?.toString() + } + } val subtitleLong: String? by lazy { if (type == BaseItemKind.EPISODE) { @@ -72,7 +73,7 @@ data class BaseItem( add(data.seasonEpisodePadded) add(data.name) add(data.premiereDate?.let { formatDateTime(it) }) - }.filterNotNull().joinToString(" - ") + }.joinNotBlank(" - ") } else { data.productionYear?.toString() } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt index 31ad597a..37353a8f 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/Extensions.kt @@ -74,6 +74,8 @@ inline fun List.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? { return if (index >= 0) index else null } +fun Iterable.joinNotBlank(sep: String): String? = filter { it.isNotNullOrBlank() }.letNotEmpty { it.joinToString(sep) } + /** * Try to call [FocusRequester.requestFocus], but catch & log the exception if something is not configured properly */ diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt index b399766c..6587c626 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CardGrid.kt @@ -141,7 +141,7 @@ fun CardGrid( { index: Int -> if (DEBUG) Timber.v("focusOn: focusedIndex=$currentFocusedIndex, index=$index") if (index != currentFocusedIndex) { - previouslyFocusedIndex = focusedIndex + previouslyFocusedIndex = currentFocusedIndex } focusedIndex = index } @@ -257,10 +257,11 @@ fun CardGrid( val newPosition = previouslyFocusedIndex if (DEBUG) Timber.d("Back long pressed: newPosition=$newPosition") if (newPosition > 0) { - focusOn(newPosition) scope.launch(ExceptionHandler()) { - gridState.scrollToItem(newPosition, -columns) - firstFocus.tryRequestFocus() + pager.getOrNull(newPosition) + gridState.scrollToItem(newPosition) + focusOn(newPosition) + alphabetFocus = true } } return@onKeyEvent true diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverPage.kt index 9751a6d0..3ef3424a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverPage.kt @@ -42,6 +42,7 @@ fun DiscoverPage( 0 -> { SeerrDiscoverPage( preferences = preferences, + positionCallback = { showHeader = it.row < 1 }, modifier = Modifier .fillMaxSize() @@ -53,6 +54,7 @@ fun DiscoverPage( 1 -> { SeerrRequestsPage( focusRequesterOnEmpty = tabDetails.tabFocusRequester, + positionCallback = { columns, index -> showHeader = index < columns }, modifier = Modifier .fillMaxSize() @@ -64,6 +66,7 @@ fun DiscoverPage( 2 -> { DiscoverSearchPage( preferences = preferences, + positionCallback = { columns, index -> showHeader = index < columns }, modifier = Modifier .fillMaxSize() diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverSearchPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverSearchPage.kt index f0664d49..fe4a198a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverSearchPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/DiscoverSearchPage.kt @@ -102,6 +102,7 @@ class DiscoverSearchViewModel @Composable fun DiscoverSearchPage( preferences: UserPreferences, + positionCallback: (columns: Int, position: Int) -> Unit, modifier: Modifier = Modifier, viewModel: DiscoverSearchViewModel = hiltViewModel(), ) { @@ -241,7 +242,10 @@ fun DiscoverSearchPage( onPlayItem = { _, _ -> }, onClickPosition = { position = it.column }, onClickDiscover = onClickDiscover, - positionCallback = { columns, index -> position = index }, + positionCallback = { columns, index -> + position = index + positionCallback.invoke(columns, index) + }, modifier = Modifier.fillMaxSize(), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt index 7e98b5c8..ccdda6d8 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrDiscoverPage.kt @@ -243,6 +243,7 @@ data class DiscoverState( @Composable fun SeerrDiscoverPage( preferences: UserPreferences, + positionCallback: (RowColumn) -> Unit, modifier: Modifier = Modifier, viewModel: SeerrDiscoverViewModel = hiltViewModel(), ) { @@ -335,12 +336,16 @@ fun SeerrDiscoverPage( viewModel.navigationManager.navigateTo(item.destination) }, onLongClickItem = { index, item -> }, - onCardFocus = { index -> position = RowColumn(rowIndex, index) }, + onCardFocus = { index -> + position = RowColumn(rowIndex, index) + positionCallback.invoke(position) + }, focusRequester = focusRequesters[rowIndex], enableViewMore = row.type != DiscoverRequestType.UNKNOWN, onClickViewMore = { (row.items as? DataLoadingState.Success>)?.data?.size?.let { position = RowColumn(rowIndex, it) + positionCallback.invoke(position) } viewModel.navigationManager.navigateTo( Destination.DiscoverMoreResult(row.type), diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrRequestsPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrRequestsPage.kt index 50f2464c..52815683 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrRequestsPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/discover/SeerrRequestsPage.kt @@ -148,10 +148,12 @@ data class RequestGridItem( @Composable fun SeerrRequestsPage( focusRequesterOnEmpty: FocusRequester?, + positionCallback: (columns: Int, position: Int) -> Unit, modifier: Modifier = Modifier, viewModel: SeerrRequestsViewModel = hiltViewModel(), ) { val state by viewModel.state.collectAsState(SeerrRequestsState.EMPTY) + val columns = 6 when (val state = state.requests) { is DataLoadingState.Error -> { @@ -213,7 +215,8 @@ fun SeerrRequestsPage( modifier = mod, ) }, - columns = 6, + columns = columns, + positionCallback = positionCallback, modifier = Modifier.fillMaxSize(), ) } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlayExternalPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlayExternalPage.kt index a9e4b61d..871264c2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlayExternalPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/playback/PlayExternalPage.kt @@ -338,12 +338,15 @@ class PlayExternalViewModel } } Timber.v("Result position: %s", position?.milliseconds) + if (position != null && position < 0L) { + Timber.w("Unknown stop position for external playback") + } if (position != null || result.data?.action != null) { api.playStateApi.reportPlaybackStopped( PlaybackStopInfo( itemId = itemId, mediaSourceId = mediaSourceId, - positionTicks = position?.milliseconds?.inWholeTicks, + positionTicks = position?.milliseconds?.inWholeTicks?.takeIf { it >= 0 }, failed = false, ), ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt b/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt index 30241845..6e11013b 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/util/TrackActivityPlaybackListener.kt @@ -21,6 +21,7 @@ import timber.log.Timber import java.util.Timer import java.util.TimerTask import java.util.UUID +import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.seconds @@ -84,10 +85,13 @@ class TrackActivityPlaybackListener( launch("reportPlaybackStopped") { getState.invoke()?.let { state -> Timber.v("reportPlaybackStopped for ${state.itemId} at $position") + if (position < Duration.ZERO) { + Timber.w("Negative position when reporting playback stopped: %s", position) + } api.playStateApi.reportPlaybackStopped( PlaybackStopInfo( itemId = state.itemId, - positionTicks = position.inWholeTicks, + positionTicks = position.inWholeTicks.takeIf { it >= 0 }, failed = false, playSessionId = state.playSessionId, liveStreamId = state.liveStreamId,