Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Jetcaster] Fixed to enable SharedElementTransition to run in the Grid list #1492

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalInspectionMode
Expand All @@ -46,7 +45,6 @@ fun PodcastImage(
// TODO: Remove the nested component modifier when shared elements are applied to entire app
imageModifier: Modifier = Modifier,
contentScale: ContentScale = ContentScale.Crop,
placeholderBrush: Brush = thumbnailPlaceholderDefaultBrush(),
) {
if (LocalInspectionMode.current) {
Box(modifier = modifier.background(MaterialTheme.colorScheme.primary))
Expand Down Expand Up @@ -80,14 +78,7 @@ fun PodcastImage(
.fillMaxSize()
)
}
else -> {
Box(
modifier = Modifier
.background(placeholderBrush)
.fillMaxSize()
Comment on lines -84 to -87
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is currently part of the design :) So i think it needs to be left in.
Screenshot 2024-10-21 at 09 47 34

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As you can see, the background created in advance made the UX appear awkward during the image animation, so I removed it. Do you think a rollback is needed?

before_img_box.mp4
after_img_box.mp4


)
}
else -> { /* */ }
}

Image(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,13 @@ fun JetcasterApp(
startDestination = Screen.Home.route
) {
composable(Screen.Home.route) { backStackEntry ->
CompositionLocalProvider(
LocalAnimatedVisibilityScope provides this
) {
MainScreen(
windowSizeClass = adaptiveInfo.windowSizeClass,
navigateToPlayer = { episode ->
appState.navigateToPlayer(episode.uri, backStackEntry)
},
)
}
MainScreen(
windowSizeClass = adaptiveInfo.windowSizeClass,
navigateToPlayer = { episode ->
appState.navigateToPlayer(episode.uri, backStackEntry)
},
animatedContentScope = this,
)
}
composable(Screen.Player.route) {
CompositionLocalProvider(
Expand Down
173 changes: 33 additions & 140 deletions Jetcaster/mobile/src/main/java/com/example/jetcaster/ui/home/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.example.jetcaster.ui.home

import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand All @@ -36,7 +37,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.pager.HorizontalPager
Expand Down Expand Up @@ -74,12 +74,12 @@ import androidx.compose.material3.adaptive.layout.PaneAdaptedValue
import androidx.compose.material3.adaptive.layout.PaneScaffoldDirective
import androidx.compose.material3.adaptive.layout.SupportingPaneScaffold
import androidx.compose.material3.adaptive.layout.SupportingPaneScaffoldRole
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldValue
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
import androidx.compose.material3.adaptive.navigation.rememberSupportingPaneScaffoldNavigator
import androidx.compose.material3.adaptive.occludingVerticalHingeBounds
import androidx.compose.material3.adaptive.separatingVerticalHingeBounds
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand Down Expand Up @@ -113,6 +113,7 @@ import com.example.jetcaster.core.model.PodcastCategoryFilterResult
import com.example.jetcaster.core.model.PodcastInfo
import com.example.jetcaster.core.player.model.PlayerEpisode
import com.example.jetcaster.designsystem.component.PodcastImage
import com.example.jetcaster.ui.LocalAnimatedVisibilityScope
import com.example.jetcaster.ui.home.discover.discoverItems
import com.example.jetcaster.ui.home.library.libraryItems
import com.example.jetcaster.ui.podcast.PodcastDetailsScreen
Expand Down Expand Up @@ -154,15 +155,6 @@ data class HomeState(
private val HomeState.showHomeCategoryTabs: Boolean
get() = featuredPodcasts.isNotEmpty() && homeCategories.isNotEmpty()

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun HomeState.showGrid(
scaffoldValue: ThreePaneScaffoldValue
): Boolean = windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.EXPANDED ||
(
windowSizeClass.windowWidthSizeClass == WindowWidthSizeClass.MEDIUM &&
scaffoldValue[SupportingPaneScaffoldRole.Supporting] == PaneAdaptedValue.Hidden
)

@OptIn(ExperimentalMaterial3AdaptiveApi::class)
private fun <T> ThreePaneScaffoldNavigator<T>.isMainPaneHidden(): Boolean {
return scaffoldValue[SupportingPaneScaffoldRole.Main] == PaneAdaptedValue.Hidden
Expand Down Expand Up @@ -238,6 +230,7 @@ private fun getExcludedVerticalBounds(posture: Posture, hingePolicy: HingePolicy
@Composable
fun MainScreen(
windowSizeClass: WindowSizeClass,
animatedContentScope: AnimatedContentScope,
navigateToPlayer: (EpisodeInfo) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
) {
Expand All @@ -251,6 +244,7 @@ fun MainScreen(
windowSizeClass = windowSizeClass,
navigateToPlayer = navigateToPlayer,
viewModel = viewModel,
animatedContentScope = animatedContentScope
)
}
}
Expand Down Expand Up @@ -299,6 +293,7 @@ fun HomeScreenErrorPreview() {
private fun HomeScreenReady(
uiState: HomeScreenUiState.Ready,
windowSizeClass: WindowSizeClass,
animatedContentScope: AnimatedContentScope,
navigateToPlayer: (EpisodeInfo) -> Unit,
viewModel: HomeViewModel = hiltViewModel()
) {
Expand Down Expand Up @@ -332,13 +327,15 @@ private fun HomeScreenReady(

Surface {
val podcastUri = navigator.currentDestination?.content
val showGrid = homeState.showGrid(navigator.scaffoldValue)
if (podcastUri.isNullOrEmpty()) {
HomeScreen(
homeState = homeState,
showGrid = showGrid,
modifier = Modifier.fillMaxSize()
)
CompositionLocalProvider(
LocalAnimatedVisibilityScope provides animatedContentScope
) {
Comment on lines +331 to +333
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats the reasoning for moving this further down the tree?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed an issue where the animation was being executed in other areas when the AnimationScope was propagated to the SupportingPane. To address this, I modified it so that the scope is passed only to the node where the animation should occur.

Fold_ux.mp4

HomeScreen(
homeState = homeState,
modifier = Modifier.fillMaxSize()
)
}
} else {
SupportingPaneScaffold(
value = navigator.scaffoldValue,
Expand All @@ -364,7 +361,6 @@ private fun HomeScreenReady(
mainPane = {
HomeScreen(
homeState = homeState,
showGrid = showGrid,
modifier = Modifier.fillMaxSize()
)
},
Expand Down Expand Up @@ -445,7 +441,6 @@ private fun HomeScreenBackground(
@Composable
private fun HomeScreen(
homeState: HomeState,
showGrid: Boolean,
modifier: Modifier = Modifier
) {
// Effect that changes the home category selection when there are no subscribed podcasts
Expand Down Expand Up @@ -475,7 +470,6 @@ private fun HomeScreen(
// Main Content
val snackBarText = stringResource(id = R.string.episode_added_to_your_queue)
HomeContent(
showGrid = showGrid,
showHomeCategoryTabs = homeState.showHomeCategoryTabs,
featuredPodcasts = homeState.featuredPodcasts,
selectedHomeCategory = homeState.selectedHomeCategory,
Expand Down Expand Up @@ -505,7 +499,6 @@ private fun HomeScreen(

@Composable
private fun HomeContent(
showGrid: Boolean,
showHomeCategoryTabs: Boolean,
featuredPodcasts: PersistentList<PodcastInfo>,
selectedHomeCategory: HomeCategory,
Expand Down Expand Up @@ -533,124 +526,25 @@ private fun HomeContent(
}
}

// Note: ideally, `HomeContentColumn` and `HomeContentGrid` would be the same implementation
// (i.e. a grid). However, LazyVerticalGrid does not have the concept of a sticky header.
// So we are using two different composables here depending on the provided window size class.
// See: https://issuetracker.google.com/issues/231557184
if (showGrid) {
HomeContentGrid(
pagerState = pagerState,
showHomeCategoryTabs = showHomeCategoryTabs,
featuredPodcasts = featuredPodcasts,
selectedHomeCategory = selectedHomeCategory,
homeCategories = homeCategories,
filterableCategoriesModel = filterableCategoriesModel,
podcastCategoryFilterResult = podcastCategoryFilterResult,
library = library,
modifier = modifier,
onPodcastUnfollowed = onPodcastUnfollowed,
onHomeCategorySelected = onHomeCategorySelected,
onCategorySelected = onCategorySelected,
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
onTogglePodcastFollowed = onTogglePodcastFollowed,
onQueueEpisode = onQueueEpisode,
removeFromQueue = removeFromQueue,
)
} else {
HomeContentColumn(
pagerState = pagerState,
showHomeCategoryTabs = showHomeCategoryTabs,
featuredPodcasts = featuredPodcasts,
selectedHomeCategory = selectedHomeCategory,
homeCategories = homeCategories,
filterableCategoriesModel = filterableCategoriesModel,
podcastCategoryFilterResult = podcastCategoryFilterResult,
library = library,
modifier = modifier,
onPodcastUnfollowed = onPodcastUnfollowed,
onHomeCategorySelected = onHomeCategorySelected,
onCategorySelected = onCategorySelected,
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
onTogglePodcastFollowed = onTogglePodcastFollowed,
onQueueEpisode = onQueueEpisode,
removeFromQueue = removeFromQueue
)
}
}

@Composable
private fun HomeContentColumn(
showHomeCategoryTabs: Boolean,
pagerState: PagerState,
featuredPodcasts: PersistentList<PodcastInfo>,
selectedHomeCategory: HomeCategory,
homeCategories: List<HomeCategory>,
filterableCategoriesModel: FilterableCategoriesModel,
podcastCategoryFilterResult: PodcastCategoryFilterResult,
library: LibraryInfo,
modifier: Modifier = Modifier,
onPodcastUnfollowed: (PodcastInfo) -> Unit,
onHomeCategorySelected: (HomeCategory) -> Unit,
onCategorySelected: (CategoryInfo) -> Unit,
navigateToPodcastDetails: (PodcastInfo) -> Unit,
navigateToPlayer: (EpisodeInfo) -> Unit,
onTogglePodcastFollowed: (PodcastInfo) -> Unit,
onQueueEpisode: (PlayerEpisode) -> Unit,
removeFromQueue: (EpisodeInfo) -> Unit,
) {
LazyColumn(
modifier = modifier.fillMaxSize()
) {
if (featuredPodcasts.isNotEmpty()) {
item {
FollowedPodcastItem(
pagerState = pagerState,
items = featuredPodcasts,
onPodcastUnfollowed = onPodcastUnfollowed,
navigateToPodcastDetails = navigateToPodcastDetails,
modifier = Modifier
.fillMaxWidth()
)
}
}

if (showHomeCategoryTabs) {
item {
HomeCategoryTabs(
categories = homeCategories,
selectedCategory = selectedHomeCategory,
showHorizontalLine = true,
onCategorySelected = onHomeCategorySelected
)
}
}

when (selectedHomeCategory) {
HomeCategory.Library -> {
libraryItems(
library = library,
navigateToPlayer = navigateToPlayer,
onQueueEpisode = onQueueEpisode,
removeFromQueue = removeFromQueue,
)
}

HomeCategory.Discover -> {
discoverItems(
filterableCategoriesModel = filterableCategoriesModel,
podcastCategoryFilterResult = podcastCategoryFilterResult,
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
onCategorySelected = onCategorySelected,
onTogglePodcastFollowed = onTogglePodcastFollowed,
onQueueEpisode = onQueueEpisode,
removeFromQueue = removeFromQueue
)
}
}
}
HomeContentGrid(
pagerState = pagerState,
showHomeCategoryTabs = showHomeCategoryTabs,
featuredPodcasts = featuredPodcasts,
selectedHomeCategory = selectedHomeCategory,
homeCategories = homeCategories,
filterableCategoriesModel = filterableCategoriesModel,
podcastCategoryFilterResult = podcastCategoryFilterResult,
library = library,
modifier = modifier,
onPodcastUnfollowed = onPodcastUnfollowed,
onHomeCategorySelected = onHomeCategorySelected,
onCategorySelected = onCategorySelected,
navigateToPodcastDetails = navigateToPodcastDetails,
navigateToPlayer = navigateToPlayer,
onTogglePodcastFollowed = onTogglePodcastFollowed,
onQueueEpisode = onQueueEpisode,
removeFromQueue = removeFromQueue,
)
}

@Composable
Expand Down Expand Up @@ -960,7 +854,6 @@ private fun PreviewHome() {
)
HomeScreen(
homeState = homeState,
showGrid = false
)
}
}
Expand Down
Loading