Several UI fixes (#1730)
## Description - Fixes not scrolling to the last season on series overview if there is a Specials season - Apply the "bouncing" fix from #1543 to all applicable card types - Fix card images not automatically reloading when changing image type on grid pages - Get rid of the sticky headers which overlap items on view options dialogs and use a fixed header as elsewhere in the app ### Related issues Related to #1708 which didn't introduce the bug, but made it much more likely to occur ### Testing Emulator & unit tests ## Screenshots N/A, the sticky header UI changes are very minor ## AI or LLM usage None
This commit is contained in:
parent
2aae7eed38
commit
0a4ab7a547
12 changed files with 159 additions and 130 deletions
|
|
@ -1,6 +1,5 @@
|
||||||
package com.github.damontecres.wholphin.ui.cards
|
package com.github.damontecres.wholphin.ui.cards
|
||||||
|
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||||
|
|
@ -219,13 +218,11 @@ fun BannerCardWithTitle(
|
||||||
useSeriesForPrimary: Boolean = item?.useSeriesForPrimary ?: true,
|
useSeriesForPrimary: Boolean = item?.useSeriesForPrimary ?: true,
|
||||||
) {
|
) {
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
|
||||||
val spaceBelow by animateDpAsState(if (focused) 0.dp else 8.dp)
|
|
||||||
val focusedAfterDelay by rememberFocusedAfterDelay(interactionSource)
|
val focusedAfterDelay by rememberFocusedAfterDelay(interactionSource)
|
||||||
val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN)
|
val aspectRationToUse = aspectRatio.coerceAtLeast(AspectRatios.MIN)
|
||||||
val width = cardHeight * aspectRationToUse
|
val width = cardHeight * aspectRationToUse
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier.width(width),
|
modifier = modifier.width(width),
|
||||||
) {
|
) {
|
||||||
BannerCard(
|
BannerCard(
|
||||||
|
|
@ -245,13 +242,7 @@ fun BannerCardWithTitle(
|
||||||
imageContentScale = imageContentScale,
|
imageContentScale = imageContentScale,
|
||||||
useSeriesForPrimary = useSeriesForPrimary,
|
useSeriesForPrimary = useSeriesForPrimary,
|
||||||
)
|
)
|
||||||
Column(
|
SlidingCardText(focused) {
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(bottom = spaceBelow)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = title ?: "",
|
text = title ?: "",
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,6 @@ fun DiscoverItemCard(
|
||||||
width: Dp = Cards.height2x3 * AspectRatios.TALL,
|
width: Dp = Cards.height2x3 * AspectRatios.TALL,
|
||||||
) {
|
) {
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
|
||||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
|
||||||
var focusedAfterDelay by remember { mutableStateOf(false) }
|
var focusedAfterDelay by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val hideOverlayDelay = 500L
|
val hideOverlayDelay = 500L
|
||||||
|
|
@ -78,7 +76,7 @@ fun DiscoverItemCard(
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier.size(width, Dp.Unspecified),
|
modifier = modifier.size(width, Dp.Unspecified),
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
|
|
@ -180,13 +178,7 @@ fun DiscoverItemCard(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
SlidingCardText(focused) {
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(bottom = spaceBelow)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = item?.title ?: "",
|
text = item?.title ?: "",
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,6 @@ fun EpisodeCard(
|
||||||
) {
|
) {
|
||||||
val dto = item?.data
|
val dto = item?.data
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
|
||||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
|
||||||
var focusedAfterDelay by remember { mutableStateOf(false) }
|
var focusedAfterDelay by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val hideOverlayDelay = 500L
|
val hideOverlayDelay = 500L
|
||||||
|
|
@ -74,7 +72,7 @@ fun EpisodeCard(
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
val imageWidthPx = remember(imageWidth) { with(density) { imageWidth.roundToPx() } }
|
val imageWidthPx = remember(imageWidth) { with(density) { imageWidth.roundToPx() } }
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier.size(width, height),
|
modifier = modifier.size(width, height),
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
|
|
@ -126,13 +124,7 @@ fun EpisodeCard(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Column(
|
SlidingCardText(focused) {
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(bottom = spaceBelow)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = dto?.seriesName ?: "",
|
text = dto?.seriesName ?: "",
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
|
||||||
|
|
@ -52,8 +52,6 @@ fun GridCard(
|
||||||
) {
|
) {
|
||||||
val dto = item?.data
|
val dto = item?.data
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
|
||||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
|
||||||
var focusedAfterDelay by remember { mutableStateOf(false) }
|
var focusedAfterDelay by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val hideOverlayDelay = 500L
|
val hideOverlayDelay = 500L
|
||||||
|
|
@ -71,7 +69,7 @@ fun GridCard(
|
||||||
}
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
|
|
@ -107,13 +105,7 @@ fun GridCard(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
AnimatedVisibility(showTitle) {
|
AnimatedVisibility(showTitle) {
|
||||||
Column(
|
SlidingCardText(focused) {
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.padding(bottom = spaceBelow)
|
|
||||||
.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = item?.title ?: "",
|
text = item?.title ?: "",
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ fun ItemCardImage(
|
||||||
) {
|
) {
|
||||||
val imageUrlService = LocalImageUrlService.current
|
val imageUrlService = LocalImageUrlService.current
|
||||||
val imageUrl =
|
val imageUrl =
|
||||||
remember(item) {
|
remember(item, imageType, fillWidth, fillHeight) {
|
||||||
item?.let {
|
item?.let {
|
||||||
imageUrlService.getItemImageUrl(
|
imageUrlService.getItemImageUrl(
|
||||||
item,
|
item,
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package com.github.damontecres.wholphin.ui.cards
|
package com.github.damontecres.wholphin.ui.cards
|
||||||
|
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
|
@ -11,7 +10,6 @@ import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.aspectRatio
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.offset
|
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
|
@ -26,11 +24,9 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.layout.layout
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.IntOffset
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.tv.material3.Border
|
import androidx.tv.material3.Border
|
||||||
|
|
@ -99,10 +95,6 @@ fun PersonCard(
|
||||||
focusedAfterDelay = false
|
focusedAfterDelay = false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not use `by` here, this way we are Defer reads and recompositions to only when modifier calculates
|
|
||||||
val spaceBetweenState = animateDpAsState(if (focused) 12.dp else 4.dp, label = "spaceBetween")
|
|
||||||
val spaceBelowState = animateDpAsState(if (focused) 4.dp else 12.dp, label = "spaceBelow")
|
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp), // Fixed base spacing
|
verticalArrangement = Arrangement.spacedBy(4.dp), // Fixed base spacing
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -170,21 +162,7 @@ fun PersonCard(
|
||||||
.clip(CircleShape),
|
.clip(CircleShape),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Column(
|
SlidingCardText(focused) {
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
// Optimization: move animation reads to layout/draw phase
|
|
||||||
.offset {
|
|
||||||
IntOffset(0, (spaceBetweenState.value - 4.dp).roundToPx())
|
|
||||||
}.layout { measurable, constraints ->
|
|
||||||
val paddingPx = spaceBelowState.value.roundToPx()
|
|
||||||
val placeable = measurable.measure(constraints)
|
|
||||||
layout(placeable.width, placeable.height + paddingPx) {
|
|
||||||
placeable.placeRelative(0, 0)
|
|
||||||
}
|
|
||||||
}.fillMaxWidth(),
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = name ?: "",
|
text = name ?: "",
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.cards
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.ColumnScope
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.layout.layout
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Slides the [content] up/down as the card is [focused] or not
|
||||||
|
*
|
||||||
|
* This is used for the card's title, moving it down when focused as the card itself scales up
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun SlidingCardText(
|
||||||
|
focused: Boolean,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
spaceBetweenFocused: Dp = 4.dp,
|
||||||
|
spaceBetweenUnfocused: Dp = 12.dp,
|
||||||
|
content: @Composable ColumnScope.() -> Unit,
|
||||||
|
) {
|
||||||
|
val spaceBetween =
|
||||||
|
animateDpAsState(
|
||||||
|
if (focused) spaceBetweenFocused else spaceBetweenUnfocused,
|
||||||
|
label = "spaceBetween",
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
|
modifier =
|
||||||
|
modifier
|
||||||
|
.layout { measurable, constraints ->
|
||||||
|
val topPaddingPx = (spaceBetweenUnfocused - spaceBetween.value).roundToPx()
|
||||||
|
val bottomPaddingPx = spaceBetween.value.roundToPx()
|
||||||
|
val placeable = measurable.measure(constraints)
|
||||||
|
layout(placeable.width, placeable.height + bottomPaddingPx + topPaddingPx) {
|
||||||
|
placeable.placeRelative(0, topPaddingPx)
|
||||||
|
}
|
||||||
|
}.fillMaxWidth(),
|
||||||
|
content = content,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -53,8 +53,6 @@ fun ViewMoreCard(
|
||||||
showTitle: Boolean = true,
|
showTitle: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val focused by interactionSource.collectIsFocusedAsState()
|
val focused by interactionSource.collectIsFocusedAsState()
|
||||||
val spaceBetween by animateDpAsState(if (focused) 12.dp else 4.dp)
|
|
||||||
val spaceBelow by animateDpAsState(if (focused) 4.dp else 12.dp)
|
|
||||||
var focusedAfterDelay by remember { mutableStateOf(false) }
|
var focusedAfterDelay by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val hideOverlayDelay = 500L
|
val hideOverlayDelay = 500L
|
||||||
|
|
@ -79,7 +77,7 @@ fun ViewMoreCard(
|
||||||
size.height.takeIf { it.isSpecified } ?: (size.height * (1f / aspectRatio.ratio))
|
size.height.takeIf { it.isSpecified } ?: (size.height * (1f / aspectRatio.ratio))
|
||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
verticalArrangement = Arrangement.spacedBy(spaceBetween),
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
Card(
|
Card(
|
||||||
|
|
@ -108,14 +106,7 @@ fun ViewMoreCard(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (showTitle) {
|
if (showTitle) {
|
||||||
Column(
|
SlidingCardText(focused) {
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
|
||||||
modifier =
|
|
||||||
Modifier
|
|
||||||
.width(width)
|
|
||||||
.padding(bottom = spaceBelow),
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.view_more),
|
text = stringResource(R.string.view_more),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@ import android.view.Gravity
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
|
@ -19,6 +22,7 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
|
@ -74,26 +78,32 @@ fun ViewOptionsDialog(
|
||||||
ViewOptionsType.DENSE_LIST -> ViewOptions.LIST_OPTIONS
|
ViewOptionsType.DENSE_LIST -> ViewOptions.LIST_OPTIONS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LazyColumn(
|
Column(
|
||||||
state = columnState,
|
|
||||||
contentPadding = PaddingValues(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.width(256.dp)
|
.width(256.dp)
|
||||||
.heightIn(max = 380.dp)
|
.heightIn(max = 380.dp)
|
||||||
.focusRequester(focusRequester)
|
|
||||||
.background(
|
.background(
|
||||||
MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp),
|
MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp),
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
stickyHeader {
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.view_options),
|
text = stringResource(R.string.view_options),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
LazyColumn(
|
||||||
|
state = columnState,
|
||||||
|
contentPadding = PaddingValues(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
|
) {
|
||||||
items(options, key = { it.title }) { pref ->
|
items(options, key = { it.title }) { pref ->
|
||||||
pref as AppPreference<ViewOptions, Any>
|
pref as AppPreference<ViewOptions, Any>
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
@ -117,6 +127,7 @@ fun ViewOptionsDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the customizable view changes from the user
|
* Stores the customizable view changes from the user
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,11 @@ import android.view.Gravity
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.heightIn
|
import androidx.compose.foundation.layout.heightIn
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
|
@ -19,6 +22,7 @@ import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.platform.LocalView
|
import androidx.compose.ui.platform.LocalView
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
|
@ -54,26 +58,32 @@ fun LiveTvViewOptionsDialog(
|
||||||
window.setGravity(Gravity.END)
|
window.setGravity(Gravity.END)
|
||||||
window.setDimAmount(0f)
|
window.setDimAmount(0f)
|
||||||
}
|
}
|
||||||
LazyColumn(
|
Column(
|
||||||
state = columnState,
|
|
||||||
contentPadding = PaddingValues(16.dp),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.width(256.dp)
|
.width(256.dp)
|
||||||
.heightIn(max = 380.dp)
|
.heightIn(max = 380.dp)
|
||||||
.focusRequester(focusRequester)
|
|
||||||
.background(
|
.background(
|
||||||
MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp),
|
MaterialTheme.colorScheme.surfaceColorAtElevation(6.dp),
|
||||||
shape = RoundedCornerShape(8.dp),
|
shape = RoundedCornerShape(8.dp),
|
||||||
),
|
),
|
||||||
) {
|
) {
|
||||||
stickyHeader {
|
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.view_options),
|
text = stringResource(R.string.view_options),
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
LazyColumn(
|
||||||
|
state = columnState,
|
||||||
|
contentPadding = PaddingValues(16.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||||
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
|
) {
|
||||||
items(liveTvPreferences) { pref ->
|
items(liveTvPreferences) { pref ->
|
||||||
pref as AppPreference<AppPreferences, Any>
|
pref as AppPreference<AppPreferences, Any>
|
||||||
val interactionSource = remember { MutableInteractionSource() }
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
|
@ -92,3 +102,4 @@ fun LiveTvViewOptionsDialog(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -746,7 +746,7 @@ suspend fun findIndexByNumberOrId(
|
||||||
// Start searching from the target number and choose direction from there
|
// Start searching from the target number and choose direction from there
|
||||||
val num = list.getBlocking(listIndex)?.indexNumber
|
val num = list.getBlocking(listIndex)?.indexNumber
|
||||||
if (num.lt(targetNum)) {
|
if (num.lt(targetNum)) {
|
||||||
for (i in listIndex + 1 until list.lastIndex) {
|
for (i in listIndex + 1 until list.size) {
|
||||||
val item = list.getBlocking(i)
|
val item = list.getBlocking(i)
|
||||||
if (checkNumberOrId(targetNum, targetId, item?.indexNumber, item?.id)) {
|
if (checkNumberOrId(targetNum, targetId, item?.indexNumber, item?.id)) {
|
||||||
return i
|
return i
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ class TestFindIndexByNumberOrId {
|
||||||
val epS02E03 = create(3, 2)
|
val epS02E03 = create(3, 2)
|
||||||
val epS02E04 = create(4, 2)
|
val epS02E04 = create(4, 2)
|
||||||
val epS02E05 = create(5, 2)
|
val epS02E05 = create(5, 2)
|
||||||
|
val epS02E06 = create(6, 2)
|
||||||
val epS00E08 = create(8, 0)
|
val epS00E08 = create(8, 0)
|
||||||
val epS00E02 = create(2, 0)
|
val epS00E02 = create(2, 0)
|
||||||
|
|
||||||
|
|
@ -70,6 +71,30 @@ class TestFindIndexByNumberOrId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test last index with zero start`() =
|
||||||
|
runTest {
|
||||||
|
val episodes =
|
||||||
|
BlockingList.of(
|
||||||
|
listOf(
|
||||||
|
create(0, 2),
|
||||||
|
epS02E01,
|
||||||
|
epS02E02,
|
||||||
|
epS02E03,
|
||||||
|
epS02E04,
|
||||||
|
epS02E05,
|
||||||
|
epS02E06,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
findIndexByNumberOrId(
|
||||||
|
targetNum = 6,
|
||||||
|
targetId = epS02E06.id,
|
||||||
|
list = BlockingList.of(episodes),
|
||||||
|
).let { index ->
|
||||||
|
Assert.assertEquals(6, index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Test missing episodes`() =
|
fun `Test missing episodes`() =
|
||||||
runTest {
|
runTest {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue