mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
Fix TV detail screen entry focus
This commit is contained in:
@@ -1,13 +1,18 @@
|
|||||||
package hu.bbara.purefin.app.content.episode
|
package hu.bbara.purefin.app.content.episode
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
import androidx.compose.ui.test.assertCountEquals
|
import androidx.compose.ui.test.assertCountEquals
|
||||||
import androidx.compose.ui.test.assertIsDisplayed
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
import androidx.compose.ui.test.assertIsFocused
|
import androidx.compose.ui.test.assertIsFocused
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.performKeyInput
|
||||||
|
import androidx.compose.ui.test.pressKey
|
||||||
import androidx.compose.ui.test.onAllNodesWithText
|
import androidx.compose.ui.test.onAllNodesWithText
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.input.key.Key
|
||||||
import hu.bbara.purefin.core.data.navigation.Route
|
import hu.bbara.purefin.core.data.navigation.Route
|
||||||
import hu.bbara.purefin.core.model.CastMember
|
import hu.bbara.purefin.core.model.CastMember
|
||||||
import hu.bbara.purefin.core.model.Episode
|
import hu.bbara.purefin.core.model.Episode
|
||||||
@@ -16,13 +21,14 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
class EpisodeScreenContentTest {
|
class EpisodeScreenContentTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun episodeScreenContent_showsSeriesContext_andFocusesPlayButton() {
|
fun episodeScreenContent_showsSeriesContext_andMovesFromBackToPlayButton() {
|
||||||
composeRule.setContent {
|
composeRule.setContent {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
EpisodeScreenContent(
|
EpisodeScreenContent(
|
||||||
@@ -42,8 +48,15 @@ class EpisodeScreenContentTest {
|
|||||||
composeRule.onNodeWithText("Episode 4").assertIsDisplayed()
|
composeRule.onNodeWithText("Episode 4").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
||||||
composeRule.onAllNodesWithText("Playback").assertCountEquals(1)
|
composeRule.onAllNodesWithText("Playback").assertCountEquals(1)
|
||||||
composeRule.onNodeWithTag(EpisodePlayButtonTag).assertIsDisplayed().assertIsFocused()
|
composeRule.onNodeWithTag(EpisodePlayButtonTag).assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Series").assertIsDisplayed()
|
composeRule.onNodeWithText("Series").assertIsDisplayed()
|
||||||
|
composeRule.onNodeWithContentDescription("Back")
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.assertIsFocused()
|
||||||
|
.performKeyInput {
|
||||||
|
pressKey(Key.DirectionDown)
|
||||||
|
}
|
||||||
|
composeRule.onNodeWithTag(EpisodePlayButtonTag).assertIsFocused()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package hu.bbara.purefin.app.content.movie
|
package hu.bbara.purefin.app.content.movie
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
import androidx.compose.ui.test.assertCountEquals
|
import androidx.compose.ui.test.assertCountEquals
|
||||||
import androidx.compose.ui.test.assertIsDisplayed
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
import androidx.compose.ui.test.assertIsFocused
|
import androidx.compose.ui.test.assertIsFocused
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.performKeyInput
|
||||||
|
import androidx.compose.ui.test.pressKey
|
||||||
import androidx.compose.ui.test.onAllNodesWithText
|
import androidx.compose.ui.test.onAllNodesWithText
|
||||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.input.key.Key
|
||||||
import hu.bbara.purefin.core.model.CastMember
|
import hu.bbara.purefin.core.model.CastMember
|
||||||
import hu.bbara.purefin.core.model.Movie
|
import hu.bbara.purefin.core.model.Movie
|
||||||
import hu.bbara.purefin.ui.theme.AppTheme
|
import hu.bbara.purefin.ui.theme.AppTheme
|
||||||
@@ -16,13 +20,14 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
class MovieScreenContentTest {
|
class MovieScreenContentTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun movieScreenContent_showsTvHeader_andFocusesPlayButton() {
|
fun movieScreenContent_focusesBack_thenMovesToPlayButton() {
|
||||||
composeRule.setContent {
|
composeRule.setContent {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
MovieScreenContent(
|
MovieScreenContent(
|
||||||
@@ -38,8 +43,14 @@ class MovieScreenContentTest {
|
|||||||
composeRule.onNodeWithText("Blade Runner 2049").assertIsDisplayed()
|
composeRule.onNodeWithText("Blade Runner 2049").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
||||||
composeRule.onAllNodesWithText("Playback").assertCountEquals(1)
|
composeRule.onAllNodesWithText("Playback").assertCountEquals(1)
|
||||||
composeRule.onNodeWithTag(MoviePlayButtonTag).assertIsDisplayed().assertIsFocused()
|
composeRule.onNodeWithTag(MoviePlayButtonTag).assertIsDisplayed()
|
||||||
composeRule.onNodeWithContentDescription("Back").assertIsDisplayed()
|
composeRule.onNodeWithContentDescription("Back")
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.assertIsFocused()
|
||||||
|
.performKeyInput {
|
||||||
|
pressKey(Key.DirectionDown)
|
||||||
|
}
|
||||||
|
composeRule.onNodeWithTag(MoviePlayButtonTag).assertIsFocused()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sampleMovie(progress: Double?): Movie {
|
private fun sampleMovie(progress: Double?): Movie {
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
package hu.bbara.purefin.app.content.series
|
package hu.bbara.purefin.app.content.series
|
||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.ui.test.ExperimentalTestApi
|
||||||
import androidx.compose.ui.test.assertIsDisplayed
|
import androidx.compose.ui.test.assertIsDisplayed
|
||||||
import androidx.compose.ui.test.assertIsFocused
|
import androidx.compose.ui.test.assertIsFocused
|
||||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||||
|
import androidx.compose.ui.test.performKeyInput
|
||||||
|
import androidx.compose.ui.test.pressKey
|
||||||
|
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.onNodeWithText
|
import androidx.compose.ui.test.onNodeWithText
|
||||||
|
import androidx.compose.ui.input.key.Key
|
||||||
import hu.bbara.purefin.core.model.CastMember
|
import hu.bbara.purefin.core.model.CastMember
|
||||||
import hu.bbara.purefin.core.model.Episode
|
import hu.bbara.purefin.core.model.Episode
|
||||||
import hu.bbara.purefin.core.model.Season
|
import hu.bbara.purefin.core.model.Season
|
||||||
@@ -15,13 +20,14 @@ import org.junit.Rule
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
@OptIn(ExperimentalTestApi::class)
|
||||||
class SeriesScreenContentTest {
|
class SeriesScreenContentTest {
|
||||||
|
|
||||||
@get:Rule
|
@get:Rule
|
||||||
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
val composeRule = createAndroidComposeRule<ComponentActivity>()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun seriesScreenContent_focusesPrimaryAction_whenNextUpExists() {
|
fun seriesScreenContent_movesFromBackToPrimaryAction_whenNextUpExists() {
|
||||||
composeRule.setContent {
|
composeRule.setContent {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
SeriesScreenContent(
|
SeriesScreenContent(
|
||||||
@@ -37,13 +43,20 @@ class SeriesScreenContentTest {
|
|||||||
composeRule.onNodeWithText("Severance").assertIsDisplayed()
|
composeRule.onNodeWithText("Severance").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Up Next").assertIsDisplayed()
|
composeRule.onNodeWithText("Up Next").assertIsDisplayed()
|
||||||
composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsDisplayed().assertIsFocused()
|
composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Season 1").assertIsDisplayed()
|
composeRule.onNodeWithText("Season 1").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Good News About Hell").assertIsDisplayed()
|
composeRule.onNodeWithText("Good News About Hell").assertIsDisplayed()
|
||||||
|
composeRule.onNodeWithContentDescription("Back")
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.assertIsFocused()
|
||||||
|
.performKeyInput {
|
||||||
|
pressKey(Key.DirectionDown)
|
||||||
|
}
|
||||||
|
composeRule.onNodeWithTag(SeriesPlayButtonTag).assertIsFocused()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun seriesScreenContent_focusesFirstSeason_whenNoPlayableEpisodeExists() {
|
fun seriesScreenContent_movesFromBackToFirstSeason_whenNoPlayableEpisodeExists() {
|
||||||
composeRule.setContent {
|
composeRule.setContent {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
SeriesScreenContent(
|
SeriesScreenContent(
|
||||||
@@ -58,7 +71,14 @@ class SeriesScreenContentTest {
|
|||||||
|
|
||||||
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
composeRule.onNodeWithText("Overview").assertIsDisplayed()
|
||||||
composeRule.onNodeWithText("Library Status").assertIsDisplayed()
|
composeRule.onNodeWithText("Library Status").assertIsDisplayed()
|
||||||
composeRule.onNodeWithTag(SeriesFirstSeasonTabTag).assertIsDisplayed().assertIsFocused()
|
composeRule.onNodeWithTag(SeriesFirstSeasonTabTag).assertIsDisplayed()
|
||||||
|
composeRule.onNodeWithContentDescription("Back")
|
||||||
|
.assertIsDisplayed()
|
||||||
|
.assertIsFocused()
|
||||||
|
.performKeyInput {
|
||||||
|
pressKey(Key.DirectionDown)
|
||||||
|
}
|
||||||
|
composeRule.onNodeWithTag(SeriesFirstSeasonTabTag).assertIsFocused()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sampleSeriesWithEpisodes(): Series {
|
private fun sampleSeriesWithEpisodes(): Series {
|
||||||
|
|||||||
@@ -53,12 +53,14 @@ internal fun EpisodeTopBar(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
shortcut: EpisodeTopBarShortcut? = null,
|
shortcut: EpisodeTopBarShortcut? = null,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
backFocusRequester: FocusRequester? = null,
|
||||||
downFocusRequester: FocusRequester? = null
|
downFocusRequester: FocusRequester? = null
|
||||||
) {
|
) {
|
||||||
MediaDetailsTopBar(
|
MediaDetailsTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
shortcut = shortcut?.let { MediaDetailsTopBarShortcut(label = it.label, onClick = it.onClick) },
|
shortcut = shortcut?.let { MediaDetailsTopBarShortcut(label = it.label, onClick = it.onClick) },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
backFocusRequester = backFocusRequester,
|
||||||
downFocusRequester = downFocusRequester
|
downFocusRequester = downFocusRequester
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,19 +83,22 @@ internal fun EpisodeScreenContent(
|
|||||||
onPlay: () -> Unit,
|
onPlay: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val backFocusRequester = remember { FocusRequester() }
|
||||||
val playFocusRequester = remember { FocusRequester() }
|
val playFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
LaunchedEffect(episode.id) {
|
LaunchedEffect(episode.id) {
|
||||||
playFocusRequester.requestFocus()
|
backFocusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
TvMediaDetailScaffold(
|
TvMediaDetailScaffold(
|
||||||
heroImageUrl = episode.heroImageUrl,
|
heroImageUrl = episode.heroImageUrl,
|
||||||
|
resetScrollKey = episode.id,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
topBar = {
|
topBar = {
|
||||||
EpisodeTopBar(
|
EpisodeTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
shortcut = topBarShortcut,
|
shortcut = topBarShortcut,
|
||||||
|
backFocusRequester = backFocusRequester,
|
||||||
downFocusRequester = playFocusRequester,
|
downFocusRequester = playFocusRequester,
|
||||||
modifier = Modifier.align(Alignment.TopStart)
|
modifier = Modifier.align(Alignment.TopStart)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ internal const val MoviePlayButtonTag = "movie-play-button"
|
|||||||
internal fun MovieTopBar(
|
internal fun MovieTopBar(
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
backFocusRequester: FocusRequester? = null,
|
||||||
downFocusRequester: FocusRequester? = null
|
downFocusRequester: FocusRequester? = null
|
||||||
) {
|
) {
|
||||||
MediaDetailsTopBar(
|
MediaDetailsTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
backFocusRequester = backFocusRequester,
|
||||||
downFocusRequester = downFocusRequester
|
downFocusRequester = downFocusRequester
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,18 +52,21 @@ internal fun MovieScreenContent(
|
|||||||
onPlay: () -> Unit,
|
onPlay: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val backFocusRequester = remember { FocusRequester() }
|
||||||
val playFocusRequester = remember { FocusRequester() }
|
val playFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
LaunchedEffect(movie.id) {
|
LaunchedEffect(movie.id) {
|
||||||
playFocusRequester.requestFocus()
|
backFocusRequester.requestFocus()
|
||||||
}
|
}
|
||||||
|
|
||||||
TvMediaDetailScaffold(
|
TvMediaDetailScaffold(
|
||||||
heroImageUrl = movie.heroImageUrl,
|
heroImageUrl = movie.heroImageUrl,
|
||||||
|
resetScrollKey = movie.id,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
topBar = {
|
topBar = {
|
||||||
MovieTopBar(
|
MovieTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
|
backFocusRequester = backFocusRequester,
|
||||||
downFocusRequester = playFocusRequester,
|
downFocusRequester = playFocusRequester,
|
||||||
modifier = Modifier.align(Alignment.TopStart)
|
modifier = Modifier.align(Alignment.TopStart)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -74,11 +74,13 @@ internal const val SeriesFirstSeasonTabTag = "series-first-season-tab"
|
|||||||
internal fun SeriesTopBar(
|
internal fun SeriesTopBar(
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
backFocusRequester: FocusRequester? = null,
|
||||||
downFocusRequester: FocusRequester? = null
|
downFocusRequester: FocusRequester? = null
|
||||||
) {
|
) {
|
||||||
MediaDetailsTopBar(
|
MediaDetailsTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
backFocusRequester = backFocusRequester,
|
||||||
downFocusRequester = downFocusRequester
|
downFocusRequester = downFocusRequester
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,23 +71,22 @@ internal fun SeriesScreenContent(
|
|||||||
season.episodes.firstOrNull { !it.watched }
|
season.episodes.firstOrNull { !it.watched }
|
||||||
} ?: series.seasons.firstOrNull()?.episodes?.firstOrNull()
|
} ?: series.seasons.firstOrNull()?.episodes?.firstOrNull()
|
||||||
}
|
}
|
||||||
|
val backFocusRequester = remember { FocusRequester() }
|
||||||
val playFocusRequester = remember { FocusRequester() }
|
val playFocusRequester = remember { FocusRequester() }
|
||||||
val firstContentFocusRequester = remember { FocusRequester() }
|
val firstContentFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
LaunchedEffect(series.id, nextUpEpisode?.id) {
|
LaunchedEffect(series.id) {
|
||||||
if (nextUpEpisode != null) {
|
backFocusRequester.requestFocus()
|
||||||
playFocusRequester.requestFocus()
|
|
||||||
} else {
|
|
||||||
firstContentFocusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TvMediaDetailScaffold(
|
TvMediaDetailScaffold(
|
||||||
heroImageUrl = series.heroImageUrl,
|
heroImageUrl = series.heroImageUrl,
|
||||||
|
resetScrollKey = series.id,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
topBar = {
|
topBar = {
|
||||||
SeriesTopBar(
|
SeriesTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
|
backFocusRequester = backFocusRequester,
|
||||||
downFocusRequester = nextUpEpisode?.let { playFocusRequester } ?: firstContentFocusRequester,
|
downFocusRequester = nextUpEpisode?.let { playFocusRequester } ?: firstContentFocusRequester,
|
||||||
modifier = Modifier.align(Alignment.TopStart)
|
modifier = Modifier.align(Alignment.TopStart)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
@@ -32,6 +34,7 @@ private val MediaDetailPanelShape = RoundedCornerShape(28.dp)
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun TvMediaDetailScaffold(
|
internal fun TvMediaDetailScaffold(
|
||||||
heroImageUrl: String,
|
heroImageUrl: String,
|
||||||
|
resetScrollKey: Any,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
heroHeightFraction: Float = 0.48f,
|
heroHeightFraction: Float = 0.48f,
|
||||||
topBar: @Composable BoxScope.() -> Unit,
|
topBar: @Composable BoxScope.() -> Unit,
|
||||||
@@ -40,6 +43,11 @@ internal fun TvMediaDetailScaffold(
|
|||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val contentPadding = Modifier.padding(horizontal = MediaDetailHorizontalPadding)
|
val contentPadding = Modifier.padding(horizontal = MediaDetailHorizontalPadding)
|
||||||
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
|
LaunchedEffect(resetScrollKey) {
|
||||||
|
listState.scrollToItem(0)
|
||||||
|
}
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -47,6 +55,7 @@ internal fun TvMediaDetailScaffold(
|
|||||||
.background(scheme.background)
|
.background(scheme.background)
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
|
state = listState,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.FocusRequester
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.focus.focusProperties
|
import androidx.compose.ui.focus.focusProperties
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
@@ -49,6 +50,7 @@ internal fun MediaDetailsTopBar(
|
|||||||
shortcut: MediaDetailsTopBarShortcut? = null,
|
shortcut: MediaDetailsTopBarShortcut? = null,
|
||||||
onCastClick: () -> Unit = {},
|
onCastClick: () -> Unit = {},
|
||||||
onMoreClick: () -> Unit = {},
|
onMoreClick: () -> Unit = {},
|
||||||
|
backFocusRequester: FocusRequester? = null,
|
||||||
downFocusRequester: FocusRequester? = null
|
downFocusRequester: FocusRequester? = null
|
||||||
) {
|
) {
|
||||||
val downModifier = if (downFocusRequester != null) {
|
val downModifier = if (downFocusRequester != null) {
|
||||||
@@ -56,6 +58,11 @@ internal fun MediaDetailsTopBar(
|
|||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier
|
||||||
}
|
}
|
||||||
|
val backModifier = if (backFocusRequester != null) {
|
||||||
|
Modifier.focusRequester(backFocusRequester)
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
@@ -70,7 +77,7 @@ internal fun MediaDetailsTopBar(
|
|||||||
icon = Icons.AutoMirrored.Outlined.ArrowBack,
|
icon = Icons.AutoMirrored.Outlined.ArrowBack,
|
||||||
contentDescription = "Back",
|
contentDescription = "Back",
|
||||||
onClick = onBack,
|
onClick = onBack,
|
||||||
modifier = downModifier
|
modifier = backModifier.then(downModifier)
|
||||||
)
|
)
|
||||||
if (shortcut != null) {
|
if (shortcut != null) {
|
||||||
GhostTextButton(
|
GhostTextButton(
|
||||||
|
|||||||
Reference in New Issue
Block a user