Several small fixes (#1612)
## Description - Fixes showing "null" in the subtitle for some tv episodes (usually daily shows like Jeopardy) - Partially fix holding back to return to the previous grid position (still has focus issues) - Show/hide tabs on the discover page similar to other pages in the app - Prevent a warning server-side if a negative playback timestamp is sent ### Related issues N/A ### Testing Emulator ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
77d7571276
commit
c945e1a472
9 changed files with 44 additions and 18 deletions
|
|
@ -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.dot
|
||||||
import com.github.damontecres.wholphin.ui.formatDateTime
|
import com.github.damontecres.wholphin.ui.formatDateTime
|
||||||
import com.github.damontecres.wholphin.ui.getDateFormatter
|
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.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.playback.playable
|
import com.github.damontecres.wholphin.ui.playback.playable
|
||||||
import com.github.damontecres.wholphin.ui.roundMinutes
|
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 title get() = if (type == BaseItemKind.EPISODE) data.seriesName else name
|
||||||
|
|
||||||
val subtitle
|
val subtitle: String? by lazy {
|
||||||
get() =
|
when (type) {
|
||||||
when (type) {
|
BaseItemKind.EPISODE -> listOf(data.seasonEpisode, name).joinNotBlank(" - ")
|
||||||
BaseItemKind.EPISODE -> data.seasonEpisode + " - " + name
|
BaseItemKind.SERIES -> data.seriesProductionYears
|
||||||
BaseItemKind.SERIES -> data.seriesProductionYears
|
BaseItemKind.AUDIO -> listOf(data.album, artistsString).joinNotBlank(" - ")
|
||||||
BaseItemKind.AUDIO -> listOfNotNull(data.album, artistsString).joinToString(" - ")
|
else -> data.productionYear?.toString()
|
||||||
else -> data.productionYear?.toString()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val subtitleLong: String? by lazy {
|
val subtitleLong: String? by lazy {
|
||||||
if (type == BaseItemKind.EPISODE) {
|
if (type == BaseItemKind.EPISODE) {
|
||||||
|
|
@ -72,7 +73,7 @@ data class BaseItem(
|
||||||
add(data.seasonEpisodePadded)
|
add(data.seasonEpisodePadded)
|
||||||
add(data.name)
|
add(data.name)
|
||||||
add(data.premiereDate?.let { formatDateTime(it) })
|
add(data.premiereDate?.let { formatDateTime(it) })
|
||||||
}.filterNotNull().joinToString(" - ")
|
}.joinNotBlank(" - ")
|
||||||
} else {
|
} else {
|
||||||
data.productionYear?.toString()
|
data.productionYear?.toString()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,8 @@ inline fun <T> List<T>.indexOfFirstOrNull(predicate: (T) -> Boolean): Int? {
|
||||||
return if (index >= 0) index else null
|
return if (index >= 0) index else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Iterable<CharSequence?>.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
|
* Try to call [FocusRequester.requestFocus], but catch & log the exception if something is not configured properly
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ fun <T : CardGridItem> CardGrid(
|
||||||
{ index: Int ->
|
{ index: Int ->
|
||||||
if (DEBUG) Timber.v("focusOn: focusedIndex=$currentFocusedIndex, index=$index")
|
if (DEBUG) Timber.v("focusOn: focusedIndex=$currentFocusedIndex, index=$index")
|
||||||
if (index != currentFocusedIndex) {
|
if (index != currentFocusedIndex) {
|
||||||
previouslyFocusedIndex = focusedIndex
|
previouslyFocusedIndex = currentFocusedIndex
|
||||||
}
|
}
|
||||||
focusedIndex = index
|
focusedIndex = index
|
||||||
}
|
}
|
||||||
|
|
@ -257,10 +257,11 @@ fun <T : CardGridItem> CardGrid(
|
||||||
val newPosition = previouslyFocusedIndex
|
val newPosition = previouslyFocusedIndex
|
||||||
if (DEBUG) Timber.d("Back long pressed: newPosition=$newPosition")
|
if (DEBUG) Timber.d("Back long pressed: newPosition=$newPosition")
|
||||||
if (newPosition > 0) {
|
if (newPosition > 0) {
|
||||||
focusOn(newPosition)
|
|
||||||
scope.launch(ExceptionHandler()) {
|
scope.launch(ExceptionHandler()) {
|
||||||
gridState.scrollToItem(newPosition, -columns)
|
pager.getOrNull(newPosition)
|
||||||
firstFocus.tryRequestFocus()
|
gridState.scrollToItem(newPosition)
|
||||||
|
focusOn(newPosition)
|
||||||
|
alphabetFocus = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return@onKeyEvent true
|
return@onKeyEvent true
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ fun DiscoverPage(
|
||||||
0 -> {
|
0 -> {
|
||||||
SeerrDiscoverPage(
|
SeerrDiscoverPage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
|
positionCallback = { showHeader = it.row < 1 },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -53,6 +54,7 @@ fun DiscoverPage(
|
||||||
1 -> {
|
1 -> {
|
||||||
SeerrRequestsPage(
|
SeerrRequestsPage(
|
||||||
focusRequesterOnEmpty = tabDetails.tabFocusRequester,
|
focusRequesterOnEmpty = tabDetails.tabFocusRequester,
|
||||||
|
positionCallback = { columns, index -> showHeader = index < columns },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
@ -64,6 +66,7 @@ fun DiscoverPage(
|
||||||
2 -> {
|
2 -> {
|
||||||
DiscoverSearchPage(
|
DiscoverSearchPage(
|
||||||
preferences = preferences,
|
preferences = preferences,
|
||||||
|
positionCallback = { columns, index -> showHeader = index < columns },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|
|
||||||
|
|
@ -102,6 +102,7 @@ class DiscoverSearchViewModel
|
||||||
@Composable
|
@Composable
|
||||||
fun DiscoverSearchPage(
|
fun DiscoverSearchPage(
|
||||||
preferences: UserPreferences,
|
preferences: UserPreferences,
|
||||||
|
positionCallback: (columns: Int, position: Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: DiscoverSearchViewModel = hiltViewModel(),
|
viewModel: DiscoverSearchViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -241,7 +242,10 @@ fun DiscoverSearchPage(
|
||||||
onPlayItem = { _, _ -> },
|
onPlayItem = { _, _ -> },
|
||||||
onClickPosition = { position = it.column },
|
onClickPosition = { position = it.column },
|
||||||
onClickDiscover = onClickDiscover,
|
onClickDiscover = onClickDiscover,
|
||||||
positionCallback = { columns, index -> position = index },
|
positionCallback = { columns, index ->
|
||||||
|
position = index
|
||||||
|
positionCallback.invoke(columns, index)
|
||||||
|
},
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -243,6 +243,7 @@ data class DiscoverState(
|
||||||
@Composable
|
@Composable
|
||||||
fun SeerrDiscoverPage(
|
fun SeerrDiscoverPage(
|
||||||
preferences: UserPreferences,
|
preferences: UserPreferences,
|
||||||
|
positionCallback: (RowColumn) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: SeerrDiscoverViewModel = hiltViewModel(),
|
viewModel: SeerrDiscoverViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
|
@ -335,12 +336,16 @@ fun SeerrDiscoverPage(
|
||||||
viewModel.navigationManager.navigateTo(item.destination)
|
viewModel.navigationManager.navigateTo(item.destination)
|
||||||
},
|
},
|
||||||
onLongClickItem = { index, item -> },
|
onLongClickItem = { index, item -> },
|
||||||
onCardFocus = { index -> position = RowColumn(rowIndex, index) },
|
onCardFocus = { index ->
|
||||||
|
position = RowColumn(rowIndex, index)
|
||||||
|
positionCallback.invoke(position)
|
||||||
|
},
|
||||||
focusRequester = focusRequesters[rowIndex],
|
focusRequester = focusRequesters[rowIndex],
|
||||||
enableViewMore = row.type != DiscoverRequestType.UNKNOWN,
|
enableViewMore = row.type != DiscoverRequestType.UNKNOWN,
|
||||||
onClickViewMore = {
|
onClickViewMore = {
|
||||||
(row.items as? DataLoadingState.Success<List<DiscoverItem>>)?.data?.size?.let {
|
(row.items as? DataLoadingState.Success<List<DiscoverItem>>)?.data?.size?.let {
|
||||||
position = RowColumn(rowIndex, it)
|
position = RowColumn(rowIndex, it)
|
||||||
|
positionCallback.invoke(position)
|
||||||
}
|
}
|
||||||
viewModel.navigationManager.navigateTo(
|
viewModel.navigationManager.navigateTo(
|
||||||
Destination.DiscoverMoreResult(row.type),
|
Destination.DiscoverMoreResult(row.type),
|
||||||
|
|
|
||||||
|
|
@ -148,10 +148,12 @@ data class RequestGridItem(
|
||||||
@Composable
|
@Composable
|
||||||
fun SeerrRequestsPage(
|
fun SeerrRequestsPage(
|
||||||
focusRequesterOnEmpty: FocusRequester?,
|
focusRequesterOnEmpty: FocusRequester?,
|
||||||
|
positionCallback: (columns: Int, position: Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: SeerrRequestsViewModel = hiltViewModel(),
|
viewModel: SeerrRequestsViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val state by viewModel.state.collectAsState(SeerrRequestsState.EMPTY)
|
val state by viewModel.state.collectAsState(SeerrRequestsState.EMPTY)
|
||||||
|
val columns = 6
|
||||||
|
|
||||||
when (val state = state.requests) {
|
when (val state = state.requests) {
|
||||||
is DataLoadingState.Error -> {
|
is DataLoadingState.Error -> {
|
||||||
|
|
@ -213,7 +215,8 @@ fun SeerrRequestsPage(
|
||||||
modifier = mod,
|
modifier = mod,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
columns = 6,
|
columns = columns,
|
||||||
|
positionCallback = positionCallback,
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -338,12 +338,15 @@ class PlayExternalViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Timber.v("Result position: %s", position?.milliseconds)
|
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) {
|
if (position != null || result.data?.action != null) {
|
||||||
api.playStateApi.reportPlaybackStopped(
|
api.playStateApi.reportPlaybackStopped(
|
||||||
PlaybackStopInfo(
|
PlaybackStopInfo(
|
||||||
itemId = itemId,
|
itemId = itemId,
|
||||||
mediaSourceId = mediaSourceId,
|
mediaSourceId = mediaSourceId,
|
||||||
positionTicks = position?.milliseconds?.inWholeTicks,
|
positionTicks = position?.milliseconds?.inWholeTicks?.takeIf { it >= 0 },
|
||||||
failed = false,
|
failed = false,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import timber.log.Timber
|
||||||
import java.util.Timer
|
import java.util.Timer
|
||||||
import java.util.TimerTask
|
import java.util.TimerTask
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
import kotlin.time.Duration
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
import kotlin.time.Duration.Companion.seconds
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
|
||||||
|
|
@ -84,10 +85,13 @@ class TrackActivityPlaybackListener(
|
||||||
launch("reportPlaybackStopped") {
|
launch("reportPlaybackStopped") {
|
||||||
getState.invoke()?.let { state ->
|
getState.invoke()?.let { state ->
|
||||||
Timber.v("reportPlaybackStopped for ${state.itemId} at $position")
|
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(
|
api.playStateApi.reportPlaybackStopped(
|
||||||
PlaybackStopInfo(
|
PlaybackStopInfo(
|
||||||
itemId = state.itemId,
|
itemId = state.itemId,
|
||||||
positionTicks = position.inWholeTicks,
|
positionTicks = position.inWholeTicks.takeIf { it >= 0 },
|
||||||
failed = false,
|
failed = false,
|
||||||
playSessionId = state.playSessionId,
|
playSessionId = state.playSessionId,
|
||||||
liveStreamId = state.liveStreamId,
|
liveStreamId = state.liveStreamId,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue