Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
184 changes: 105 additions & 79 deletions Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
Expand Down Expand Up @@ -65,7 +66,6 @@ import androidx.compose.material3.TabPosition
import androidx.compose.material3.TabRow
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
import androidx.compose.material3.adaptive.Posture
import androidx.compose.material3.adaptive.WindowAdaptiveInfo
Expand Down Expand Up @@ -124,6 +124,7 @@ import com.example.jetcaster.util.ToggleFollowPodcastIconButton
import com.example.jetcaster.util.fullWidthItem
import com.example.jetcaster.util.isCompact
import com.example.jetcaster.util.quantityStringResource
import com.example.jetcaster.util.radialGradientScrim
import java.time.Duration
import java.time.LocalDateTime
import java.time.OffsetDateTime
Expand Down Expand Up @@ -325,41 +326,55 @@ private fun HomeAppBar(
isExpanded: Boolean,
modifier: Modifier = Modifier,
) {
TopAppBar(
title = {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier
.fillMaxWidth()
.padding(end = 16.dp)
) {
SearchBar(
query = "",
onQueryChange = {},
placeholder = {
Text(stringResource(id = R.string.search_for_a_podcast))
},
onSearch = {},
active = false,
onActiveChange = {},
leadingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null
)
},
trailingIcon = {
Icon(
imageVector = Icons.Default.AccountCircle,
contentDescription = stringResource(R.string.cd_account)
)
},
modifier = if (isExpanded) Modifier else Modifier.fillMaxWidth()
) { }
}
},
modifier = modifier.padding(vertical = 8.dp)
)
Row(
horizontalArrangement = Arrangement.End,
modifier = modifier
.fillMaxWidth()
.background(Color.Transparent)
.padding(end = 16.dp, top = 8.dp, bottom = 8.dp)
) {
SearchBar(
query = "",
onQueryChange = {},
placeholder = {
Text(stringResource(id = R.string.search_for_a_podcast))
},
onSearch = {},
active = false,
onActiveChange = {},
leadingIcon = {
Icon(
imageVector = Icons.Default.Search,
contentDescription = null
)
},
trailingIcon = {
Icon(
imageVector = Icons.Default.AccountCircle,
contentDescription = stringResource(R.string.cd_account)
)
},
modifier = if (isExpanded) Modifier else Modifier.fillMaxWidth()
) { }
}
}

@Composable
private fun HomeScreenBackground(
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit
) {
Box(
modifier = modifier
.background(MaterialTheme.colorScheme.background)
) {
Box(
modifier = Modifier
.fillMaxSize()
.radialGradientScrim(MaterialTheme.colorScheme.primary.copy(alpha = 0.15f))
)
content()
}
}

