Compare commits

...

9 Commits

Author SHA1 Message Date
ce539a1a5a Refine SuggestionsSection layout for better scaling
Adjusts the `HorizontalPager` to use a fixed page size and consistent horizontal padding. This ensures the suggestion cards have a predictable width on wider screens.
2026-03-31 23:50:19 +02:00
b08b74bab3 Replace HomeAvatar with standard Icon and update top bar layout
- Remove HomeAvatar.kt and replace usages with standard Material Icons
- Update search and profile button sizes to 50dp and icon sizes to 30dp
- Add spacing between top bar actions in HomeTopBar
2026-03-31 23:38:07 +02:00
6852e0b13a Update app branding and home screen top bar
Replaces the previous application icon with a new version (`purefin_logo_2`) across all density buckets and updates the `AndroidManifest.xml` accordingly.

The `HomeTopBar` is redesigned to feature a new brand-aligned layout:
- Replaced the generic "Watch now" title and subtitle with the Purefin logo and stylized "PureFin" text.
- Added a `PurefinLogo` component to render the raw logo resource.
- Updated the search button styling to use theme-based secondary colors.
2026-03-31 23:31:48 +02:00
72f6a37203 refactor: update MediaResumeButton to use white color for button text 2026-03-31 22:29:45 +02:00
c85767cf25 refactor: remove MediaPlayButton component as it is unused 2026-03-31 22:28:51 +02:00
8563b41111 Refactor media screens to use a unified hero section with gradient overlays
Moved title, metadata chips, and hero images into new `MediaHeroSection`, `EpisodeHeroSection`, and `SeriesHeroSection` components across Movie, Episode, and Series screens. This change:
- Increases hero section height to 40% of the screen.
- Adds a vertical gradient overlay to the hero image to improve text legibility.
- Layers title and metadata directly over the hero section instead of placing them in the scrollable content body.
- Cleans up unused imports and experimental annotations in component files.
2026-03-31 22:27:52 +02:00
3daf45a421 refactor: remove unused initialization logic in AppViewModel 2026-03-31 20:53:29 +02:00
74c532140e feat: auto-scroll to first page on SuggestionsSection load 2026-03-31 19:41:34 +02:00
e8938a9290 refactor: remove redundant Text component in SuggestionCard 2026-03-31 19:41:27 +02:00
36 changed files with 350 additions and 265 deletions

View File

@@ -12,9 +12,9 @@
android:screenOrientation="portrait"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/purefin_logo"
android:icon="@mipmap/purefin_logo_2"
android:label="@string/app_name"
android:roundIcon="@mipmap/purefin_logo_round"
android:roundIcon="@mipmap/purefin_logo_2_round"
android:supportsRtl="true"
android:theme="@style/Theme.Purefin">
<activity

View File

