mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-24 03:36:51 +00:00
Compare commits
10 Commits
71a9a8b98e
...
0f0c86dcf9
| Author | SHA1 | Date | |
|---|---|---|---|
| 0f0c86dcf9 | |||
| 557679861a | |||
| 64b1032b74 | |||
| 44219d00d8 | |||
| cff0b60b31 | |||
| 9610124af0 | |||
| 185a0c8a85 | |||
| dfaf1079ac | |||
| 65693a9220 | |||
| 58087bb4a5 |
@@ -6,7 +6,6 @@ import androidx.compose.foundation.background
|
|||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -55,22 +54,21 @@ internal fun TvIconButton(
|
|||||||
targetValue = if (isFocused) scheme.primary else Color.Transparent,
|
targetValue = if (isFocused) scheme.primary else Color.Transparent,
|
||||||
label = "border"
|
label = "border"
|
||||||
)
|
)
|
||||||
|
val shape = RoundedCornerShape(50)
|
||||||
|
val iconSize = (size - 24).coerceAtLeast(0).dp
|
||||||
|
|
||||||
Box(
|
val buttonModifier = modifier
|
||||||
modifier = modifier
|
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
scaleX = scale
|
scaleX = scale
|
||||||
scaleY = scale
|
scaleY = scale
|
||||||
}
|
}
|
||||||
.alpha(if (enabled) 1f else 0.4f)
|
.alpha(if (enabled) 1f else 0.4f)
|
||||||
.widthIn(min = if (label == null) size.dp else 104.dp)
|
|
||||||
.height(size.dp)
|
|
||||||
.border(
|
.border(
|
||||||
width = if (isFocused) 2.dp else 0.dp,
|
width = if (isFocused) 2.dp else 0.dp,
|
||||||
color = borderColor,
|
color = borderColor,
|
||||||
shape = RoundedCornerShape(50)
|
shape = shape
|
||||||
)
|
)
|
||||||
.clip(RoundedCornerShape(50))
|
.clip(shape)
|
||||||
.background(
|
.background(
|
||||||
if (isFocused) scheme.primary.copy(alpha = 0.5f)
|
if (isFocused) scheme.primary.copy(alpha = 0.5f)
|
||||||
else scheme.background.copy(alpha = 0.65f)
|
else scheme.background.copy(alpha = 0.65f)
|
||||||
@@ -82,27 +80,32 @@ internal fun TvIconButton(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onFocusChanged { isFocused = it.isFocused }
|
.onFocusChanged { isFocused = it.isFocused }
|
||||||
.clickable(enabled = enabled) { onClick() },
|
.clickable(enabled = enabled) { onClick() }
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
if (label == null) {
|
if (label == null) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
tint = scheme.onBackground,
|
tint = scheme.onBackground,
|
||||||
modifier = Modifier.padding(8.dp)
|
modifier = buttonModifier
|
||||||
|
.size(size.dp)
|
||||||
|
.padding(12.dp)
|
||||||
|
.size(iconSize)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
modifier = buttonModifier
|
||||||
|
.widthIn(min = 104.dp)
|
||||||
|
.height(size.dp)
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = contentDescription,
|
contentDescription = contentDescription,
|
||||||
tint = scheme.onBackground,
|
tint = scheme.onBackground,
|
||||||
modifier = Modifier.size(28.dp)
|
modifier = Modifier.size(iconSize)
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = label,
|
text = label,
|
||||||
@@ -112,5 +115,4 @@ internal fun TvIconButton(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -258,21 +258,21 @@ private fun TvPlayerBottomSection(
|
|||||||
icon = Icons.Outlined.SkipPrevious,
|
icon = Icons.Outlined.SkipPrevious,
|
||||||
contentDescription = "Previous",
|
contentDescription = "Previous",
|
||||||
onClick = onPrevious,
|
onClick = onPrevious,
|
||||||
size = 64,
|
size = 56,
|
||||||
modifier = expandPlaylistModifier
|
modifier = expandPlaylistModifier
|
||||||
)
|
)
|
||||||
TvIconButton(
|
TvIconButton(
|
||||||
icon = Icons.Outlined.Replay10,
|
icon = Icons.Outlined.Replay10,
|
||||||
contentDescription = "Seek backward 10 seconds",
|
contentDescription = "Seek backward 10 seconds",
|
||||||
onClick = { onSeekRelative(-10_000) },
|
onClick = { onSeekRelative(-10_000) },
|
||||||
size = 64,
|
size = 56,
|
||||||
modifier = expandPlaylistModifier
|
modifier = expandPlaylistModifier
|
||||||
)
|
)
|
||||||
TvIconButton(
|
TvIconButton(
|
||||||
icon = if (uiState.isPlaying) Icons.Outlined.Pause else Icons.Outlined.PlayArrow,
|
icon = if (uiState.isPlaying) Icons.Outlined.Pause else Icons.Outlined.PlayArrow,
|
||||||
contentDescription = if (uiState.isPlaying) "Pause" else "Play",
|
contentDescription = if (uiState.isPlaying) "Pause" else "Play",
|
||||||
onClick = onPlayPause,
|
onClick = onPlayPause,
|
||||||
size = 72,
|
size = 64,
|
||||||
modifier = expandPlaylistModifier
|
modifier = expandPlaylistModifier
|
||||||
.focusRequester(focusRequester)
|
.focusRequester(focusRequester)
|
||||||
.testTag(TvPlayerPlayPauseButtonTag)
|
.testTag(TvPlayerPlayPauseButtonTag)
|
||||||
@@ -281,14 +281,14 @@ private fun TvPlayerBottomSection(
|
|||||||
icon = Icons.Outlined.Forward30,
|
icon = Icons.Outlined.Forward30,
|
||||||
contentDescription = "Seek forward 30 seconds",
|
contentDescription = "Seek forward 30 seconds",
|
||||||
onClick = { onSeekRelative(30_000) },
|
onClick = { onSeekRelative(30_000) },
|
||||||
size = 64,
|
size = 56,
|
||||||
modifier = expandPlaylistModifier
|
modifier = expandPlaylistModifier
|
||||||
)
|
)
|
||||||
TvIconButton(
|
TvIconButton(
|
||||||
icon = Icons.Outlined.SkipNext,
|
icon = Icons.Outlined.SkipNext,
|
||||||
contentDescription = "Next",
|
contentDescription = "Next",
|
||||||
onClick = onNext,
|
onClick = onNext,
|
||||||
size = 64,
|
size = 56,
|
||||||
modifier = expandPlaylistModifier
|
modifier = expandPlaylistModifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionLayout
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.togetherWith
|
import androidx.compose.animation.togetherWith
|
||||||
@@ -39,8 +41,11 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
import hu.bbara.purefin.data.SessionBootstrapper
|
import hu.bbara.purefin.data.SessionBootstrapper
|
||||||
import hu.bbara.purefin.jellyfin.JellyfinAuthInterceptor
|
import hu.bbara.purefin.jellyfin.JellyfinAuthInterceptor
|
||||||
import hu.bbara.purefin.data.UserSessionRepository
|
import hu.bbara.purefin.data.UserSessionRepository
|
||||||
|
import hu.bbara.purefin.navigation.LocalHomeMediaSharedBoundsKey
|
||||||
import hu.bbara.purefin.navigation.LocalNavigationBackStack
|
import hu.bbara.purefin.navigation.LocalNavigationBackStack
|
||||||
import hu.bbara.purefin.navigation.LocalNavigationManager
|
import hu.bbara.purefin.navigation.LocalNavigationManager
|
||||||
|
import hu.bbara.purefin.navigation.LocalSetHomeMediaSharedBoundsKey
|
||||||
|
import hu.bbara.purefin.navigation.LocalSharedTransitionScope
|
||||||
import hu.bbara.purefin.navigation.NavigationCommand
|
import hu.bbara.purefin.navigation.NavigationCommand
|
||||||
import hu.bbara.purefin.navigation.NavigationManager
|
import hu.bbara.purefin.navigation.NavigationManager
|
||||||
import hu.bbara.purefin.navigation.Route
|
import hu.bbara.purefin.navigation.Route
|
||||||
@@ -131,6 +136,7 @@ class PurefinActivity : ComponentActivity() {
|
|||||||
return (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
|
return (applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MainApp(
|
fun MainApp(
|
||||||
userSessionRepository: UserSessionRepository,
|
userSessionRepository: UserSessionRepository,
|
||||||
@@ -154,6 +160,7 @@ class PurefinActivity : ComponentActivity() {
|
|||||||
if (isLoggedIn) {
|
if (isLoggedIn) {
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
val backStack = rememberNavBackStack(Route.Home) as NavBackStack<Route>
|
val backStack = rememberNavBackStack(Route.Home) as NavBackStack<Route>
|
||||||
|
var homeMediaSharedBoundsKey by remember { mutableStateOf<String?>(null) }
|
||||||
val appEntryProvider =
|
val appEntryProvider =
|
||||||
entryProvider {
|
entryProvider {
|
||||||
entryBuilders.forEach { builder -> builder() }
|
entryBuilders.forEach { builder -> builder() }
|
||||||
@@ -172,9 +179,15 @@ class PurefinActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedTransitionLayout {
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
LocalNavigationManager provides navigationManager,
|
LocalNavigationManager provides navigationManager,
|
||||||
LocalNavigationBackStack provides backStack.toList()
|
LocalNavigationBackStack provides backStack.toList(),
|
||||||
|
LocalSharedTransitionScope provides this,
|
||||||
|
LocalHomeMediaSharedBoundsKey provides homeMediaSharedBoundsKey,
|
||||||
|
LocalSetHomeMediaSharedBoundsKey provides { key ->
|
||||||
|
homeMediaSharedBoundsKey = key
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
NavDisplay(
|
NavDisplay(
|
||||||
backStack = backStack,
|
backStack = backStack,
|
||||||
@@ -212,6 +225,7 @@ class PurefinActivity : ComponentActivity() {
|
|||||||
entryProvider = appEntryProvider
|
entryProvider = appEntryProvider
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
LoginScreen()
|
LoginScreen()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package hu.bbara.purefin.navigation
|
package hu.bbara.purefin.navigation
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedVisibilityScope
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
import androidx.compose.runtime.ProvidableCompositionLocal
|
import androidx.compose.runtime.ProvidableCompositionLocal
|
||||||
import androidx.compose.runtime.compositionLocalOf
|
import androidx.compose.runtime.compositionLocalOf
|
||||||
import androidx.compose.runtime.staticCompositionLocalOf
|
import androidx.compose.runtime.staticCompositionLocalOf
|
||||||
@@ -8,3 +11,15 @@ val LocalNavigationManager: ProvidableCompositionLocal<NavigationManager> =
|
|||||||
staticCompositionLocalOf { error("NavigationManager not provided") }
|
staticCompositionLocalOf { error("NavigationManager not provided") }
|
||||||
|
|
||||||
val LocalNavigationBackStack = compositionLocalOf<List<Route>> { emptyList() }
|
val LocalNavigationBackStack = compositionLocalOf<List<Route>> { emptyList() }
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
|
val LocalSharedTransitionScope = compositionLocalOf<SharedTransitionScope?> { null }
|
||||||
|
|
||||||
|
val LocalNavSharedAnimatedVisibilityScope =
|
||||||
|
compositionLocalOf<AnimatedVisibilityScope?> { null }
|
||||||
|
|
||||||
|
val LocalHomeMediaSharedBoundsKey = compositionLocalOf<String?> { null }
|
||||||
|
|
||||||
|
val LocalSetHomeMediaSharedBoundsKey = staticCompositionLocalOf<(String) -> Unit> { {} }
|
||||||
|
|
||||||
|
const val HOME_SEARCH_SHARED_BOUNDS_KEY = "home_search_shared_bounds"
|
||||||
|
|||||||
@@ -1,26 +1,52 @@
|
|||||||
package hu.bbara.purefin.navigation
|
package hu.bbara.purefin.navigation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.navigation3.runtime.EntryProviderScope
|
import androidx.navigation3.runtime.EntryProviderScope
|
||||||
|
import androidx.navigation3.ui.LocalNavAnimatedContentScope
|
||||||
import hu.bbara.purefin.ui.screen.library.LibraryScreen
|
import hu.bbara.purefin.ui.screen.library.LibraryScreen
|
||||||
import hu.bbara.purefin.ui.screen.login.LoginScreen
|
import hu.bbara.purefin.ui.screen.login.LoginScreen
|
||||||
import hu.bbara.purefin.ui.screen.AppScreen
|
import hu.bbara.purefin.ui.screen.AppScreen
|
||||||
import hu.bbara.purefin.ui.screen.episode.EpisodeScreen
|
import hu.bbara.purefin.ui.screen.episode.EpisodeScreen
|
||||||
|
import hu.bbara.purefin.ui.screen.home.components.search.HomeSearchFullScreen
|
||||||
import hu.bbara.purefin.ui.screen.movie.MovieScreen
|
import hu.bbara.purefin.ui.screen.movie.MovieScreen
|
||||||
import hu.bbara.purefin.ui.screen.series.SeriesScreen
|
import hu.bbara.purefin.ui.screen.series.SeriesScreen
|
||||||
|
|
||||||
fun EntryProviderScope<Route>.appRouteEntryBuilder() {
|
fun EntryProviderScope<Route>.appRouteEntryBuilder() {
|
||||||
entry<Route.Home> {
|
entry<Route.Home> {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalNavSharedAnimatedVisibilityScope provides LocalNavAnimatedContentScope.current
|
||||||
|
) {
|
||||||
AppScreen()
|
AppScreen()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
entry<Route.HomeSearchRoute> {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalNavSharedAnimatedVisibilityScope provides LocalNavAnimatedContentScope.current
|
||||||
|
) {
|
||||||
|
HomeSearchFullScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
entry<Route.MovieRoute> {
|
entry<Route.MovieRoute> {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalNavSharedAnimatedVisibilityScope provides LocalNavAnimatedContentScope.current
|
||||||
|
) {
|
||||||
MovieScreen(movie = it.item)
|
MovieScreen(movie = it.item)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
entry<Route.SeriesRoute> {
|
entry<Route.SeriesRoute> {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalNavSharedAnimatedVisibilityScope provides LocalNavAnimatedContentScope.current
|
||||||
|
) {
|
||||||
SeriesScreen(series = it.item)
|
SeriesScreen(series = it.item)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
entry<Route.EpisodeRoute> {
|
entry<Route.EpisodeRoute> {
|
||||||
|
CompositionLocalProvider(
|
||||||
|
LocalNavSharedAnimatedVisibilityScope provides LocalNavAnimatedContentScope.current
|
||||||
|
) {
|
||||||
EpisodeScreen(episode = it.item)
|
EpisodeScreen(episode = it.item)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
entry<Route.LibraryRoute> {
|
entry<Route.LibraryRoute> {
|
||||||
LibraryScreen(library = it.library)
|
LibraryScreen(library = it.library)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package hu.bbara.purefin.ui.common.media
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import hu.bbara.purefin.navigation.LocalHomeMediaSharedBoundsKey
|
||||||
|
import hu.bbara.purefin.navigation.LocalNavSharedAnimatedVisibilityScope
|
||||||
|
import hu.bbara.purefin.navigation.LocalSetHomeMediaSharedBoundsKey
|
||||||
|
import hu.bbara.purefin.navigation.LocalSharedTransitionScope
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
fun homeMediaSharedBoundsKey(origin: String, mediaId: UUID): String =
|
||||||
|
"home_media_${origin}_$mediaId"
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
|
@Composable
|
||||||
|
fun Modifier.homeMediaSharedBoundsSource(sharedBoundsKey: String): Modifier {
|
||||||
|
val selectedKey = LocalHomeMediaSharedBoundsKey.current
|
||||||
|
return homeMediaSharedBounds(sharedBoundsKey.takeIf { it == selectedKey })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun Modifier.homeMediaSharedBoundsDestination(): Modifier =
|
||||||
|
homeMediaSharedBounds(LocalHomeMediaSharedBoundsKey.current)
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
|
@Composable
|
||||||
|
fun isHomeMediaSharedBoundsTransitionActive(): Boolean {
|
||||||
|
val sharedBoundsKey = LocalHomeMediaSharedBoundsKey.current
|
||||||
|
val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||||
|
return sharedBoundsKey != null && sharedTransitionScope?.isTransitionActive == true
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun rememberHomeMediaSharedBoundsClick(
|
||||||
|
sharedBoundsKey: String,
|
||||||
|
onClick: () -> Unit
|
||||||
|
): () -> Unit {
|
||||||
|
val selectSharedBoundsKey = LocalSetHomeMediaSharedBoundsKey.current
|
||||||
|
return remember(sharedBoundsKey, onClick, selectSharedBoundsKey) {
|
||||||
|
{
|
||||||
|
selectSharedBoundsKey(sharedBoundsKey)
|
||||||
|
onClick()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun Modifier.homeMediaSharedBounds(sharedBoundsKey: String?): Modifier {
|
||||||
|
val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||||
|
val animatedVisibilityScope = LocalNavSharedAnimatedVisibilityScope.current
|
||||||
|
|
||||||
|
if (
|
||||||
|
sharedBoundsKey == null ||
|
||||||
|
sharedTransitionScope == null ||
|
||||||
|
animatedVisibilityScope == null
|
||||||
|
) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
return with(sharedTransitionScope) {
|
||||||
|
this@homeMediaSharedBounds.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState(key = sharedBoundsKey),
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@ fun AppScreen(
|
|||||||
onProfileClick = {},
|
onProfileClick = {},
|
||||||
onSettingsClick = {},
|
onSettingsClick = {},
|
||||||
onLogoutClick = viewModel::logout,
|
onLogoutClick = viewModel::logout,
|
||||||
|
onSearchClick = viewModel::openSearch,
|
||||||
selectedTab = selectedTab,
|
selectedTab = selectedTab,
|
||||||
onTabSelected = onTabSelected,
|
onTabSelected = onTabSelected,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
@@ -86,6 +87,7 @@ fun AppScreen(
|
|||||||
LibrariesScreen(
|
LibrariesScreen(
|
||||||
items = libraries,
|
items = libraries,
|
||||||
onLibrarySelected = { item -> viewModel.onLibrarySelected(item.id, item.name) },
|
onLibrarySelected = { item -> viewModel.onLibrarySelected(item.id, item.name) },
|
||||||
|
onSearchClick = viewModel::openSearch,
|
||||||
selectedTab = selectedTab,
|
selectedTab = selectedTab,
|
||||||
onTabSelected = onTabSelected,
|
onTabSelected = onTabSelected,
|
||||||
modifier = Modifier.fillMaxSize()
|
modifier = Modifier.fillMaxSize()
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
|||||||
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
||||||
import hu.bbara.purefin.ui.common.media.MediaHero
|
import hu.bbara.purefin.ui.common.media.MediaHero
|
||||||
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsDestination
|
||||||
|
import hu.bbara.purefin.ui.common.media.isHomeMediaSharedBoundsTransitionActive
|
||||||
import hu.bbara.purefin.download.DownloadState
|
import hu.bbara.purefin.download.DownloadState
|
||||||
import hu.bbara.purefin.image.ImageUrlBuilder
|
import hu.bbara.purefin.image.ImageUrlBuilder
|
||||||
import hu.bbara.purefin.navigation.EpisodeDto
|
import hu.bbara.purefin.navigation.EpisodeDto
|
||||||
@@ -117,11 +119,13 @@ private fun EpisodeScreenInternal(
|
|||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
EpisodeTopBar(
|
EpisodeTopBar(
|
||||||
shortcut = topBarShortcut,
|
shortcut = topBarShortcut,
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
@@ -129,6 +133,7 @@ private fun EpisodeScreenInternal(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -136,6 +141,7 @@ private fun EpisodeScreenInternal(
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
EpisodeHeroSection(episode = episode)
|
EpisodeHeroSection(episode = episode)
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
EpisodeDetails(
|
EpisodeDetails(
|
||||||
episode = episode,
|
episode = episode,
|
||||||
downloadState = downloadState,
|
downloadState = downloadState,
|
||||||
@@ -147,6 +153,7 @@ private fun EpisodeScreenInternal(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -160,6 +167,7 @@ private fun EpisodeHeroSection(
|
|||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.homeMediaSharedBoundsDestination()
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(sectionHeight)
|
.height(sectionHeight)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,26 +1,18 @@
|
|||||||
package hu.bbara.purefin.ui.screen.home
|
package hu.bbara.purefin.ui.screen.home
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import hu.bbara.purefin.ui.model.LibraryUiModel
|
import hu.bbara.purefin.ui.model.LibraryUiModel
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
import hu.bbara.purefin.ui.screen.AppBottomBar
|
import hu.bbara.purefin.ui.screen.AppBottomBar
|
||||||
import hu.bbara.purefin.ui.screen.home.components.HomeContent
|
import hu.bbara.purefin.ui.screen.home.components.HomeContent
|
||||||
import hu.bbara.purefin.ui.screen.home.components.HomeTopBar
|
import hu.bbara.purefin.ui.screen.home.components.HomeTopBar
|
||||||
import hu.bbara.purefin.ui.screen.home.components.search.HomeSearchOverlay
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeScreen(
|
fun HomeScreen(
|
||||||
libraries: List<LibraryUiModel>,
|
libraries: List<LibraryUiModel>,
|
||||||
@@ -35,12 +27,11 @@ fun HomeScreen(
|
|||||||
onProfileClick: () -> Unit,
|
onProfileClick: () -> Unit,
|
||||||
onSettingsClick: () -> Unit,
|
onSettingsClick: () -> Unit,
|
||||||
onLogoutClick: () -> Unit,
|
onLogoutClick: () -> Unit,
|
||||||
|
onSearchClick: () -> Unit,
|
||||||
selectedTab: Int,
|
selectedTab: Int,
|
||||||
onTabSelected: (Int) -> Unit,
|
onTabSelected: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
var isSearchVisible by rememberSaveable { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxSize(),
|
.fillMaxSize(),
|
||||||
@@ -48,7 +39,7 @@ fun HomeScreen(
|
|||||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||||
topBar = {
|
topBar = {
|
||||||
HomeTopBar(
|
HomeTopBar(
|
||||||
onSearchClick = { isSearchVisible = true },
|
onSearchClick = onSearchClick,
|
||||||
onProfileClick = onProfileClick,
|
onProfileClick = onProfileClick,
|
||||||
onSettingsClick = onSettingsClick,
|
onSettingsClick = onSettingsClick,
|
||||||
onLogoutClick = onLogoutClick
|
onLogoutClick = onLogoutClick
|
||||||
@@ -61,7 +52,6 @@ fun HomeScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
HomeContent(
|
HomeContent(
|
||||||
libraries = libraries,
|
libraries = libraries,
|
||||||
libraryContent = libraryContent,
|
libraryContent = libraryContent,
|
||||||
@@ -75,23 +65,5 @@ fun HomeScreen(
|
|||||||
onBrowseLibrariesClick = { onTabSelected(1) },
|
onBrowseLibrariesClick = { onTabSelected(1) },
|
||||||
modifier = Modifier.padding(innerPadding)
|
modifier = Modifier.padding(innerPadding)
|
||||||
)
|
)
|
||||||
HomeSearchOverlay(
|
|
||||||
visible = isSearchVisible,
|
|
||||||
topPadding = innerPadding.calculateTopPadding(),
|
|
||||||
onDismiss = { isSearchVisible = false },
|
|
||||||
onMovieSelected = {
|
|
||||||
isSearchVisible = false
|
|
||||||
//TODO use MediaUiModel as well
|
|
||||||
//onMovieSelected(it)
|
|
||||||
},
|
|
||||||
onSeriesSelected = {
|
|
||||||
isSearchVisible = false
|
|
||||||
//onSeriesSelected(it)
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(innerPadding)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,20 @@
|
|||||||
package hu.bbara.purefin.ui.screen.home.components
|
package hu.bbara.purefin.ui.screen.home.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.Search
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.IconButtonColors
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
@@ -18,6 +27,9 @@ import androidx.compose.ui.text.font.FontStyle
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
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 hu.bbara.purefin.navigation.HOME_SEARCH_SHARED_BOUNDS_KEY
|
||||||
|
import hu.bbara.purefin.navigation.LocalNavSharedAnimatedVisibilityScope
|
||||||
|
import hu.bbara.purefin.navigation.LocalSharedTransitionScope
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinLogo
|
import hu.bbara.purefin.ui.common.image.PurefinLogo
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -58,3 +70,51 @@ fun DefaultTopBar(
|
|||||||
modifier = Modifier.padding(end = 12.dp)
|
modifier = Modifier.padding(end = 12.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||||
|
@Composable
|
||||||
|
fun DefaultTopBarSearchButton(
|
||||||
|
onClick: () -> Unit
|
||||||
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||||
|
val animatedVisibilityScope = LocalNavSharedAnimatedVisibilityScope.current
|
||||||
|
|
||||||
|
val searchButtonModifier = if (
|
||||||
|
sharedTransitionScope != null &&
|
||||||
|
animatedVisibilityScope != null
|
||||||
|
) {
|
||||||
|
with(sharedTransitionScope) {
|
||||||
|
Modifier
|
||||||
|
.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState(
|
||||||
|
key = HOME_SEARCH_SHARED_BOUNDS_KEY
|
||||||
|
),
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
|
||||||
|
)
|
||||||
|
.size(50.dp)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier.size(50.dp)
|
||||||
|
}
|
||||||
|
|
||||||
|
IconButton(
|
||||||
|
onClick = onClick,
|
||||||
|
colors = IconButtonColors(
|
||||||
|
containerColor = scheme.surface,
|
||||||
|
contentColor = scheme.onSurface,
|
||||||
|
disabledContainerColor = scheme.surface,
|
||||||
|
disabledContentColor = scheme.onSurface
|
||||||
|
),
|
||||||
|
modifier = searchButtonModifier,
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.Search,
|
||||||
|
contentDescription = "Search",
|
||||||
|
modifier = Modifier.size(30.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
|
||||||
import hu.bbara.purefin.ui.model.LibraryUiModel
|
import hu.bbara.purefin.ui.model.LibraryUiModel
|
||||||
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
import hu.bbara.purefin.ui.screen.home.components.continuewatching.ContinueWatchingSection
|
import hu.bbara.purefin.ui.screen.home.components.continuewatching.ContinueWatchingSection
|
||||||
import hu.bbara.purefin.ui.screen.home.components.featured.SuggestionsSection
|
import hu.bbara.purefin.ui.screen.home.components.featured.SuggestionsSection
|
||||||
import hu.bbara.purefin.ui.screen.home.components.library.LibraryPosterSection
|
import hu.bbara.purefin.ui.screen.home.components.library.LibraryPosterSection
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.compose.foundation.layout.size
|
|||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
import androidx.compose.material.icons.outlined.Search
|
|
||||||
import androidx.compose.material3.DropdownMenu
|
import androidx.compose.material3.DropdownMenu
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -35,22 +34,7 @@ fun HomeTopBar(
|
|||||||
|
|
||||||
DefaultTopBar()
|
DefaultTopBar()
|
||||||
{
|
{
|
||||||
IconButton(
|
DefaultTopBarSearchButton(onClick = onSearchClick)
|
||||||
onClick = onSearchClick,
|
|
||||||
colors = IconButtonColors(
|
|
||||||
containerColor = scheme.surface,
|
|
||||||
contentColor = scheme.onSurface,
|
|
||||||
disabledContainerColor = scheme.surface,
|
|
||||||
disabledContentColor = scheme.onSurface
|
|
||||||
),
|
|
||||||
modifier = Modifier.size(50.dp),
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Search,
|
|
||||||
contentDescription = "Search",
|
|
||||||
modifier = Modifier.size(30.dp),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.size(12.dp))
|
Spacer(modifier = Modifier.size(12.dp))
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = { isProfileMenuExpanded = true },
|
onClick = { isProfileMenuExpanded = true },
|
||||||
|
|||||||
@@ -26,17 +26,23 @@ import androidx.compose.ui.unit.dp
|
|||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
|
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
||||||
|
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ContinueWatchingCard(
|
internal fun ContinueWatchingCard(
|
||||||
item: MediaUiModel,
|
item: MediaUiModel,
|
||||||
|
sharedBoundsKey: String,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
||||||
|
onMediaSelected(item)
|
||||||
|
}
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
onClick = { onMediaSelected(item) },
|
onClick = onClick,
|
||||||
shape = RoundedCornerShape(26.dp),
|
shape = RoundedCornerShape(26.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
|
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
|
||||||
modifier = modifier.width(280.dp)
|
modifier = modifier.width(280.dp)
|
||||||
@@ -44,6 +50,7 @@ internal fun ContinueWatchingCard(
|
|||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.homeMediaSharedBoundsSource(sharedBoundsKey)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(16f / 9f)
|
.aspectRatio(16f / 9f)
|
||||||
.background(scheme.surfaceContainer)
|
.background(scheme.surfaceContainer)
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ package hu.bbara.purefin.ui.screen.home.components.continuewatching
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
|
||||||
import hu.bbara.purefin.ui.common.header.SectionHeader
|
import hu.bbara.purefin.ui.common.header.SectionHeader
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsKey
|
||||||
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContinueWatchingSection(
|
fun ContinueWatchingSection(
|
||||||
@@ -33,15 +36,17 @@ fun ContinueWatchingSection(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
SectionHeader(title = "Continue Watching")
|
SectionHeader(title = "Continue Watching")
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
LazyRow(
|
LazyRow(
|
||||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
state = listState
|
state = listState
|
||||||
) {
|
) {
|
||||||
items(items = items, key = { item -> item.id }) { item ->
|
itemsIndexed(items = items, key = { _, item -> item.id }) { index, item ->
|
||||||
ContinueWatchingCard(
|
ContinueWatchingCard(
|
||||||
item = item,
|
item = item,
|
||||||
|
sharedBoundsKey = homeMediaSharedBoundsKey("continue-$index", item.id),
|
||||||
onMediaSelected = onMediaSelected
|
onMediaSelected = onMediaSelected
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,18 +26,25 @@ import androidx.compose.ui.unit.dp
|
|||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
|
import hu.bbara.purefin.ui.common.bar.MediaProgressBar
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
||||||
|
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun SuggestionCard(
|
internal fun SuggestionCard(
|
||||||
item: MediaUiModel,
|
item: MediaUiModel,
|
||||||
|
sharedBoundsKey: String,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
val description = item.description.trim()
|
val description = item.description.trim()
|
||||||
|
val onCardClick = rememberHomeMediaSharedBoundsClick(
|
||||||
|
sharedBoundsKey = sharedBoundsKey,
|
||||||
|
onClick = onClick
|
||||||
|
)
|
||||||
|
|
||||||
ElevatedCard(
|
ElevatedCard(
|
||||||
onClick = onClick,
|
onClick = onCardClick,
|
||||||
colors = CardDefaults.elevatedCardColors(containerColor = scheme.surfaceContainerLow),
|
colors = CardDefaults.elevatedCardColors(containerColor = scheme.surfaceContainerLow),
|
||||||
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 1.dp),
|
elevation = CardDefaults.elevatedCardElevation(defaultElevation = 1.dp),
|
||||||
shape = RoundedCornerShape(30.dp),
|
shape = RoundedCornerShape(30.dp),
|
||||||
@@ -45,6 +52,7 @@ internal fun SuggestionCard(
|
|||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.homeMediaSharedBoundsSource(sharedBoundsKey)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(16f / 11f)
|
.aspectRatio(16f / 11f)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import androidx.compose.runtime.LaunchedEffect
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsKey
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -49,9 +50,11 @@ fun SuggestionsSection(
|
|||||||
pageSpacing = 16.dp,
|
pageSpacing = 16.dp,
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) { page ->
|
) { page ->
|
||||||
|
val item = items[page]
|
||||||
SuggestionCard(
|
SuggestionCard(
|
||||||
item = items[page],
|
item = item,
|
||||||
onClick = { onItemOpen(items[page]) }
|
sharedBoundsKey = homeMediaSharedBoundsKey("suggestion-$page", item.id),
|
||||||
|
onClick = { onItemOpen(item) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (items.size > 1) {
|
if (items.size > 1) {
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
|
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
||||||
|
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
||||||
import hu.bbara.purefin.ui.model.EpisodeUiModel
|
import hu.bbara.purefin.ui.model.EpisodeUiModel
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
import hu.bbara.purefin.ui.model.MovieUiModel
|
import hu.bbara.purefin.ui.model.MovieUiModel
|
||||||
@@ -32,13 +34,17 @@ import hu.bbara.purefin.ui.model.MovieUiModel
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun HomeBrowseCard(
|
internal fun HomeBrowseCard(
|
||||||
uiModel: MediaUiModel,
|
uiModel: MediaUiModel,
|
||||||
|
sharedBoundsKey: String,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
||||||
|
onMediaSelected(uiModel)
|
||||||
|
}
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
onClick = { onMediaSelected(uiModel) },
|
onClick = onClick,
|
||||||
shape = RoundedCornerShape(12.dp),
|
shape = RoundedCornerShape(12.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
|
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
|
||||||
modifier = modifier.width(188.dp)
|
modifier = modifier.width(188.dp)
|
||||||
@@ -46,6 +52,7 @@ internal fun HomeBrowseCard(
|
|||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.homeMediaSharedBoundsSource(sharedBoundsKey)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(16f / 10f)
|
.aspectRatio(16f / 10f)
|
||||||
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
|
.clip(RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp))
|
||||||
|
|||||||
@@ -3,17 +3,20 @@ package hu.bbara.purefin.ui.screen.home.components.library
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
|
||||||
import hu.bbara.purefin.ui.model.LibraryUiModel
|
|
||||||
import hu.bbara.purefin.ui.common.header.SectionHeader
|
import hu.bbara.purefin.ui.common.header.SectionHeader
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsKey
|
||||||
|
import hu.bbara.purefin.ui.model.LibraryUiModel
|
||||||
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LibraryPosterSection(
|
fun LibraryPosterSection(
|
||||||
@@ -40,15 +43,20 @@ fun LibraryPosterSection(
|
|||||||
actionLabel = "See all",
|
actionLabel = "See all",
|
||||||
onActionClick = { onLibrarySelected(library) }
|
onActionClick = { onLibrarySelected(library) }
|
||||||
)
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
LazyRow(
|
LazyRow(
|
||||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
state = listState
|
state = listState
|
||||||
) {
|
) {
|
||||||
items(items = items, key = { item -> item.id }) { item ->
|
itemsIndexed(items = items, key = { _, item -> item.id }) { index, item ->
|
||||||
HomeBrowseCard(
|
HomeBrowseCard(
|
||||||
uiModel = item,
|
uiModel = item,
|
||||||
|
sharedBoundsKey = homeMediaSharedBoundsKey(
|
||||||
|
origin = "library-${library.id}-$index",
|
||||||
|
mediaId = item.id
|
||||||
|
),
|
||||||
onMediaSelected = onMediaSelected
|
onMediaSelected = onMediaSelected
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,23 @@ import androidx.compose.ui.text.style.TextOverflow
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
||||||
|
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun NextUpCard(
|
internal fun NextUpCard(
|
||||||
uiModel: MediaUiModel,
|
uiModel: MediaUiModel,
|
||||||
|
sharedBoundsKey: String,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
||||||
|
onMediaSelected(uiModel)
|
||||||
|
}
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
onClick = { onMediaSelected(uiModel) },
|
onClick = onClick,
|
||||||
shape = RoundedCornerShape(24.dp),
|
shape = RoundedCornerShape(24.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
|
colors = CardDefaults.cardColors(containerColor = scheme.surfaceContainer),
|
||||||
modifier = modifier.width(256.dp)
|
modifier = modifier.width(256.dp)
|
||||||
@@ -42,6 +48,7 @@ internal fun NextUpCard(
|
|||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
.homeMediaSharedBoundsSource(sharedBoundsKey)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(16f / 10f)
|
.aspectRatio(16f / 10f)
|
||||||
.background(scheme.surface)
|
.background(scheme.surface)
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ package hu.bbara.purefin.ui.screen.home.components.nextup
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.lazy.LazyRow
|
import androidx.compose.foundation.lazy.LazyRow
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import hu.bbara.purefin.ui.model.MediaUiModel
|
|
||||||
import hu.bbara.purefin.ui.common.header.SectionHeader
|
import hu.bbara.purefin.ui.common.header.SectionHeader
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsKey
|
||||||
|
import hu.bbara.purefin.ui.model.MediaUiModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NextUpSection(
|
fun NextUpSection(
|
||||||
@@ -33,15 +36,17 @@ fun NextUpSection(
|
|||||||
modifier = modifier
|
modifier = modifier
|
||||||
) {
|
) {
|
||||||
SectionHeader(title = "Next Up")
|
SectionHeader(title = "Next Up")
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
LazyRow(
|
LazyRow(
|
||||||
contentPadding = PaddingValues(horizontal = 16.dp),
|
contentPadding = PaddingValues(horizontal = 16.dp),
|
||||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
state = listState
|
state = listState
|
||||||
) {
|
) {
|
||||||
items(items = items, key = { item -> item.id }) { item ->
|
itemsIndexed(items = items, key = { _, item -> item.id }) { index, item ->
|
||||||
NextUpCard(
|
NextUpCard(
|
||||||
uiModel = item,
|
uiModel = item,
|
||||||
|
sharedBoundsKey = homeMediaSharedBoundsKey("next-up-$index", item.id),
|
||||||
onMediaSelected = onMediaSelected
|
onMediaSelected = onMediaSelected
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package hu.bbara.purefin.ui.screen.home.components.search
|
package hu.bbara.purefin.ui.screen.home.components.search
|
||||||
|
|
||||||
|
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||||
|
import androidx.compose.animation.SharedTransitionScope
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -51,17 +55,41 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
import hu.bbara.purefin.ui.common.image.PurefinAsyncImage
|
||||||
import hu.bbara.purefin.ui.common.image.PurefinLogo
|
import hu.bbara.purefin.ui.common.image.PurefinLogo
|
||||||
|
import hu.bbara.purefin.navigation.HOME_SEARCH_SHARED_BOUNDS_KEY
|
||||||
|
import hu.bbara.purefin.navigation.LocalNavSharedAnimatedVisibilityScope
|
||||||
|
import hu.bbara.purefin.navigation.LocalSharedTransitionScope
|
||||||
import hu.bbara.purefin.ui.theme.AppTheme
|
import hu.bbara.purefin.ui.theme.AppTheme
|
||||||
|
|
||||||
@OptIn(ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalLayoutApi::class, ExperimentalSharedTransitionApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeSearchFullScreen(
|
fun HomeSearchFullScreen(
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val sharedTransitionScope = LocalSharedTransitionScope.current
|
||||||
|
val animatedVisibilityScope = LocalNavSharedAnimatedVisibilityScope.current
|
||||||
|
val sharedBoundsModifier = if (
|
||||||
|
sharedTransitionScope != null &&
|
||||||
|
animatedVisibilityScope != null
|
||||||
|
) {
|
||||||
|
with(sharedTransitionScope) {
|
||||||
|
Modifier.sharedBounds(
|
||||||
|
sharedContentState = rememberSharedContentState(
|
||||||
|
key = HOME_SEARCH_SHARED_BOUNDS_KEY
|
||||||
|
),
|
||||||
|
animatedVisibilityScope = animatedVisibilityScope,
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Modifier
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.then(sharedBoundsModifier)
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(scheme.background)
|
.background(scheme.background)
|
||||||
.statusBarsPadding()
|
.statusBarsPadding()
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import hu.bbara.purefin.ui.screen.library.components.LibraryTopBar
|
|||||||
fun LibrariesScreen(
|
fun LibrariesScreen(
|
||||||
items: List<LibraryUiModel>,
|
items: List<LibraryUiModel>,
|
||||||
onLibrarySelected: (LibraryUiModel) -> Unit,
|
onLibrarySelected: (LibraryUiModel) -> Unit,
|
||||||
|
onSearchClick: () -> Unit,
|
||||||
selectedTab: Int,
|
selectedTab: Int,
|
||||||
onTabSelected: (Int) -> Unit,
|
onTabSelected: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
@@ -25,7 +26,7 @@ fun LibrariesScreen(
|
|||||||
contentColor = MaterialTheme.colorScheme.onBackground,
|
contentColor = MaterialTheme.colorScheme.onBackground,
|
||||||
topBar = {
|
topBar = {
|
||||||
LibraryTopBar(
|
LibraryTopBar(
|
||||||
onSearchClick = {},
|
onSearchClick = onSearchClick,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
|
|||||||
@@ -1,39 +1,14 @@
|
|||||||
package hu.bbara.purefin.ui.screen.library.components
|
package hu.bbara.purefin.ui.screen.library.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
import androidx.compose.material.icons.Icons
|
|
||||||
import androidx.compose.material.icons.outlined.Search
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.IconButtonColors
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import hu.bbara.purefin.ui.screen.home.components.DefaultTopBar
|
import hu.bbara.purefin.ui.screen.home.components.DefaultTopBar
|
||||||
|
import hu.bbara.purefin.ui.screen.home.components.DefaultTopBarSearchButton
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LibraryTopBar(
|
fun LibraryTopBar(
|
||||||
onSearchClick: () -> Unit,
|
onSearchClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
|
|
||||||
DefaultTopBar {
|
DefaultTopBar {
|
||||||
IconButton(
|
DefaultTopBarSearchButton(onClick = onSearchClick)
|
||||||
onClick = onSearchClick,
|
|
||||||
colors = IconButtonColors(
|
|
||||||
containerColor = scheme.surface,
|
|
||||||
contentColor = scheme.onSurface,
|
|
||||||
disabledContainerColor = scheme.surface,
|
|
||||||
disabledContentColor = scheme.onSurface
|
|
||||||
),
|
|
||||||
modifier = Modifier.size(50.dp),
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Outlined.Search,
|
|
||||||
contentDescription = "Search",
|
|
||||||
modifier = Modifier.size(30.dp),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,6 +39,8 @@ import hu.bbara.purefin.model.Movie
|
|||||||
import hu.bbara.purefin.navigation.MovieDto
|
import hu.bbara.purefin.navigation.MovieDto
|
||||||
import hu.bbara.purefin.ui.common.media.MediaHero
|
import hu.bbara.purefin.ui.common.media.MediaHero
|
||||||
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
import hu.bbara.purefin.ui.common.media.MediaMetadataFlowRow
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsDestination
|
||||||
|
import hu.bbara.purefin.ui.common.media.isHomeMediaSharedBoundsTransitionActive
|
||||||
import hu.bbara.purefin.ui.screen.movie.components.MovieDetails
|
import hu.bbara.purefin.ui.screen.movie.components.MovieDetails
|
||||||
import hu.bbara.purefin.ui.screen.movie.components.MovieTopBar
|
import hu.bbara.purefin.ui.screen.movie.components.MovieTopBar
|
||||||
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
||||||
@@ -94,15 +96,19 @@ private fun MovieScreenInternal(
|
|||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
MovieTopBar(
|
MovieTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -110,6 +116,7 @@ private fun MovieScreenInternal(
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
MediaHeroSection(movie = movie)
|
MediaHeroSection(movie = movie)
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
MovieDetails(
|
MovieDetails(
|
||||||
movie = movie,
|
movie = movie,
|
||||||
downloadState = downloadState,
|
downloadState = downloadState,
|
||||||
@@ -121,6 +128,7 @@ private fun MovieScreenInternal(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -134,6 +142,7 @@ fun MediaHeroSection(
|
|||||||
|
|
||||||
Box (
|
Box (
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.homeMediaSharedBoundsDestination()
|
||||||
.height(sectionHeight)
|
.height(sectionHeight)
|
||||||
) {
|
) {
|
||||||
MediaHero(
|
MediaHero(
|
||||||
|
|||||||
@@ -32,16 +32,13 @@ 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.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
import androidx.media3.common.util.UnstableApi
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.media3.ui.AspectRatioFrameLayout
|
import androidx.media3.ui.AspectRatioFrameLayout
|
||||||
import androidx.media3.ui.PlayerView
|
import androidx.media3.ui.PlayerView
|
||||||
import androidx.media3.ui.SubtitleView
|
import androidx.media3.ui.SubtitleView
|
||||||
import hu.bbara.purefin.player.viewmodel.PlayerViewModel
|
import hu.bbara.purefin.player.viewmodel.PlayerViewModel
|
||||||
import hu.bbara.purefin.ui.common.button.PurefinTextButton
|
|
||||||
import hu.bbara.purefin.ui.common.visual.EmptyValueTimedVisibility
|
import hu.bbara.purefin.ui.common.visual.EmptyValueTimedVisibility
|
||||||
import hu.bbara.purefin.ui.common.visual.ValueChangeTimedVisibility
|
import hu.bbara.purefin.ui.common.visual.ValueChangeTimedVisibility
|
||||||
import hu.bbara.purefin.ui.screen.player.components.PersistentOverlayContainer
|
import hu.bbara.purefin.ui.screen.player.components.PersistentOverlayContainer
|
||||||
@@ -50,6 +47,7 @@ import hu.bbara.purefin.ui.screen.player.components.PlayerControlsOverlay
|
|||||||
import hu.bbara.purefin.ui.screen.player.components.PlayerGesturesLayer
|
import hu.bbara.purefin.ui.screen.player.components.PlayerGesturesLayer
|
||||||
import hu.bbara.purefin.ui.screen.player.components.PlayerLoadingErrorEndCard
|
import hu.bbara.purefin.ui.screen.player.components.PlayerLoadingErrorEndCard
|
||||||
import hu.bbara.purefin.ui.screen.player.components.PlayerQueuePanel
|
import hu.bbara.purefin.ui.screen.player.components.PlayerQueuePanel
|
||||||
|
import hu.bbara.purefin.ui.screen.player.components.SkipSegmentButton
|
||||||
import hu.bbara.purefin.ui.screen.player.components.rememberPersistentOverlayController
|
import hu.bbara.purefin.ui.screen.player.components.rememberPersistentOverlayController
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@@ -216,16 +214,7 @@ fun PlayerScreen(
|
|||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
.padding(end = 24.dp, bottom = 24.dp)
|
.padding(end = 24.dp, bottom = 24.dp)
|
||||||
) {
|
) {
|
||||||
PurefinTextButton(
|
SkipSegmentButton(onClick = { viewModel.skipActiveSegment() })
|
||||||
onClick = { viewModel.skipActiveSegment() },
|
|
||||||
modifier = Modifier.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Skip",
|
|
||||||
fontSize = 20.sp,
|
|
||||||
fontWeight = FontWeight.ExtraBold
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerLoadingErrorEndCard(
|
PlayerLoadingErrorEndCard(
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import androidx.compose.foundation.layout.Spacer
|
|||||||
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.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.ArrowBack
|
import androidx.compose.material.icons.outlined.ArrowBack
|
||||||
import androidx.compose.material.icons.outlined.Cast
|
import androidx.compose.material.icons.outlined.Cast
|
||||||
@@ -22,6 +26,9 @@ import androidx.compose.material.icons.outlined.PlaylistPlay
|
|||||||
import androidx.compose.material.icons.outlined.Replay10
|
import androidx.compose.material.icons.outlined.Replay10
|
||||||
import androidx.compose.material.icons.outlined.SkipNext
|
import androidx.compose.material.icons.outlined.SkipNext
|
||||||
import androidx.compose.material.icons.outlined.SkipPrevious
|
import androidx.compose.material.icons.outlined.SkipPrevious
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
|
import androidx.compose.material3.FilledTonalButton
|
||||||
|
import androidx.compose.material3.Icon
|
||||||
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
|
||||||
@@ -35,12 +42,10 @@ import androidx.compose.ui.graphics.Brush
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import hu.bbara.purefin.player.model.PlayerUiState
|
import hu.bbara.purefin.player.model.PlayerUiState
|
||||||
import hu.bbara.purefin.player.model.TrackOption
|
import hu.bbara.purefin.player.model.TrackOption
|
||||||
import hu.bbara.purefin.ui.common.button.GhostIconButton
|
import hu.bbara.purefin.ui.common.button.GhostIconButton
|
||||||
import hu.bbara.purefin.ui.common.button.PurefinIconButton
|
import hu.bbara.purefin.ui.common.button.PurefinIconButton
|
||||||
import hu.bbara.purefin.ui.common.button.PurefinTextButton
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerControlsOverlay(
|
fun PlayerControlsOverlay(
|
||||||
@@ -174,26 +179,6 @@ private fun BottomSection(
|
|||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
if (uiState.activeSkippableSegmentEndMs != null) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(horizontal = 8.dp),
|
|
||||||
horizontalArrangement = Arrangement.End
|
|
||||||
) {
|
|
||||||
PurefinTextButton(
|
|
||||||
onClick = onSkipSegment,
|
|
||||||
modifier = Modifier.padding(8.dp)
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = "Skip",
|
|
||||||
fontSize = 20.sp,
|
|
||||||
fontWeight = FontWeight.ExtraBold
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(6.dp))
|
|
||||||
}
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -279,7 +264,7 @@ private fun BottomSection(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.align(Alignment.CenterEnd),
|
modifier = Modifier.align(Alignment.CenterStart),
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -302,11 +287,48 @@ private fun BottomSection(
|
|||||||
overlayController = overlayController
|
overlayController = overlayController
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (uiState.activeSkippableSegmentEndMs != null) {
|
||||||
|
SkipSegmentButton(
|
||||||
|
onClick = onSkipSegment,
|
||||||
|
modifier = Modifier.align(Alignment.CenterEnd)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SkipSegmentButton(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier
|
||||||
|
) {
|
||||||
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
|
||||||
|
FilledTonalButton(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = modifier.heightIn(min = 44.dp),
|
||||||
|
shape = RoundedCornerShape(50),
|
||||||
|
colors = ButtonDefaults.filledTonalButtonColors(
|
||||||
|
containerColor = scheme.secondary.copy(alpha = 0.92f),
|
||||||
|
contentColor = scheme.onSecondary
|
||||||
|
),
|
||||||
|
contentPadding = ButtonDefaults.ButtonWithIconContentPadding
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Skip",
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.SkipNext,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun formatTime(positionMs: Long): String {
|
private fun formatTime(positionMs: Long): String {
|
||||||
val totalSeconds = positionMs / 1000
|
val totalSeconds = positionMs / 1000
|
||||||
val seconds = (totalSeconds % 60).toInt()
|
val seconds = (totalSeconds % 60).toInt()
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ import hu.bbara.purefin.model.Series
|
|||||||
import hu.bbara.purefin.navigation.SeriesDto
|
import hu.bbara.purefin.navigation.SeriesDto
|
||||||
import hu.bbara.purefin.ui.common.media.MediaHero
|
import hu.bbara.purefin.ui.common.media.MediaHero
|
||||||
import hu.bbara.purefin.ui.common.media.MediaSynopsis
|
import hu.bbara.purefin.ui.common.media.MediaSynopsis
|
||||||
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsDestination
|
||||||
|
import hu.bbara.purefin.ui.common.media.isHomeMediaSharedBoundsTransitionActive
|
||||||
import hu.bbara.purefin.ui.screen.series.components.CastRow
|
import hu.bbara.purefin.ui.screen.series.components.CastRow
|
||||||
import hu.bbara.purefin.ui.screen.series.components.EpisodeCarousel
|
import hu.bbara.purefin.ui.screen.series.components.EpisodeCarousel
|
||||||
import hu.bbara.purefin.ui.screen.series.components.SeasonTabs
|
import hu.bbara.purefin.ui.screen.series.components.SeasonTabs
|
||||||
@@ -103,6 +105,7 @@ private fun SeriesScreenInternal(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scheme = MaterialTheme.colorScheme
|
val scheme = MaterialTheme.colorScheme
|
||||||
|
val isHomeMediaTransitionActive = isHomeMediaSharedBoundsTransitionActive()
|
||||||
|
|
||||||
fun getDefaultSeason() : Season {
|
fun getDefaultSeason() : Season {
|
||||||
for (season in series.seasons) {
|
for (season in series.seasons) {
|
||||||
@@ -131,11 +134,13 @@ private fun SeriesScreenInternal(
|
|||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
containerColor = MaterialTheme.colorScheme.background,
|
containerColor = MaterialTheme.colorScheme.background,
|
||||||
topBar = {
|
topBar = {
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
SeriesTopBar(
|
SeriesTopBar(
|
||||||
onBack = onBack,
|
onBack = onBack,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -143,6 +148,7 @@ private fun SeriesScreenInternal(
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
) {
|
) {
|
||||||
SeriesHeroSection(series = series)
|
SeriesHeroSection(series = series)
|
||||||
|
if (!isHomeMediaTransitionActive) {
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -189,6 +195,7 @@ private fun SeriesScreenInternal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -202,6 +209,7 @@ private fun SeriesHeroSection(
|
|||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.homeMediaSharedBoundsDestination()
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.height(sectionHeight)
|
.height(sectionHeight)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun CircularIconButton(
|
internal fun CircularIconButton(
|
||||||
@@ -47,6 +48,7 @@ internal fun CircularIconButton(
|
|||||||
targetValue = if (isFocused) focusedBackgroundColor else containerColor,
|
targetValue = if (isFocused) focusedBackgroundColor else containerColor,
|
||||||
label = "background"
|
label = "background"
|
||||||
)
|
)
|
||||||
|
val iconSize = (size - 24.dp).coerceAtLeast(0.dp)
|
||||||
|
|
||||||
FilledIconButton(
|
FilledIconButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
@@ -67,7 +69,8 @@ internal fun CircularIconButton(
|
|||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
contentDescription = contentDescription
|
contentDescription = contentDescription,
|
||||||
|
modifier = Modifier.size(iconSize)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,6 +251,10 @@ class AppViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun openSearch() {
|
||||||
|
navigationManager.navigate(Route.HomeSearchRoute)
|
||||||
|
}
|
||||||
|
|
||||||
fun logout() {
|
fun logout() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
userSessionRepository.setLoggedIn(false)
|
userSessionRepository.setLoggedIn(false)
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ sealed interface Route : NavKey {
|
|||||||
@Serializable
|
@Serializable
|
||||||
data object Home : Route
|
data object Home : Route
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data object HomeSearchRoute : Route
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MovieRoute(val item: MovieDto) : Route
|
data class MovieRoute(val item: MovieDto) : Route
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class EpisodeUiModel : MediaUiModel {
|
|||||||
constructor(episode: Episode) {
|
constructor(episode: Episode) {
|
||||||
id = episode.id
|
id = episode.id
|
||||||
primaryText = episode.seriesName
|
primaryText = episode.seriesName
|
||||||
secondaryText = "${episode.seasonIndex} x ${episode.index} : ${episode.title}"
|
secondaryText = "S${episode.seasonIndex.toString().padStart(2, '0')}xE${episode.index.toString().padStart(2, '0')} : ${episode.title}"
|
||||||
description = episode.synopsis
|
description = episode.synopsis
|
||||||
prefixImageUrl = episode.imageUrlPrefix
|
prefixImageUrl = episode.imageUrlPrefix
|
||||||
progress = (episode.progress?.toFloat() ?: 0f) / 100f
|
progress = (episode.progress?.toFloat() ?: 0f) / 100f
|
||||||
|
|||||||
Reference in New Issue
Block a user