mirror of
https://github.com/bbara04/Purefin.git
synced 2026-03-31 17:10:08 +02:00
refactor color management to use MaterialTheme and implement dynamic color schemes
This commit is contained in:
@@ -22,11 +22,12 @@ fun EpisodeCard(
|
||||
episode: EpisodeUiModel,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(EpisodeBackgroundDark)
|
||||
.background(colors.background)
|
||||
) {
|
||||
val isWide = maxWidth >= 900.dp
|
||||
val contentPadding = if (isWide) 32.dp else 20.dp
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
package hu.bbara.purefin.app.content.episode
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
internal val EpisodePrimary = Color(0xFFDDA73C)
|
||||
internal val EpisodeBackgroundDark = Color(0xFF141414)
|
||||
internal val EpisodeSurfaceDark = Color(0xFF1F1F1F)
|
||||
internal val EpisodeSurfaceBorder = Color(0x1AFFFFFF)
|
||||
internal val EpisodeMuted = Color(0x99FFFFFF)
|
||||
internal val EpisodeMutedStrong = Color(0x66FFFFFF)
|
||||
internal data class EpisodeColors(
|
||||
val primary: Color,
|
||||
val onPrimary: Color,
|
||||
val background: Color,
|
||||
val surface: Color,
|
||||
val surfaceAlt: Color,
|
||||
val surfaceBorder: Color,
|
||||
val textPrimary: Color,
|
||||
val textSecondary: Color,
|
||||
val textMuted: Color,
|
||||
val textMutedStrong: Color
|
||||
)
|
||||
|
||||
@Composable
|
||||
internal fun rememberEpisodeColors(): EpisodeColors {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
return remember(scheme) {
|
||||
EpisodeColors(
|
||||
primary = scheme.primary,
|
||||
onPrimary = scheme.onPrimary,
|
||||
background = scheme.background,
|
||||
surface = scheme.surface,
|
||||
surfaceAlt = scheme.surfaceVariant,
|
||||
surfaceBorder = scheme.outlineVariant,
|
||||
textPrimary = scheme.onBackground,
|
||||
textSecondary = scheme.onSurface,
|
||||
textMuted = scheme.onSurfaceVariant,
|
||||
textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,18 +87,19 @@ private fun GhostIconButton(
|
||||
contentDescription: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(EpisodeBackgroundDark.copy(alpha = 0.4f))
|
||||
.background(colors.background.copy(alpha = 0.4f))
|
||||
.clickable { onClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = Color.White
|
||||
tint = colors.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -110,10 +111,11 @@ internal fun EpisodeHero(
|
||||
isWide: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(height)
|
||||
.background(EpisodeBackgroundDark)
|
||||
.background(colors.background)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = episode.heroImageUrl,
|
||||
@@ -128,8 +130,8 @@ internal fun EpisodeHero(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
EpisodeBackgroundDark.copy(alpha = 0.4f),
|
||||
EpisodeBackgroundDark
|
||||
colors.background.copy(alpha = 0.4f),
|
||||
colors.background
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -142,7 +144,7 @@ internal fun EpisodeHero(
|
||||
Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
EpisodeBackgroundDark.copy(alpha = 0.8f)
|
||||
colors.background.copy(alpha = 0.8f)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -163,10 +165,11 @@ internal fun EpisodeDetails(
|
||||
episode: EpisodeUiModel,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = episode.title,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 32.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
lineHeight = 38.sp
|
||||
@@ -181,9 +184,9 @@ internal fun EpisodeDetails(
|
||||
MetaChip(text = episode.runtime)
|
||||
MetaChip(
|
||||
text = episode.format,
|
||||
background = EpisodePrimary.copy(alpha = 0.2f),
|
||||
border = EpisodePrimary.copy(alpha = 0.3f),
|
||||
textColor = EpisodePrimary
|
||||
background = colors.primary.copy(alpha = 0.2f),
|
||||
border = colors.primary.copy(alpha = 0.3f),
|
||||
textColor = colors.primary
|
||||
)
|
||||
}
|
||||
|
||||
@@ -193,14 +196,14 @@ internal fun EpisodeDetails(
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = "Synopsis",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = episode.synopsis,
|
||||
color = EpisodeMuted,
|
||||
color = colors.textMuted,
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
@@ -211,7 +214,7 @@ internal fun EpisodeDetails(
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
Text(
|
||||
text = "Cast",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -223,23 +226,27 @@ internal fun EpisodeDetails(
|
||||
@Composable
|
||||
private fun MetaChip(
|
||||
text: String,
|
||||
background: Color = Color.White.copy(alpha = 0.1f),
|
||||
border: Color = Color.Transparent,
|
||||
textColor: Color = Color.White
|
||||
background: Color? = null,
|
||||
border: Color? = null,
|
||||
textColor: Color? = null
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
val resolvedBackground = background ?: colors.surfaceAlt
|
||||
val resolvedBorder = border ?: Color.Transparent
|
||||
val resolvedTextColor = textColor ?: colors.textSecondary
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(28.dp)
|
||||
.wrapContentHeight(Alignment.CenterVertically)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(background)
|
||||
.border(width = 1.dp, color = border, shape = RoundedCornerShape(6.dp))
|
||||
.background(resolvedBackground)
|
||||
.border(width = 1.dp, color = resolvedBorder, shape = RoundedCornerShape(6.dp))
|
||||
.padding(horizontal = 12.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
color = textColor,
|
||||
color = resolvedTextColor,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -248,12 +255,13 @@ private fun MetaChip(
|
||||
|
||||
@Composable
|
||||
private fun PlaybackSettings(episode: EpisodeUiModel) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(EpisodeSurfaceDark)
|
||||
.border(1.dp, EpisodeSurfaceBorder, RoundedCornerShape(16.dp))
|
||||
.background(colors.surfaceAlt)
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(16.dp))
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Row(
|
||||
@@ -262,12 +270,12 @@ private fun PlaybackSettings(episode: EpisodeUiModel) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Tune,
|
||||
contentDescription = null,
|
||||
tint = EpisodePrimary
|
||||
tint = colors.primary
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Playback Settings",
|
||||
color = EpisodeMuted,
|
||||
color = colors.textMuted,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
letterSpacing = 2.sp
|
||||
@@ -295,10 +303,11 @@ private fun SettingDropdown(
|
||||
icon: ImageVector,
|
||||
value: String
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Column {
|
||||
Text(
|
||||
text = label,
|
||||
color = EpisodeMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = Modifier.padding(start = 4.dp, bottom = 6.dp)
|
||||
@@ -308,18 +317,18 @@ private fun SettingDropdown(
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(EpisodeBackgroundDark)
|
||||
.border(1.dp, EpisodeSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surface)
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = EpisodeMutedStrong)
|
||||
Icon(imageVector = icon, contentDescription = null, tint = colors.textMutedStrong)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text(text = value, color = Color.White, fontSize = 14.sp)
|
||||
Text(text = value, color = colors.textPrimary, fontSize = 14.sp)
|
||||
}
|
||||
Icon(imageVector = Icons.Outlined.ExpandMore, contentDescription = null, tint = EpisodeMutedStrong)
|
||||
Icon(imageVector = Icons.Outlined.ExpandMore, contentDescription = null, tint = colors.textMutedStrong)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,24 +360,26 @@ private fun ActionButton(
|
||||
icon: ImageVector,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color.White.copy(alpha = 0.05f))
|
||||
.border(1.dp, EpisodeSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f))
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
.clickable { },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = Color.White)
|
||||
Icon(imageVector = icon, contentDescription = null, tint = colors.textPrimary)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = text, color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
||||
Text(text = text, color = colors.textPrimary, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CastRow(cast: List<CastMember>) {
|
||||
val colors = rememberEpisodeColors()
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(horizontal = 4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
@@ -379,19 +390,19 @@ private fun CastRow(cast: List<CastMember>) {
|
||||
modifier = Modifier
|
||||
.aspectRatio(4f / 5f)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(EpisodeSurfaceDark)
|
||||
.background(colors.surfaceAlt)
|
||||
) {
|
||||
if (member.imageUrl == null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.White.copy(alpha = 0.05f)),
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Person,
|
||||
contentDescription = null,
|
||||
tint = EpisodeMutedStrong
|
||||
tint = colors.textMutedStrong
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -406,7 +417,7 @@ private fun CastRow(cast: List<CastMember>) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(
|
||||
text = member.name,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
@@ -414,7 +425,7 @@ private fun CastRow(cast: List<CastMember>) {
|
||||
)
|
||||
Text(
|
||||
text = member.role,
|
||||
color = EpisodeMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 10.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
@@ -430,6 +441,7 @@ private fun PlayButton(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: EpisodeScreenViewModel = hiltViewModel()
|
||||
) {
|
||||
val colors = rememberEpisodeColors()
|
||||
val context = LocalContext.current
|
||||
val episodeItem = viewModel.episode.collectAsState()
|
||||
|
||||
@@ -438,7 +450,7 @@ private fun PlayButton(
|
||||
.size(size)
|
||||
.shadow(24.dp, CircleShape)
|
||||
.clip(CircleShape)
|
||||
.background(EpisodePrimary)
|
||||
.background(colors.primary)
|
||||
.clickable {
|
||||
val intent = Intent(context, PlayerActivity::class.java)
|
||||
intent.putExtra("MEDIA_ID", episodeItem.value!!.id.toString())
|
||||
@@ -449,7 +461,7 @@ private fun PlayButton(
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PlayArrow,
|
||||
contentDescription = "Play",
|
||||
tint = EpisodeBackgroundDark,
|
||||
tint = colors.onPrimary,
|
||||
modifier = Modifier.size(42.dp)
|
||||
)
|
||||
}
|
||||
@@ -457,18 +469,19 @@ private fun PlayButton(
|
||||
|
||||
@Composable
|
||||
internal fun FloatingPlayButton(modifier: Modifier = Modifier) {
|
||||
val colors = rememberEpisodeColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(56.dp)
|
||||
.shadow(20.dp, CircleShape)
|
||||
.clip(CircleShape)
|
||||
.background(EpisodePrimary),
|
||||
.background(colors.primary),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PlayArrow,
|
||||
contentDescription = "Play",
|
||||
tint = EpisodeBackgroundDark
|
||||
tint = colors.onPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,12 @@ fun MovieCard(
|
||||
movie: MovieUiModel,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(MovieBackgroundDark)
|
||||
.background(colors.background)
|
||||
) {
|
||||
val isWide = maxWidth >= 900.dp
|
||||
val contentPadding = if (isWide) 32.dp else 20.dp
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
package hu.bbara.purefin.app.content.movie
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
internal val MoviePrimary = Color(0xFFDDA73C)
|
||||
internal val MovieBackgroundDark = Color(0xFF141414)
|
||||
internal val MovieSurfaceDark = Color(0xFF1F1F1F)
|
||||
internal val MovieSurfaceBorder = Color(0x1AFFFFFF)
|
||||
internal val MovieMuted = Color(0x99FFFFFF)
|
||||
internal val MovieMutedStrong = Color(0x66FFFFFF)
|
||||
internal data class MovieColors(
|
||||
val primary: Color,
|
||||
val onPrimary: Color,
|
||||
val background: Color,
|
||||
val surface: Color,
|
||||
val surfaceAlt: Color,
|
||||
val surfaceBorder: Color,
|
||||
val textPrimary: Color,
|
||||
val textSecondary: Color,
|
||||
val textMuted: Color,
|
||||
val textMutedStrong: Color
|
||||
)
|
||||
|
||||
@Composable
|
||||
internal fun rememberMovieColors(): MovieColors {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
return remember(scheme) {
|
||||
MovieColors(
|
||||
primary = scheme.primary,
|
||||
onPrimary = scheme.onPrimary,
|
||||
background = scheme.background,
|
||||
surface = scheme.surface,
|
||||
surfaceAlt = scheme.surfaceVariant,
|
||||
surfaceBorder = scheme.outlineVariant,
|
||||
textPrimary = scheme.onBackground,
|
||||
textSecondary = scheme.onSurface,
|
||||
textMuted = scheme.onSurfaceVariant,
|
||||
textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,18 +87,19 @@ private fun GhostIconButton(
|
||||
onClick: () -> Unit = {},
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(MovieBackgroundDark.copy(alpha = 0.4f))
|
||||
.background(colors.background.copy(alpha = 0.4f))
|
||||
.clickable { onClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = Color.White
|
||||
tint = colors.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -110,10 +111,11 @@ internal fun MovieHero(
|
||||
isWide: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(height)
|
||||
.background(MovieBackgroundDark)
|
||||
.background(colors.background)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = movie.heroImageUrl,
|
||||
@@ -128,8 +130,8 @@ internal fun MovieHero(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
MovieBackgroundDark.copy(alpha = 0.4f),
|
||||
MovieBackgroundDark
|
||||
colors.background.copy(alpha = 0.4f),
|
||||
colors.background
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -141,7 +143,7 @@ internal fun MovieHero(
|
||||
.background(
|
||||
Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent, MovieBackgroundDark.copy(alpha = 0.8f)
|
||||
Color.Transparent, colors.background.copy(alpha = 0.8f)
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -162,10 +164,11 @@ internal fun MovieDetails(
|
||||
movie: MovieUiModel,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
Column(modifier = modifier) {
|
||||
Text(
|
||||
text = movie.title,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 32.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
lineHeight = 38.sp
|
||||
@@ -180,9 +183,9 @@ internal fun MovieDetails(
|
||||
MetaChip(text = movie.runtime)
|
||||
MetaChip(
|
||||
text = movie.format,
|
||||
background = MoviePrimary.copy(alpha = 0.2f),
|
||||
border = MoviePrimary.copy(alpha = 0.3f),
|
||||
textColor = MoviePrimary
|
||||
background = colors.primary.copy(alpha = 0.2f),
|
||||
border = colors.primary.copy(alpha = 0.3f),
|
||||
textColor = colors.primary
|
||||
)
|
||||
}
|
||||
|
||||
@@ -192,14 +195,14 @@ internal fun MovieDetails(
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = "Synopsis",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = movie.synopsis,
|
||||
color = MovieMuted,
|
||||
color = colors.textMuted,
|
||||
fontSize = 15.sp,
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
@@ -210,7 +213,7 @@ internal fun MovieDetails(
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
Text(
|
||||
text = "Cast",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -222,23 +225,27 @@ internal fun MovieDetails(
|
||||
@Composable
|
||||
private fun MetaChip(
|
||||
text: String,
|
||||
background: Color = Color.White.copy(alpha = 0.1f),
|
||||
border: Color = Color.Transparent,
|
||||
textColor: Color = Color.White
|
||||
background: Color? = null,
|
||||
border: Color? = null,
|
||||
textColor: Color? = null
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
val resolvedBackground = background ?: colors.surfaceAlt
|
||||
val resolvedBorder = border ?: Color.Transparent
|
||||
val resolvedTextColor = textColor ?: colors.textSecondary
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(28.dp)
|
||||
.wrapContentHeight(Alignment.CenterVertically)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(background)
|
||||
.border(width = 1.dp, color = border, shape = RoundedCornerShape(6.dp))
|
||||
.background(resolvedBackground)
|
||||
.border(width = 1.dp, color = resolvedBorder, shape = RoundedCornerShape(6.dp))
|
||||
.padding(horizontal = 12.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
color = textColor,
|
||||
color = resolvedTextColor,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -247,12 +254,13 @@ private fun MetaChip(
|
||||
|
||||
@Composable
|
||||
private fun PlaybackSettings(movie: MovieUiModel) {
|
||||
val colors = rememberMovieColors()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(MovieSurfaceDark)
|
||||
.border(1.dp, MovieSurfaceBorder, RoundedCornerShape(16.dp))
|
||||
.background(colors.surfaceAlt)
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(16.dp))
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Row(
|
||||
@@ -261,12 +269,12 @@ private fun PlaybackSettings(movie: MovieUiModel) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Tune,
|
||||
contentDescription = null,
|
||||
tint = MoviePrimary
|
||||
tint = colors.primary
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = "Playback Settings",
|
||||
color = MovieMuted,
|
||||
color = colors.textMuted,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
letterSpacing = 2.sp
|
||||
@@ -294,10 +302,11 @@ private fun SettingDropdown(
|
||||
icon: ImageVector,
|
||||
value: String
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
Column {
|
||||
Text(
|
||||
text = label,
|
||||
color = MovieMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
modifier = Modifier.padding(start = 4.dp, bottom = 6.dp)
|
||||
@@ -307,18 +316,18 @@ private fun SettingDropdown(
|
||||
.fillMaxWidth()
|
||||
.height(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(MovieBackgroundDark)
|
||||
.border(1.dp, MovieSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surface)
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = MovieMutedStrong)
|
||||
Icon(imageVector = icon, contentDescription = null, tint = colors.textMutedStrong)
|
||||
Spacer(modifier = Modifier.width(10.dp))
|
||||
Text(text = value, color = Color.White, fontSize = 14.sp)
|
||||
Text(text = value, color = colors.textPrimary, fontSize = 14.sp)
|
||||
}
|
||||
Icon(imageVector = Icons.Outlined.ExpandMore, contentDescription = null, tint = MovieMutedStrong)
|
||||
Icon(imageVector = Icons.Outlined.ExpandMore, contentDescription = null, tint = colors.textMutedStrong)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,24 +359,26 @@ private fun ActionButton(
|
||||
icon: ImageVector,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color.White.copy(alpha = 0.05f))
|
||||
.border(1.dp, MovieSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f))
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
.clickable { },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = Color.White)
|
||||
Icon(imageVector = icon, contentDescription = null, tint = colors.textPrimary)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = text, color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
||||
Text(text = text, color = colors.textPrimary, fontSize = 14.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun CastRow(cast: List<CastMember>) {
|
||||
val colors = rememberMovieColors()
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(horizontal = 4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
@@ -378,19 +389,19 @@ private fun CastRow(cast: List<CastMember>) {
|
||||
modifier = Modifier
|
||||
.aspectRatio(4f / 5f)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(MovieSurfaceDark)
|
||||
.background(colors.surfaceAlt)
|
||||
) {
|
||||
if (member.imageUrl == null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.White.copy(alpha = 0.05f)),
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Person,
|
||||
contentDescription = null,
|
||||
tint = MovieMutedStrong
|
||||
tint = colors.textMutedStrong
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -405,7 +416,7 @@ private fun CastRow(cast: List<CastMember>) {
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(
|
||||
text = member.name,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
@@ -413,7 +424,7 @@ private fun CastRow(cast: List<CastMember>) {
|
||||
)
|
||||
Text(
|
||||
text = member.role,
|
||||
color = MovieMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 10.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
@@ -429,6 +440,7 @@ private fun PlayButton(
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: MovieScreenViewModel = hiltViewModel()
|
||||
) {
|
||||
val colors = rememberMovieColors()
|
||||
val context = LocalContext.current
|
||||
val movieId = viewModel.movie.collectAsState()
|
||||
|
||||
@@ -437,7 +449,7 @@ private fun PlayButton(
|
||||
.size(size)
|
||||
.shadow(24.dp, CircleShape)
|
||||
.clip(CircleShape)
|
||||
.background(MoviePrimary)
|
||||
.background(colors.primary)
|
||||
.clickable {
|
||||
val intent = Intent(context, PlayerActivity::class.java)
|
||||
intent.putExtra("MEDIA_ID", movieId.value!!.id.toString())
|
||||
@@ -448,7 +460,7 @@ private fun PlayButton(
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PlayArrow,
|
||||
contentDescription = "Play",
|
||||
tint = MovieBackgroundDark,
|
||||
tint = colors.onPrimary,
|
||||
modifier = Modifier.size(42.dp)
|
||||
)
|
||||
}
|
||||
@@ -456,18 +468,19 @@ private fun PlayButton(
|
||||
|
||||
@Composable
|
||||
internal fun FloatingPlayButton(modifier: Modifier = Modifier) {
|
||||
val colors = rememberMovieColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(56.dp)
|
||||
.shadow(20.dp, CircleShape)
|
||||
.clip(CircleShape)
|
||||
.background(MoviePrimary),
|
||||
.background(colors.primary),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PlayArrow,
|
||||
contentDescription = "Play",
|
||||
tint = MovieBackgroundDark
|
||||
tint = colors.onPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -26,11 +25,12 @@ fun SeriesCard(
|
||||
series: SeriesUiModel,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val colors = rememberSeriesColors()
|
||||
|
||||
BoxWithConstraints(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.background(SeriesBackgroundDark)
|
||||
.background(colors.background)
|
||||
) {
|
||||
val heroHeight = maxHeight * 0.4f
|
||||
Column(
|
||||
@@ -54,7 +54,7 @@ fun SeriesCard(
|
||||
) {
|
||||
Text(
|
||||
text = series.title,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 30.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
lineHeight = 36.sp
|
||||
@@ -66,20 +66,20 @@ fun SeriesCard(
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Text(
|
||||
text = "Synopsis",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = series.synopsis,
|
||||
color = SeriesMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 13.sp,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(28.dp))
|
||||
Text(
|
||||
text = "Episodes",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -101,7 +101,7 @@ fun SeriesCard(
|
||||
) {
|
||||
Text(
|
||||
text = "Cast",
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier.padding(horizontal = 20.dp)
|
||||
|
||||
@@ -1,10 +1,38 @@
|
||||
package hu.bbara.purefin.app.content.series
|
||||
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
internal val SeriesPrimary = Color(0xFFDDA73C)
|
||||
internal val SeriesBackgroundDark = Color(0xFF141414)
|
||||
internal val SeriesSurfaceDark = Color(0xFF1F1F1F)
|
||||
internal val SeriesSurfaceBorder = Color(0x1AFFFFFF)
|
||||
internal val SeriesMuted = Color(0xB3FFFFFF)
|
||||
internal val SeriesMutedStrong = Color(0x99FFFFFF)
|
||||
internal data class SeriesColors(
|
||||
val primary: Color,
|
||||
val onPrimary: Color,
|
||||
val background: Color,
|
||||
val surface: Color,
|
||||
val surfaceAlt: Color,
|
||||
val surfaceBorder: Color,
|
||||
val textPrimary: Color,
|
||||
val textSecondary: Color,
|
||||
val textMuted: Color,
|
||||
val textMutedStrong: Color
|
||||
)
|
||||
|
||||
@Composable
|
||||
internal fun rememberSeriesColors(): SeriesColors {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
return remember(scheme) {
|
||||
SeriesColors(
|
||||
primary = scheme.primary,
|
||||
onPrimary = scheme.onPrimary,
|
||||
background = scheme.background,
|
||||
surface = scheme.surface,
|
||||
surfaceAlt = scheme.surfaceVariant,
|
||||
surfaceBorder = scheme.outlineVariant,
|
||||
textPrimary = scheme.onBackground,
|
||||
textSecondary = scheme.onSurface,
|
||||
textMuted = scheme.onSurfaceVariant,
|
||||
textMutedStrong = scheme.onSurfaceVariant.copy(alpha = 0.7f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,18 +81,19 @@ private fun GhostIconButton(
|
||||
contentDescription: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberSeriesColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(40.dp)
|
||||
.clip(CircleShape)
|
||||
.background(SeriesBackgroundDark.copy(alpha = 0.4f))
|
||||
.background(colors.background.copy(alpha = 0.4f))
|
||||
.clickable { onClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = contentDescription,
|
||||
tint = Color.White
|
||||
tint = colors.textPrimary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -103,10 +104,11 @@ internal fun SeriesHero(
|
||||
height: Dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberSeriesColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.height(height)
|
||||
.background(SeriesBackgroundDark)
|
||||
.background(colors.background)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = imageUrl,
|
||||
@@ -121,8 +123,8 @@ internal fun SeriesHero(
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
SeriesBackgroundDark.copy(alpha = 0.4f),
|
||||
SeriesBackgroundDark
|
||||
colors.background.copy(alpha = 0.4f),
|
||||
colors.background
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -133,6 +135,7 @@ internal fun SeriesHero(
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
internal fun SeriesMetaChips(series: SeriesUiModel) {
|
||||
val colors = rememberSeriesColors()
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
@@ -142,9 +145,9 @@ internal fun SeriesMetaChips(series: SeriesUiModel) {
|
||||
MetaChip(text = series.seasons)
|
||||
MetaChip(
|
||||
text = series.format,
|
||||
background = SeriesPrimary.copy(alpha = 0.2f),
|
||||
border = SeriesPrimary.copy(alpha = 0.3f),
|
||||
textColor = SeriesPrimary
|
||||
background = colors.primary.copy(alpha = 0.2f),
|
||||
border = colors.primary.copy(alpha = 0.3f),
|
||||
textColor = colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -152,23 +155,27 @@ internal fun SeriesMetaChips(series: SeriesUiModel) {
|
||||
@Composable
|
||||
private fun MetaChip(
|
||||
text: String,
|
||||
background: Color = Color.White.copy(alpha = 0.1f),
|
||||
border: Color = Color.White.copy(alpha = 0.05f),
|
||||
textColor: Color = Color.White
|
||||
background: Color? = null,
|
||||
border: Color? = null,
|
||||
textColor: Color? = null
|
||||
) {
|
||||
val colors = rememberSeriesColors()
|
||||
val resolvedBackground = background ?: colors.surfaceAlt
|
||||
val resolvedBorder = border ?: colors.surfaceBorder
|
||||
val resolvedTextColor = textColor ?: colors.textSecondary
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(28.dp)
|
||||
.wrapContentHeight(Alignment.CenterVertically)
|
||||
.clip(RoundedCornerShape(6.dp))
|
||||
.background(background)
|
||||
.border(width = 1.dp, color = border, shape = RoundedCornerShape(6.dp))
|
||||
.background(resolvedBackground)
|
||||
.border(width = 1.dp, color = resolvedBorder, shape = RoundedCornerShape(6.dp))
|
||||
.padding(horizontal = 12.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
color = textColor,
|
||||
color = resolvedTextColor,
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -200,19 +207,20 @@ private fun ActionButton(
|
||||
icon: ImageVector,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val colors = rememberSeriesColors()
|
||||
Row(
|
||||
modifier = modifier
|
||||
.height(44.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color.White.copy(alpha = 0.1f))
|
||||
.border(1.dp, SeriesSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f))
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
.clickable { },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center
|
||||
) {
|
||||
Icon(imageVector = icon, contentDescription = null, tint = Color.White, modifier = Modifier.size(18.dp))
|
||||
Icon(imageVector = icon, contentDescription = null, tint = colors.textPrimary, modifier = Modifier.size(18.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = text, color = Color.White, fontSize = 13.sp, fontWeight = FontWeight.Bold)
|
||||
Text(text = text, color = colors.textPrimary, fontSize = 13.sp, fontWeight = FontWeight.Bold)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,8 +240,9 @@ internal fun SeasonTabs(seasons: List<SeriesSeasonUiModel>, modifier: Modifier =
|
||||
|
||||
@Composable
|
||||
private fun SeasonTab(name: String, isSelected: Boolean) {
|
||||
val color = if (isSelected) SeriesPrimary else SeriesMutedStrong
|
||||
val borderColor = if (isSelected) SeriesPrimary else Color.Transparent
|
||||
val colors = rememberSeriesColors()
|
||||
val color = if (isSelected) colors.primary else colors.textMutedStrong
|
||||
val borderColor = if (isSelected) colors.primary else Color.Transparent
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 8.dp)
|
||||
@@ -273,12 +282,13 @@ private fun EpisodeCard(
|
||||
viewModel: SeriesViewModel = hiltViewModel(),
|
||||
episode: SeriesEpisodeUiModel
|
||||
) {
|
||||
val colors = rememberSeriesColors()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.width(260.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(SeriesSurfaceDark.copy(alpha = 0.3f))
|
||||
.border(1.dp, SeriesSurfaceBorder, RoundedCornerShape(16.dp))
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f))
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(16.dp))
|
||||
.padding(12.dp)
|
||||
.clickable { viewModel.onSelectEpisode(episode.id) },
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
@@ -288,8 +298,8 @@ private fun EpisodeCard(
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(16f / 9f)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(SeriesSurfaceDark)
|
||||
.border(1.dp, SeriesSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surface)
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
) {
|
||||
AsyncImage(
|
||||
model = episode.imageUrl,
|
||||
@@ -300,12 +310,12 @@ private fun EpisodeCard(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(Color.Black.copy(alpha = 0.2f))
|
||||
.background(colors.background.copy(alpha = 0.2f))
|
||||
)
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.PlayCircle,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
tint = colors.textPrimary,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(32.dp)
|
||||
@@ -314,12 +324,12 @@ private fun EpisodeCard(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomEnd)
|
||||
.padding(6.dp)
|
||||
.background(Color.Black.copy(alpha = 0.8f), RoundedCornerShape(6.dp))
|
||||
.background(colors.background.copy(alpha = 0.8f), RoundedCornerShape(6.dp))
|
||||
.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
) {
|
||||
Text(
|
||||
text = episode.duration,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 10.sp,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -330,7 +340,7 @@ private fun EpisodeCard(
|
||||
) {
|
||||
Text(
|
||||
text = episode.title,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 13.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
@@ -338,7 +348,7 @@ private fun EpisodeCard(
|
||||
)
|
||||
Text(
|
||||
text = episode.description,
|
||||
color = SeriesMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
maxLines = 2,
|
||||
@@ -363,6 +373,7 @@ internal fun CastRow(cast: List<SeriesCastMemberUiModel>, modifier: Modifier = M
|
||||
|
||||
@Composable
|
||||
private fun CastCard(member: SeriesCastMemberUiModel) {
|
||||
val colors = rememberSeriesColors()
|
||||
Column(
|
||||
modifier = Modifier.width(84.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp)
|
||||
@@ -371,20 +382,20 @@ private fun CastCard(member: SeriesCastMemberUiModel) {
|
||||
modifier = Modifier
|
||||
.aspectRatio(4f / 5f)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(SeriesSurfaceDark)
|
||||
.border(1.dp, SeriesSurfaceBorder, RoundedCornerShape(12.dp))
|
||||
.background(colors.surfaceAlt)
|
||||
.border(1.dp, colors.surfaceBorder, RoundedCornerShape(12.dp))
|
||||
) {
|
||||
if (member.imageUrl == null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.White.copy(alpha = 0.05f)),
|
||||
.background(colors.surfaceAlt.copy(alpha = 0.6f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Person,
|
||||
contentDescription = null,
|
||||
tint = SeriesMutedStrong
|
||||
tint = colors.textMutedStrong
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -398,7 +409,7 @@ private fun CastCard(member: SeriesCastMemberUiModel) {
|
||||
}
|
||||
Text(
|
||||
text = member.name,
|
||||
color = Color.White,
|
||||
color = colors.textPrimary,
|
||||
fontSize = 11.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
maxLines = 1,
|
||||
@@ -406,7 +417,7 @@ private fun CastCard(member: SeriesCastMemberUiModel) {
|
||||
)
|
||||
Text(
|
||||
text = member.role,
|
||||
color = SeriesMutedStrong,
|
||||
color = colors.textMutedStrong,
|
||||
fontSize = 10.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
@@ -416,19 +427,20 @@ private fun CastCard(member: SeriesCastMemberUiModel) {
|
||||
|
||||
@Composable
|
||||
private fun PlayButton(size: Dp, modifier: Modifier = Modifier) {
|
||||
val colors = rememberSeriesColors()
|
||||
Box(
|
||||
modifier = modifier
|
||||
.size(size)
|
||||
.shadow(24.dp, CircleShape)
|
||||
.clip(CircleShape)
|
||||
.background(SeriesPrimary)
|
||||
.background(colors.primary)
|
||||
.clickable { },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.PlayArrow,
|
||||
contentDescription = "Play",
|
||||
tint = SeriesBackgroundDark,
|
||||
tint = colors.onPrimary,
|
||||
modifier = Modifier.size(36.dp)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package hu.bbara.purefin.app.home.ui
|
||||
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -20,21 +20,21 @@ data class HomeColors(
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun rememberHomeColors(isDark: Boolean = isSystemInDarkTheme()): HomeColors {
|
||||
val primary = Color(0xFFDDA73C)
|
||||
return remember(isDark) {
|
||||
fun rememberHomeColors(): HomeColors {
|
||||
val scheme = MaterialTheme.colorScheme
|
||||
return remember(scheme) {
|
||||
HomeColors(
|
||||
primary = primary,
|
||||
onPrimary = Color(0xFF17171C),
|
||||
background = if (isDark) Color(0xFF17171C) else Color(0xFFF8F7F6),
|
||||
drawerBackground = if (isDark) Color(0xFF1E1E24) else Color(0xFFF8F7F6),
|
||||
card = Color(0xFF24242B),
|
||||
textPrimary = if (isDark) Color.White else Color(0xFF141517),
|
||||
textSecondary = if (isDark) Color(0xFF9AA0A6) else Color(0xFF6B7280),
|
||||
divider = if (isDark) Color.White.copy(alpha = 0.08f) else Color.Black.copy(alpha = 0.08f),
|
||||
avatarBackground = Color(0xFF3A3A46),
|
||||
avatarBorder = primary.copy(alpha = 0.3f),
|
||||
drawerFooterBackground = if (isDark) Color.Black.copy(alpha = 0.2f) else Color.Black.copy(alpha = 0.05f)
|
||||
primary = scheme.primary,
|
||||
onPrimary = scheme.onPrimary,
|
||||
background = scheme.background,
|
||||
drawerBackground = scheme.surface,
|
||||
card = scheme.surfaceVariant,
|
||||
textPrimary = scheme.onBackground,
|
||||
textSecondary = scheme.onSurfaceVariant,
|
||||
divider = scheme.outlineVariant,
|
||||
avatarBackground = scheme.primaryContainer,
|
||||
avatarBorder = scheme.outline,
|
||||
drawerFooterBackground = scheme.surfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,44 @@ package hu.bbara.purefin.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
// Light Mode Palette
|
||||
val LightPrimary = Color(0xFF30D0F8) // The main brand color
|
||||
val LightOnPrimary = Color(0xFFFFFFFF)
|
||||
val LightPrimaryContainer = Color(0xFFD4FCFF)
|
||||
val LightOnPrimaryContainer = Color(0xFF03405E)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
val LightSecondary = Color(0xFF0F81AB) // A darker teal/blue from the end of the light list
|
||||
val LightOnSecondary = Color(0xFFFFFFFF)
|
||||
val LightSecondaryContainer = Color(0xFFABF7FF)
|
||||
val LightOnSecondaryContainer = Color(0xFF055E85)
|
||||
|
||||
val LightTertiary = Color(0xFF1DA7D1) // Mid-tone blue
|
||||
val LightOnTertiary = Color(0xFFFFFFFF)
|
||||
val LightTertiaryContainer = Color(0xFFF0FEFF) // Very light blue
|
||||
val LightOnTertiaryContainer = Color(0xFF03405E)
|
||||
|
||||
val LightBackground = Color(0xFFF0FEFF) // The lightest swatch
|
||||
val LightOnBackground = Color(0xFF03405E) // The darkest swatch for text
|
||||
val LightSurface = Color(0xFFF0FEFF)
|
||||
val LightOnSurface = Color(0xFF03405E)
|
||||
|
||||
// Dark Mode Palette
|
||||
val DarkPrimary = Color(0xFF30D0F8) // Main brand color stays vibrant
|
||||
val DarkOnPrimary = Color(0xFF003546) // Dark text on bright primary
|
||||
val DarkPrimaryContainer = Color(0xFF1C4C58)
|
||||
val DarkOnPrimaryContainer = Color(0xFFABF7FF)
|
||||
|
||||
val DarkSecondary = Color(0xFF52CEE8) // Lighter blue for secondary in dark mode
|
||||
val DarkOnSecondary = Color(0xFF003546)
|
||||
val DarkSecondaryContainer = Color(0xFF21697B)
|
||||
val DarkOnSecondaryContainer = Color(0xFFD4FCFF)
|
||||
|
||||
val DarkTertiary = Color(0xFF7DE3F3) // Another light accent
|
||||
val DarkOnTertiary = Color(0xFF003546)
|
||||
val DarkTertiaryContainer = Color(0xFF268EA8)
|
||||
val DarkOnTertiaryContainer = Color(0xFFD0F7FA)
|
||||
|
||||
val DarkBackground = Color(0xFF13242B) // Darkest swatch from dark mode list
|
||||
val DarkOnBackground = Color(0xFFD0F7FA) // Lightest swatch for text
|
||||
val DarkSurface = Color(0xFF13242B)
|
||||
val DarkOnSurface = Color(0xFFD0F7FA)
|
||||
@@ -1,6 +1,5 @@
|
||||
package hu.bbara.purefin.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
@@ -11,32 +10,47 @@ import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = LightPrimary,
|
||||
onPrimary = LightOnPrimary,
|
||||
primaryContainer = LightPrimaryContainer,
|
||||
onPrimaryContainer = LightOnPrimaryContainer,
|
||||
secondary = LightSecondary,
|
||||
onSecondary = LightOnSecondary,
|
||||
secondaryContainer = LightSecondaryContainer,
|
||||
onSecondaryContainer = LightOnSecondaryContainer,
|
||||
tertiary = LightTertiary,
|
||||
onTertiary = LightOnTertiary,
|
||||
tertiaryContainer = LightTertiaryContainer,
|
||||
onTertiaryContainer = LightOnTertiaryContainer,
|
||||
background = LightBackground,
|
||||
onBackground = LightOnBackground,
|
||||
surface = LightSurface,
|
||||
onSurface = LightOnSurface,
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = DarkPrimary,
|
||||
onPrimary = DarkOnPrimary,
|
||||
primaryContainer = DarkPrimaryContainer,
|
||||
onPrimaryContainer = DarkOnPrimaryContainer,
|
||||
secondary = DarkSecondary,
|
||||
onSecondary = DarkOnSecondary,
|
||||
secondaryContainer = DarkSecondaryContainer,
|
||||
onSecondaryContainer = DarkOnSecondaryContainer,
|
||||
tertiary = DarkTertiary,
|
||||
onTertiary = DarkOnTertiary,
|
||||
tertiaryContainer = DarkTertiaryContainer,
|
||||
onTertiaryContainer = DarkOnTertiaryContainer,
|
||||
background = DarkBackground,
|
||||
onBackground = DarkOnBackground,
|
||||
surface = DarkSurface,
|
||||
onSurface = DarkOnSurface,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun PurefinTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user