@@ -6,8 +6,6 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -40,7 +38,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import hu.bbara.purefin.common.ui.MediaCastRow
import hu.bbara.purefin.common.ui.MediaMetaChip
import hu.bbara.purefin.common.ui.MediaSynopsis
import hu.bbara.purefin.common.ui.components.GhostIconButton
import hu.bbara.purefin.common.ui.components.MediaActionButton
@@ -114,7 +111,6 @@ internal fun EpisodeTopBar(
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
internal fun EpisodeDetails(
episode: Episode,
@@ -134,37 +130,6 @@ internal fun EpisodeDetails(
}
Column(modifier = modifier) {
Text(
text = episode.title,
color = scheme.onBackground,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 38.sp
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Episode ${episode.index}",
color = scheme.onBackground,
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
Spacer(modifier = Modifier.height(16.dp))
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
MediaMetaChip(text = episode.releaseDate)
MediaMetaChip(text = episode.rating)
MediaMetaChip(text = episode.runtime)
MediaMetaChip(
text = episode.format,
background = scheme.primary.copy(alpha = 0.2f),
border = scheme.primary.copy(alpha = 0.3f),
textColor = scheme.primary
)
}
Spacer(modifier = Modifier.height(24.dp))
MediaSynopsis(
synopsis = episode.synopsis
)

View File

@@ -4,30 +4,44 @@ import android.Manifest
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.common.ui.MediaMetaChip
import hu.bbara.purefin.common.ui.PurefinWaitingScreen
import hu.bbara.purefin.common.ui.components.MediaHero
import hu.bbara.purefin.core.data.navigation.EpisodeDto
import hu.bbara.purefin.core.data.navigation.LocalNavigationBackStack
import hu.bbara.purefin.core.data.navigation.LocalNavigationManager
import hu.bbara.purefin.core.data.navigation.Route
import hu.bbara.purefin.core.model.Episode
import hu.bbara.purefin.core.model.CastMember
import hu.bbara.purefin.core.model.Episode
import hu.bbara.purefin.feature.download.DownloadState
import hu.bbara.purefin.feature.shared.content.episode.EpisodeScreenViewModel
import hu.bbara.purefin.ui.theme.AppTheme
@@ -39,7 +53,6 @@ fun EpisodeScreen(
viewModel: EpisodeScreenViewModel = hiltViewModel(),
modifier: Modifier = Modifier
) {
val navigationManager = LocalNavigationManager.current
val backStack = LocalNavigationBackStack.current
val previousRoute = remember(backStack) { backStack.getOrNull(backStack.lastIndex - 1) }
@@ -120,12 +133,7 @@ private fun EpisodeScreenInternal(
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
MediaHero(
imageUrl = episode.heroImageUrl,
backgroundColor = MaterialTheme.colorScheme.background,
heightFraction = 0.30f,
modifier = Modifier.fillMaxWidth()
)
EpisodeHeroSection(episode = episode)
EpisodeDetails(
episode = episode,
downloadState = downloadState,
@@ -139,6 +147,86 @@ private fun EpisodeScreenInternal(
}
}
@Composable
private fun EpisodeHeroSection(
episode: Episode,
modifier: Modifier = Modifier,
) {
val scheme = MaterialTheme.colorScheme
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val sectionHeight = screenHeight * 0.4f
Box(
modifier = modifier
.fillMaxWidth()
.height(sectionHeight)
) {
MediaHero(
imageUrl = episode.heroImageUrl,
backgroundColor = scheme.background,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Transparent,
scheme.background.copy(alpha = 0.5f),
scheme.background
)
)
)
)
Column(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomStart)
.padding(16.dp)
) {
Text(
text = episode.title,
color = scheme.onBackground,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 38.sp
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Episode ${episode.index}",
color = scheme.onBackground,
fontSize = 14.sp,
fontWeight = FontWeight.Medium
)
Spacer(modifier = Modifier.height(16.dp))
EpisodeMetaChips(episode = episode)
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun EpisodeMetaChips(episode: Episode) {
val scheme = MaterialTheme.colorScheme
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
MediaMetaChip(text = episode.releaseDate)
MediaMetaChip(text = episode.rating)
MediaMetaChip(text = episode.runtime)
MediaMetaChip(
text = episode.format,
background = scheme.primary.copy(alpha = 0.2f),
border = scheme.primary.copy(alpha = 0.3f),
textColor = scheme.primary
)
}
}
@Preview(showBackground = true)
@Composable
private fun EpisodeScreenPreview() {

View File

@@ -4,7 +4,6 @@ import android.content.Intent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
@@ -12,9 +11,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Add
import androidx.compose.material.icons.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Cast
import androidx.compose.material.icons.outlined.Close
@@ -33,7 +30,6 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import hu.bbara.purefin.common.ui.MediaCastRow
import hu.bbara.purefin.common.ui.MediaMetaChip
import hu.bbara.purefin.common.ui.MediaSynopsis
import hu.bbara.purefin.common.ui.components.GhostIconButton
import hu.bbara.purefin.common.ui.components.MediaActionButton
@@ -89,30 +85,6 @@ internal fun MovieDetails(
}
Column(modifier = modifier) {
Text(
text = movie.title,
color = scheme.onBackground,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 38.sp
)
Spacer(modifier = Modifier.height(16.dp))
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
MediaMetaChip(text = movie.year)
MediaMetaChip(text = movie.rating)
MediaMetaChip(text = movie.runtime)
MediaMetaChip(
text = movie.format,
background = scheme.primary.copy(alpha = 0.2f),
border = scheme.primary.copy(alpha = 0.3f),
textColor = scheme.primary
)
}
Spacer(modifier = Modifier.height(24.dp))
MediaSynopsis(
synopsis = movie.synopsis
)

View File

@@ -4,21 +4,35 @@ import android.Manifest
import android.os.Build
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel
import hu.bbara.purefin.common.ui.MediaMetaChip
import hu.bbara.purefin.common.ui.PurefinWaitingScreen
import hu.bbara.purefin.common.ui.components.MediaHero
import hu.bbara.purefin.core.data.navigation.MovieDto
@@ -93,12 +107,7 @@ private fun MovieScreenInternal(
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
MediaHero(
imageUrl = movie.heroImageUrl,
backgroundColor = MaterialTheme.colorScheme.background,
heightFraction = 0.30f,
modifier = Modifier.fillMaxWidth()
)
MediaHeroSection(movie = movie)
MovieDetails(
movie = movie,
downloadState = downloadState,
@@ -112,6 +121,69 @@ private fun MovieScreenInternal(
}
}
@Composable
fun MediaHeroSection(
movie: Movie,
modifier: Modifier = Modifier) {
val scheme = MaterialTheme.colorScheme
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val sectionHeight = screenHeight * 0.4f
Box (
modifier = modifier
.height(sectionHeight)
) {
MediaHero(
imageUrl = movie.heroImageUrl,
backgroundColor = MaterialTheme.colorScheme.background,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Transparent,
scheme.background.copy(alpha = 0.5f),
scheme.background
)
)
)
)
Column (
modifier = Modifier.fillMaxWidth()
.align(Alignment.BottomStart)
.padding(16.dp)
) {
Text(
text = movie.title,
color = scheme.onBackground,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
lineHeight = 38.sp
)
Spacer(modifier = Modifier.height(16.dp))
FlowRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
MediaMetaChip(text = movie.year)
MediaMetaChip(text = movie.rating)
MediaMetaChip(text = movie.runtime)
MediaMetaChip(
text = movie.format,
background = scheme.primary.copy(alpha = 0.2f),
border = scheme.primary.copy(alpha = 0.3f),
textColor = scheme.primary
)
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun MovieScreenPreview() {

View File

@@ -19,7 +19,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.Alignment
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -128,28 +134,13 @@ private fun SeriesScreenInternal(
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
MediaHero(
imageUrl = series.heroImageUrl,
heightFraction = 0.30f,
backgroundColor = MaterialTheme.colorScheme.background,
modifier = Modifier.fillMaxWidth()
)
SeriesHeroSection(series = series)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = innerPadding.calculateBottomPadding())
) {
Text(
text = series.name,
color = scheme.onBackground,
fontSize = 30.sp,
fontWeight = FontWeight.Bold,
lineHeight = 36.sp
)
Spacer(modifier = Modifier.height(16.dp))
SeriesMetaChips(series = series)
Spacer(modifier = Modifier.height(24.dp))
SeriesActionButtons(
nextUpEpisode = nextUpEpisode,
seriesDownloadState = seriesDownloadState,
@@ -192,6 +183,58 @@ private fun SeriesScreenInternal(
}
}
@Composable
private fun SeriesHeroSection(
series: Series,
modifier: Modifier = Modifier,
) {
val scheme = MaterialTheme.colorScheme
val screenHeight = LocalConfiguration.current.screenHeightDp.dp
val sectionHeight = screenHeight * 0.4f
Box(
modifier = modifier
.fillMaxWidth()
.height(sectionHeight)
) {
MediaHero(
imageUrl = series.heroImageUrl,
backgroundColor = scheme.background,
modifier = Modifier.fillMaxSize()
)
Box(
modifier = Modifier
.matchParentSize()
.background(
Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Transparent,
scheme.background.copy(alpha = 0.5f),
scheme.background
)
)
)
)
Column(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.BottomStart)
.padding(16.dp)
) {
Text(
text = series.name,
color = scheme.onBackground,
fontSize = 30.sp,
fontWeight = FontWeight.Bold,
lineHeight = 36.sp
)
Spacer(modifier = Modifier.height(16.dp))
SeriesMetaChips(series = series)
}
}
}
@Preview(showBackground = true)
@Composable
private fun SeriesScreenPreview() {

View File

@@ -22,10 +22,10 @@ import hu.bbara.purefin.app.home.ui.homePreviewLibraryContent
import hu.bbara.purefin.app.home.ui.homePreviewNextUp
import hu.bbara.purefin.app.home.ui.search.HomeSearchOverlay
import hu.bbara.purefin.feature.shared.home.ContinueWatchingItem
import hu.bbara.purefin.feature.shared.home.SuggestedItem
import hu.bbara.purefin.feature.shared.home.LibraryItem
import hu.bbara.purefin.feature.shared.home.NextUpItem
import hu.bbara.purefin.feature.shared.home.PosterItem
import hu.bbara.purefin.feature.shared.home.SuggestedItem
import hu.bbara.purefin.ui.theme.AppTheme
import org.jellyfin.sdk.model.UUID
@@ -67,8 +67,6 @@ fun HomeScreen(
contentColor = MaterialTheme.colorScheme.onBackground,
topBar = {
HomeTopBar(
title = "Watch now",
subtitle = subtitle,
onSearchClick = { isSearchVisible = true },
onProfileClick = onProfileClick,
onSettingsClick = onSettingsClick,

View File

@@ -11,6 +11,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -81,13 +82,10 @@ fun DefaultTopBar(
.size(56.dp)
.clip(CircleShape),
) {
HomeAvatar(
size = 56.dp,
borderWidth = 1.dp,
borderColor = scheme.outlineVariant,
backgroundColor = scheme.secondaryContainer,
icon = Icons.Outlined.Person,
iconTint = scheme.onSecondaryContainer
Icon(
imageVector = Icons.Outlined.Person,
contentDescription = "Profile",
modifier = Modifier.size(30.dp),
)
}
DropdownMenu(

View File

@@ -1,41 +0,0 @@
package hu.bbara.purefin.app.home.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.Dp
@Composable
fun HomeAvatar(
size: Dp,
borderWidth: Dp,
borderColor: Color,
backgroundColor: Color,
icon: ImageVector,
iconTint: Color,
modifier: Modifier = Modifier
) {
Box(
modifier = modifier
.size(size)
.clip(CircleShape)
.border(borderWidth, borderColor, CircleShape)
.background(backgroundColor),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = icon,
contentDescription = null,
tint = iconTint
)
}
}

View File

@@ -1,6 +1,8 @@
package hu.bbara.purefin.app.home.ui
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
@@ -11,6 +13,7 @@ import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
@@ -20,16 +23,19 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import hu.bbara.purefin.common.ui.components.PurefinLogo
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HomeTopBar(
title: String,
subtitle: String,
onSearchClick: () -> Unit,
onProfileClick: () -> Unit,
onSettingsClick: () -> Unit,
@@ -41,42 +47,57 @@ fun HomeTopBar(
TopAppBar(
title = {
Column {
Text(
text = title,
style = MaterialTheme.typography.headlineSmall,
maxLines = 1,
overflow = TextOverflow.Ellipsis
Row(
modifier = Modifier,
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
PurefinLogo(
contentDescription = "Purefin",
modifier = Modifier.size(48.dp),
contentScale = ContentScale.Fit,
)
Text(
text = subtitle,
style = MaterialTheme.typography.bodySmall,
color = scheme.onSurfaceVariant,
maxLines = 1,
overflow = TextOverflow.Ellipsis
text = "PureFin",
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
fontStyle = FontStyle.Italic,
color = scheme.onSecondary
)
}
},
actions = {
IconButton(onClick = onSearchClick) {
IconButton(
onClick = onSearchClick,
colors = IconButtonColors(
containerColor = scheme.secondary,
contentColor = scheme.onSecondary,
disabledContainerColor = scheme.secondary,
disabledContentColor = scheme.onSecondary),
modifier = Modifier.size(50.dp),
) {
Icon(
imageVector = Icons.Outlined.Search,
contentDescription = "Search"
contentDescription = "Search",
modifier = Modifier.size(30.dp),
)
}
Spacer(modifier = Modifier.size(12.dp))
IconButton(
onClick = { isProfileMenuExpanded = true },
colors = IconButtonColors(
containerColor = scheme.secondary,
contentColor = scheme.onSecondary,
disabledContainerColor = scheme.secondary,
disabledContentColor = scheme.onSecondary),
modifier = Modifier
.size(48.dp)
.size(50.dp)
.clip(CircleShape),
) {
HomeAvatar(
size = 40.dp,
borderWidth = 1.dp,
borderColor = scheme.outlineVariant,
backgroundColor = scheme.secondaryContainer,
icon = Icons.Outlined.Person,
iconTint = scheme.onSecondaryContainer
Icon(
imageVector = Icons.Outlined.Person,
contentDescription = "Profile",
modifier = Modifier.size(30.dp),
)
}
DropdownMenu(

View File

@@ -76,9 +76,6 @@ internal fun SuggestionCard(
.padding(24.dp)
) {
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
Text(
text = item.title
)
Text(
text = item.title,
style = MaterialTheme.typography.headlineMedium,

View File

@@ -13,10 +13,12 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PageSize
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
@@ -32,13 +34,18 @@ fun SuggestionsSection(
val pagerState = rememberPagerState(pageCount = { items.size })
LaunchedEffect(Unit) {
pagerState.scrollToPage(0)
}
Column(
verticalArrangement = Arrangement.spacedBy(14.dp),
modifier = modifier
modifier = modifier.fillMaxWidth()
) {
HorizontalPager(
state = pagerState,
contentPadding = PaddingValues(start = 16.dp, end = 56.dp),
pageSize = PageSize.Fixed(360.dp),
contentPadding = PaddingValues(horizontal = 16.dp),
pageSpacing = 16.dp,
modifier = Modifier.fillMaxWidth()
) { page ->

View File

@@ -1,73 +0,0 @@
package hu.bbara.purefin.common.ui.components
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
@Composable
fun MediaPlayButton(
backgroundColor: Color,
foregroundColor: Color,
text: String = "Play",
subText: String? = null,
size: Dp,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier
.height(size)
.clip(CircleShape)
.background(backgroundColor)
.padding(start = 16.dp, end = 32.dp)
.clickable { onClick() },
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
imageVector = Icons.Filled.PlayArrow,
contentDescription = "Play",
tint = foregroundColor,
modifier = Modifier.size(42.dp)
)
Column() {
Text(
text = text,
color = foregroundColor,
fontSize = TextUnit(
value = 16f,
type = TextUnitType.Sp
)
)
subText?.let {
Text(
text = subText,
color = foregroundColor.copy(alpha = 0.7f),
fontSize = TextUnit(
value = 14f,
type = TextUnitType.Sp
)
)
}
}
}
}

View File

@@ -8,7 +8,6 @@ import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -22,6 +21,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@@ -50,7 +50,7 @@ fun MediaResumeButton(
.background(onPrimaryColor),
contentAlignment = Alignment.Center
) {
ButtonContent(text = text, color = primaryColor)
ButtonContent(text = text, color = Color.White)
}
// Top layer: primary colors, clipped to the progress %
@@ -71,7 +71,7 @@ fun MediaResumeButton(
.background(primaryColor),
contentAlignment = Alignment.Center
) {
ButtonContent(text = text, color = onPrimaryColor)
ButtonContent(text = text, color = Color.White)
}
}
}

View File

@@ -0,0 +1,32 @@
package hu.bbara.purefin.common.ui.components
import android.graphics.BitmapFactory
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import hu.bbara.purefin.R
@Composable
fun PurefinLogo(
modifier: Modifier = Modifier,
contentDescription: String? = null,
contentScale: ContentScale = ContentScale.Fit,
) {
val resources = LocalContext.current.resources
val logoBitmap = remember(resources) {
BitmapFactory.decodeResource(resources, R.raw.logo_raw)?.asImageBitmap()
}
logoBitmap?.let { bitmap ->
Image(
bitmap = bitmap,
contentDescription = contentDescription,
contentScale = contentScale,
modifier = modifier,
)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/purefin_logo_2_background"/>
<foreground android:drawable="@mipmap/purefin_logo_2_foreground"/>
<monochrome android:drawable="@mipmap/purefin_logo_2_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/purefin_logo_2_background"/>
<foreground android:drawable="@mipmap/purefin_logo_2_foreground"/>
<monochrome android:drawable="@mipmap/purefin_logo_2_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purefin_logo_2_background">#201337</color>
</resources>

View File

@@ -152,16 +152,6 @@ class AppViewModel @Inject constructor(
initialValue = emptyMap()
)
init {
viewModelScope.launch {
try {
appContentRepository.ensureReady()
} catch (e: Exception) {
// State is already set to Error by ensureReady; don't crash the app
}
}
}
fun onLibrarySelected(id: UUID, name: String) {
viewModelScope.launch {
navigationManager.navigate(Route.LibraryRoute(library = LibraryDto(id = id, name = name)))