@Composable
Expand All @@ -377,47 +392,52 @@ private fun HomeScreen(

val coroutineScope = rememberCoroutineScope()
val snackbarHostState = remember { SnackbarHostState() }
Scaffold(
modifier = modifier.windowInsetsPadding(
WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)
),
topBar = {
HomeAppBar(
isExpanded = homeState.windowSizeClass.isCompact,
modifier = Modifier.fillMaxWidth(),
HomeScreenBackground(
modifier = modifier
.windowInsetsPadding(
WindowInsets.systemBars.only(WindowInsetsSides.Horizontal)
)
},
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
}
) { contentPadding ->
// Main Content
val snackBarText = stringResource(id = R.string.episode_added_to_your_queue)
HomeContent(
showGrid = showGrid,
showHomeCategoryTabs = homeState.showHomeCategoryTabs,
featuredPodcasts = homeState.featuredPodcasts,
isRefreshing = homeState.isRefreshing,
selectedHomeCategory = homeState.selectedHomeCategory,
homeCategories = homeState.homeCategories,
filterableCategoriesModel = homeState.filterableCategoriesModel,
podcastCategoryFilterResult = homeState.podcastCategoryFilterResult,
library = homeState.library,
modifier = Modifier.padding(contentPadding),
onPodcastUnfollowed = homeState.onPodcastUnfollowed,
onHomeCategorySelected = homeState.onHomeCategorySelected,
onCategorySelected = homeState.onCategorySelected,
navigateToPodcastDetails = homeState.navigateToPodcastDetails,
navigateToPlayer = homeState.navigateToPlayer,
onTogglePodcastFollowed = homeState.onTogglePodcastFollowed,
onLibraryPodcastSelected = homeState.onLibraryPodcastSelected,
onQueueEpisode = {
coroutineScope.launch {
snackbarHostState.showSnackbar(snackBarText)
) {
Scaffold(
topBar = {
HomeAppBar(
isExpanded = homeState.windowSizeClass.isCompact,
modifier = Modifier.fillMaxWidth(),
)
},
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
containerColor = Color.Transparent
) { contentPadding ->
// Main Content
val snackBarText = stringResource(id = R.string.episode_added_to_your_queue)
HomeContent(
showGrid = showGrid,
showHomeCategoryTabs = homeState.showHomeCategoryTabs,
featuredPodcasts = homeState.featuredPodcasts,
isRefreshing = homeState.isRefreshing,
selectedHomeCategory = homeState.selectedHomeCategory,
homeCategories = homeState.homeCategories,
filterableCategoriesModel = homeState.filterableCategoriesModel,
podcastCategoryFilterResult = homeState.podcastCategoryFilterResult,
library = homeState.library,
modifier = Modifier.padding(contentPadding),
onPodcastUnfollowed = homeState.onPodcastUnfollowed,
onHomeCategorySelected = homeState.onHomeCategorySelected,
onCategorySelected = homeState.onCategorySelected,
navigateToPodcastDetails = homeState.navigateToPodcastDetails,
navigateToPlayer = homeState.navigateToPlayer,
onTogglePodcastFollowed = homeState.onTogglePodcastFollowed,
onLibraryPodcastSelected = homeState.onLibraryPodcastSelected,
onQueueEpisode = {
coroutineScope.launch {
snackbarHostState.showSnackbar(snackBarText)
}
homeState.onQueueEpisode(it)
}
homeState.onQueueEpisode(it)
}
)
)
}
}
}

Expand Down Expand Up @@ -519,7 +539,9 @@ private fun HomeContentColumn(
onTogglePodcastFollowed: (PodcastInfo) -> Unit,
onQueueEpisode: (PlayerEpisode) -> Unit,
) {
LazyColumn(modifier = modifier.fillMaxSize()) {
LazyColumn(
modifier = modifier.fillMaxSize()
) {
if (featuredPodcasts.isNotEmpty()) {
item {
FollowedPodcastItem(
Expand All @@ -538,7 +560,7 @@ private fun HomeContentColumn(
}

if (showHomeCategoryTabs) {
stickyHeader {
item {
HomeCategoryTabs(
categories = homeCategories,
selectedCategory = selectedHomeCategory,
Expand Down Expand Up @@ -695,6 +717,7 @@ private fun HomeCategoryTabs(

TabRow(
selectedTabIndex = selectedIndex,
containerColor = Color.Transparent,
indicator = indicator,
modifier = modifier,
divider = {
Expand Down Expand Up @@ -749,7 +772,9 @@ private fun FollowedPodcasts(
// Alternatively, version 1.7.0-alpha05 of Compose Foundation supports `snapPosition`
// which solves this problem and avoids this calculation altogether. Once 1.7.0 is
// stable, this implementation can be updated.
BoxWithConstraints(modifier) {
BoxWithConstraints(
modifier = modifier.background(Color.Transparent)
) {
val horizontalPadding = (this.maxWidth - FEATURED_PODCAST_IMAGE_SIZE_DP) / 2
HorizontalPager(
state = pagerState,
Expand Down Expand Up @@ -839,12 +864,13 @@ private fun lastUpdated(updated: OffsetDateTime): String {
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Preview
@Composable
private fun HomeAppBarPreview() {
JetcasterTheme {
HomeAppBar(
isExpanded = false
isExpanded = false,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.example.jetcaster.R
Expand Down Expand Up @@ -131,6 +132,7 @@ private fun PodcastCategoryTabs(
)
ScrollableTabRow(
selectedTabIndex = selectedIndex,
containerColor = Color.Transparent,
divider = {}, /* Disable the built-in divider */
edgePadding = Keyline1,
indicator = emptyTabIndicator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
package com.example.jetcaster.util

import androidx.annotation.FloatRange
import androidx.compose.foundation.background
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.geometry.center
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RadialGradientShader
import androidx.compose.ui.graphics.Shader
import androidx.compose.ui.graphics.ShaderBrush
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.node.DrawModifierNode
Expand All @@ -31,6 +37,25 @@ import kotlin.math.max
import kotlin.math.min
import kotlin.math.pow

/**
* Applies a radial gradient scrim in the foreground emanating from the top
* center quarter of the element.
*/
fun Modifier.radialGradientScrim(color: Color): Modifier {
val radialGradient = object : ShaderBrush() {
override fun createShader(size: Size): Shader {
val largerDimension = max(size.height, size.width)
return RadialGradientShader(
center = size.center.copy(y = size.height / 4),
colors = listOf(color, Color.Transparent),
radius = largerDimension / 2,
colorStops = listOf(0f, 0.9f)
)
}
}
return this.background(radialGradient)
}

/**
* Draws a vertical gradient scrim in the foreground.
*
Expand Down