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