diff --git a/app-tv/src/main/java/hu/bbara/purefin/common/ui/PosterCard.kt b/app-tv/src/main/java/hu/bbara/purefin/common/ui/PosterCard.kt index 7dfb5b3..b95e569 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/common/ui/PosterCard.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/common/ui/PosterCard.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext @@ -93,7 +94,11 @@ fun PosterCard( Column( modifier = modifier .width(posterWidth) - .graphicsLayer { scaleX = scale; scaleY = scale } + .graphicsLayer { + scaleX = scale + scaleY = scale + transformOrigin = TransformOrigin(0.5f, 0f) + } ) { Box() { PurefinAsyncImage( diff --git a/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeContent.kt b/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeContent.kt index 3dbd1dd..cac0c03 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeContent.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeContent.kt @@ -33,15 +33,31 @@ fun TvHomeContent( val visibleLibraries = remember(libraries, libraryContent) { libraries.filter { libraryContent[it.id]?.isEmpty() != true } } - val continueWatchingFocusRequester = remember { FocusRequester() } - val nextUpFocusRequester = remember { FocusRequester() } - val libraryFocusRequesters = remember(visibleLibraries.map { it.id }) { - visibleLibraries.associate { it.id to FocusRequester() } + val continueWatchingSectionFocusRequester = remember { FocusRequester() } + val continueWatchingFirstItemFocusRequester = remember { FocusRequester() } + val nextUpSectionFocusRequester = remember { FocusRequester() } + val nextUpFirstItemFocusRequester = remember { FocusRequester() } + val librarySectionFocusRequesters = remember(visibleLibraries.map { it.id }) { + visibleLibraries.associate { library -> library.id to FocusRequester() } + } + val libraryFirstItemFocusRequesters = remember(visibleLibraries.map { it.id }) { + visibleLibraries.associate { library -> library.id to FocusRequester() } } val firstSectionFocusRequester = when { - continueWatching.isNotEmpty() -> continueWatchingFocusRequester - nextUp.isNotEmpty() -> nextUpFocusRequester - else -> visibleLibraries.firstOrNull()?.let { libraryFocusRequesters[it.id] } + continueWatching.isNotEmpty() -> continueWatchingSectionFocusRequester + nextUp.isNotEmpty() -> nextUpSectionFocusRequester + else -> visibleLibraries.firstOrNull()?.let { librarySectionFocusRequesters[it.id] } + } + val firstVisibleLibrarySectionFocusRequester = visibleLibraries.firstOrNull() + ?.let { librarySectionFocusRequesters[it.id] } + val firstHomeRowBelowContinueWatching = when { + nextUp.isNotEmpty() -> nextUpSectionFocusRequester + else -> firstVisibleLibrarySectionFocusRequester + } + val firstHomeRowAboveLibraries = when { + nextUp.isNotEmpty() -> nextUpSectionFocusRequester + continueWatching.isNotEmpty() -> continueWatchingSectionFocusRequester + else -> null } LaunchedEffect(firstSectionFocusRequester) { @@ -59,11 +75,9 @@ fun TvHomeContent( item { TvContinueWatchingSection( items = continueWatching, - firstItemFocusRequester = continueWatchingFocusRequester, - downFocusRequester = when { - nextUp.isNotEmpty() -> nextUpFocusRequester - else -> visibleLibraries.firstOrNull()?.let { libraryFocusRequesters[it.id] } - }, + sectionFocusRequester = continueWatchingSectionFocusRequester, + firstItemFocusRequester = continueWatchingFirstItemFocusRequester, + downFocusRequester = firstHomeRowBelowContinueWatching, onMovieSelected = onMovieSelected, onEpisodeSelected = onEpisodeSelected ) @@ -74,9 +88,11 @@ fun TvHomeContent( item { TvNextUpSection( items = nextUp, - firstItemFocusRequester = nextUpFocusRequester, - upFocusRequester = continueWatching.takeIf { it.isNotEmpty() }?.let { continueWatchingFocusRequester }, - downFocusRequester = visibleLibraries.firstOrNull()?.let { libraryFocusRequesters[it.id] }, + sectionFocusRequester = nextUpSectionFocusRequester, + firstItemFocusRequester = nextUpFirstItemFocusRequester, + upFocusRequester = continueWatching.takeIf { it.isNotEmpty() } + ?.let { continueWatchingSectionFocusRequester }, + downFocusRequester = firstVisibleLibrarySectionFocusRequester, onEpisodeSelected = onEpisodeSelected ) } @@ -88,28 +104,25 @@ fun TvHomeContent( key = { it.id } ) { item -> val libraryIndex = visibleLibraries.indexOfFirst { it.id == item.id } - val previousLibraryFocusRequester = visibleLibraries + val previousLibrarySectionFocusRequester = visibleLibraries .getOrNull(libraryIndex - 1) - ?.let { libraryFocusRequesters[it.id] } - val nextLibraryFocusRequester = visibleLibraries + ?.let { librarySectionFocusRequesters[it.id] } + val nextLibrarySectionFocusRequester = visibleLibraries .getOrNull(libraryIndex + 1) - ?.let { libraryFocusRequesters[it.id] } + ?.let { librarySectionFocusRequesters[it.id] } TvLibraryPosterSection( title = item.name, items = libraryContent[item.id] ?: emptyList(), action = "See All", - firstItemFocusRequester = libraryFocusRequesters[item.id], + sectionFocusRequester = librarySectionFocusRequesters.getValue(item.id), + firstItemFocusRequester = libraryFirstItemFocusRequesters.getValue(item.id), upFocusRequester = if (libraryIndex == 0) { - when { - nextUp.isNotEmpty() -> nextUpFocusRequester - continueWatching.isNotEmpty() -> continueWatchingFocusRequester - else -> null - } + firstHomeRowAboveLibraries } else { - previousLibraryFocusRequester + previousLibrarySectionFocusRequester }, - downFocusRequester = nextLibraryFocusRequester, + downFocusRequester = nextLibrarySectionFocusRequester, onMovieSelected = onMovieSelected, onSeriesSelected = onSeriesSelected, onEpisodeSelected = onEpisodeSelected diff --git a/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeSections.kt b/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeSections.kt index 3846bde..ae56176 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeSections.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/tv/home/ui/TvHomeSections.kt @@ -32,6 +32,8 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusProperties import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.focus.focusRestorer +import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext @@ -54,6 +56,7 @@ import kotlin.math.nextUp @Composable fun TvContinueWatchingSection( items: List, + sectionFocusRequester: FocusRequester, firstItemFocusRequester: FocusRequester? = null, upFocusRequester: FocusRequester? = null, downFocusRequester: FocusRequester? = null, @@ -67,7 +70,10 @@ fun TvContinueWatchingSection( action = null ) LazyRow( - modifier = modifier.fillMaxWidth(), + modifier = modifier + .fillMaxWidth() + .focusRequester(sectionFocusRequester) + .focusRestorer(firstItemFocusRequester ?: FocusRequester.Default), contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { @@ -161,7 +167,11 @@ fun TvContinueWatchingCard( modifier = modifier .width(cardWidth) .wrapContentHeight() - .graphicsLayer { scaleX = scale; scaleY = scale } + .graphicsLayer { + scaleX = scale + scaleY = scale + transformOrigin = TransformOrigin(0.5f, 0f) + } ) { Box( modifier = Modifier @@ -217,6 +227,7 @@ fun TvContinueWatchingCard( @Composable fun TvNextUpSection( items: List, + sectionFocusRequester: FocusRequester, firstItemFocusRequester: FocusRequester? = null, upFocusRequester: FocusRequester? = null, downFocusRequester: FocusRequester? = null, @@ -229,7 +240,10 @@ fun TvNextUpSection( action = null ) LazyRow( - modifier = modifier.fillMaxWidth(), + modifier = modifier + .fillMaxWidth() + .focusRequester(sectionFocusRequester) + .focusRestorer(firstItemFocusRequester ?: FocusRequester.Default), contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) { @@ -313,7 +327,11 @@ fun TvNextUpCard( modifier = modifier .width(cardWidth) .wrapContentHeight() - .graphicsLayer { scaleX = scale; scaleY = scale } + .graphicsLayer { + scaleX = scale + scaleY = scale + transformOrigin = TransformOrigin(0.5f, 0f) + } ) { Box( modifier = Modifier @@ -364,6 +382,7 @@ fun TvLibraryPosterSection( title: String, items: List, action: String?, + sectionFocusRequester: FocusRequester, firstItemFocusRequester: FocusRequester? = null, upFocusRequester: FocusRequester? = null, downFocusRequester: FocusRequester? = null, @@ -377,7 +396,10 @@ fun TvLibraryPosterSection( action = action ) LazyRow( - modifier = modifier.fillMaxWidth(), + modifier = modifier + .fillMaxWidth() + .focusRequester(sectionFocusRequester) + .focusRestorer(firstItemFocusRequester ?: FocusRequester.Default), contentPadding = PaddingValues(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp) ) {