Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,27 @@ package com.example.jetcaster.tv.model

import androidx.compose.runtime.Immutable
import com.example.jetcaster.core.data.database.model.Category
import com.example.jetcaster.core.model.CategoryInfo

@Immutable
data class CategoryList(val member: List<Category>) : List<Category> by member
data class CategoryInfoList(val member: List<CategoryInfo>) : List<CategoryInfo> by member {

fun intoCategoryList(): List<Category> {
return map(CategoryInfo::intoCategory)
}

companion object {
fun from(list: List<Category>): CategoryInfoList {
val member = list.map(Category::intoCategoryInfo)
return CategoryInfoList(member)
}
}
}

private fun CategoryInfo.intoCategory(): Category {
return Category(id, name)
}

private fun Category.intoCategoryInfo(): CategoryInfo {
return CategoryInfo(id, name)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
package com.example.jetcaster.tv.model

import androidx.compose.runtime.Immutable
import com.example.jetcaster.core.data.database.model.Category
import com.example.jetcaster.core.model.CategoryInfo

data class CategorySelection(val category: Category, val isSelected: Boolean = false)
data class CategorySelection(val categoryInfo: CategoryInfo, val isSelected: Boolean = false)

@Immutable
data class CategorySelectionList(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.unit.dp
import androidx.tv.foundation.lazy.list.TvLazyColumn
import androidx.tv.foundation.lazy.list.TvLazyListState
import androidx.tv.foundation.lazy.list.TvLazyRow
import androidx.tv.foundation.lazy.list.items
import androidx.tv.foundation.lazy.list.rememberTvLazyListState
import androidx.tv.material3.Card
import androidx.tv.material3.CardScale
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.StandardCardLayout
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.jetcaster.core.data.database.model.Podcast
import com.example.jetcaster.core.data.database.model.PodcastWithExtraInfo
import com.example.jetcaster.core.model.PlayerEpisode
import com.example.jetcaster.tv.R
Expand Down Expand Up @@ -168,27 +162,3 @@ private fun PodcastRow(
}
}
}

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
internal fun PodcastCard(
podcast: Podcast,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
StandardCardLayout(
imageCard = {
Card(
onClick = onClick,
interactionSource = it,
scale = CardScale.None,
) {
AsyncImage(model = podcast.imageUrl, contentDescription = null)
}
},
title = {
Text(text = podcast.title, modifier = Modifier.padding(top = 12.dp))
},
modifier = modifier,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@ package com.example.jetcaster.tv.ui.component
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Card
import androidx.tv.material3.CardScale
Expand All @@ -35,31 +37,20 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text
import androidx.tv.material3.WideCardLayout
import coil.compose.AsyncImage
import com.example.jetcaster.core.data.database.model.EpisodeToPodcast
import com.example.jetcaster.core.data.database.model.toPlayerEpisode
import com.example.jetcaster.core.model.PlayerEpisode
import com.example.jetcaster.tv.ui.theme.JetcasterAppDefaults

@Composable
internal fun EpisodeCard(
episode: EpisodeToPodcast,
onClick: () -> Unit,
modifier: Modifier = Modifier,
cardWidth: Dp = JetcasterAppDefaults.cardWidth.small,
) =
EpisodeCard(episode.toPlayerEpisode(), onClick, modifier, cardWidth)

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
internal fun EpisodeCard(
playerEpisode: PlayerEpisode,
onClick: () -> Unit,
modifier: Modifier = Modifier,
cardWidth: Dp = JetcasterAppDefaults.cardWidth.small,
cardSize: DpSize = JetcasterAppDefaults.thumbnailSize.episode,
) {
WideCardLayout(
imageCard = {
EpisodeThumbnail(playerEpisode, onClick = onClick, modifier = Modifier.width(cardWidth))
EpisodeThumbnail(playerEpisode, onClick = onClick, modifier = Modifier.size(cardSize))
},
title = {
EpisodeMetaData(
Expand Down Expand Up @@ -87,7 +78,12 @@ private fun EpisodeThumbnail(
scale = CardScale.None,
modifier = modifier,
) {
AsyncImage(model = playerEpisode.podcastImageUrl, contentDescription = null)
AsyncImage(
model = playerEpisode.podcastImageUrl,
contentDescription = null,
placeholder = thumbnailPlaceholder(),
modifier = Modifier.fillMaxSize()
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ internal fun EpisodeDetails(
first = {
Thumbnail(
playerEpisode,
size = JetcasterAppDefaults.thumbnailSize.episode
size = JetcasterAppDefaults.thumbnailSize.episodeDetails
)
},
second = {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetcaster.tv.ui.component

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.tv.material3.Card
import androidx.tv.material3.CardScale
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.StandardCardLayout
import androidx.tv.material3.Text
import coil.compose.AsyncImage
import com.example.jetcaster.core.data.database.model.Podcast
import com.example.jetcaster.tv.ui.theme.JetcasterAppDefaults

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
internal fun PodcastCard(
podcast: Podcast,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
StandardCardLayout(
imageCard = {
Card(
onClick = onClick,
interactionSource = it,
scale = CardScale.None,
) {
AsyncImage(
model = podcast.imageUrl,
contentDescription = null,
placeholder = thumbnailPlaceholder(),
modifier = Modifier.size(JetcasterAppDefaults.thumbnailSize.podcast)
)
}
},
title = {
Text(text = podcast.title, modifier = Modifier.padding(top = 12.dp))
},
modifier = modifier,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.example.jetcaster.tv.ui.component

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.painter.BrushPainter
import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.MaterialTheme

@OptIn(ExperimentalTvMaterial3Api::class)
@Composable
internal fun thumbnailPlaceholder(
brush: Brush = SolidColor(MaterialTheme.colorScheme.surfaceVariant)
): BrushPainter {
return BrushPainter(brush)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ import androidx.tv.material3.ExperimentalTvMaterial3Api
import androidx.tv.material3.Tab
import androidx.tv.material3.TabRow
import androidx.tv.material3.Text
import com.example.jetcaster.core.data.database.model.Category
import com.example.jetcaster.core.data.database.model.Podcast
import com.example.jetcaster.core.data.database.model.PodcastWithExtraInfo
import com.example.jetcaster.core.model.CategoryInfo
import com.example.jetcaster.core.model.PlayerEpisode
import com.example.jetcaster.tv.model.CategoryList
import com.example.jetcaster.tv.model.CategoryInfoList
import com.example.jetcaster.tv.model.EpisodeList
import com.example.jetcaster.tv.model.PodcastList
import com.example.jetcaster.tv.ui.component.Catalog
Expand All @@ -67,7 +67,7 @@ fun DiscoverScreen(

is DiscoverScreenUiState.Ready -> {
CatalogWithCategorySelection(
categoryList = s.categoryList,
categoryInfoList = s.categoryInfoList,
podcastList = s.podcastList,
selectedCategory = s.selectedCategory,
latestEpisodeList = s.latestEpisodeList,
Expand All @@ -88,13 +88,14 @@ fun DiscoverScreen(
@OptIn(ExperimentalTvMaterial3Api::class, ExperimentalComposeUiApi::class)
@Composable
private fun CatalogWithCategorySelection(
categoryList: CategoryList,
categoryInfoList: CategoryInfoList,
podcastList: PodcastList,
selectedCategory: Category,

selectedCategory: CategoryInfo,
latestEpisodeList: EpisodeList,
onPodcastSelected: (PodcastWithExtraInfo) -> Unit,
onEpisodeSelected: (PlayerEpisode) -> Unit,
onCategorySelected: (Category) -> Unit,
onCategorySelected: (CategoryInfo) -> Unit,
modifier: Modifier = Modifier,
state: TvLazyListState = rememberTvLazyListState(),
) {
Expand All @@ -104,7 +105,7 @@ private fun CatalogWithCategorySelection(
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
val selectedTabIndex = categoryList.indexOf(selectedCategory)
val selectedTabIndex = categoryInfoList.indexOf(selectedCategory)

Catalog(
podcastList = podcastList,
Expand All @@ -131,7 +132,7 @@ private fun CatalogWithCategorySelection(
}
}
) {
categoryList.forEachIndexed { index, category ->
categoryInfoList.forEachIndexed { index, category ->
val tabModifier = if (selectedTabIndex == index) {
Modifier.focusRequester(selectedTab)
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ package com.example.jetcaster.tv.ui.discover

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.jetcaster.core.data.database.model.Category
import com.example.jetcaster.core.data.database.model.toPlayerEpisode
import com.example.jetcaster.core.data.repository.CategoryStore
import com.example.jetcaster.core.data.repository.PodcastsRepository
import com.example.jetcaster.core.model.CategoryInfo
import com.example.jetcaster.core.model.PlayerEpisode
import com.example.jetcaster.core.player.EpisodePlayer
import com.example.jetcaster.tv.model.CategoryList
import com.example.jetcaster.tv.model.CategoryInfoList
import com.example.jetcaster.tv.model.EpisodeList
import com.example.jetcaster.tv.model.PodcastList
import dagger.hilt.android.lifecycle.HiltViewModel
Expand All @@ -46,13 +46,13 @@ class DiscoverScreenViewModel @Inject constructor(
private val episodePlayer: EpisodePlayer,
) : ViewModel() {

private val _selectedCategory = MutableStateFlow<Category?>(null)
private val _selectedCategory = MutableStateFlow<CategoryInfo?>(null)

private val categoryListFlow = categoryStore
.categoriesSortedByPodcastCount()
.map { categoryList ->
categoryList.map { category ->
Category(
CategoryInfo(
id = category.id,
name = category.name.filter { !it.isWhitespace() }
)
Expand All @@ -69,7 +69,7 @@ class DiscoverScreenViewModel @Inject constructor(
@OptIn(ExperimentalCoroutinesApi::class)
private val podcastInSelectedCategory = selectedCategoryFlow.flatMapLatest {
if (it != null) {
categoryStore.podcastsInCategorySortedByPodcastCount(it.id)
categoryStore.podcastsInCategorySortedByPodcastCount(it.id, limit = 10)
} else {
flowOf(emptyList())
}
Expand All @@ -96,7 +96,7 @@ class DiscoverScreenViewModel @Inject constructor(
) { categoryList, category, podcastList, latestEpisodes ->
if (category != null) {
DiscoverScreenUiState.Ready(
CategoryList(categoryList),
CategoryInfoList(categoryList),
category,
podcastList,
latestEpisodes
Expand All @@ -114,7 +114,7 @@ class DiscoverScreenViewModel @Inject constructor(
refresh()
}

fun selectCategory(category: Category) {
fun selectCategory(category: CategoryInfo) {
_selectedCategory.value = category
}

Expand All @@ -132,8 +132,8 @@ class DiscoverScreenViewModel @Inject constructor(
sealed interface DiscoverScreenUiState {
data object Loading : DiscoverScreenUiState
data class Ready(
val categoryList: CategoryList,
val selectedCategory: Category,
val categoryInfoList: CategoryInfoList,
val selectedCategory: CategoryInfo,
val podcastList: PodcastList,
val latestEpisodeList: EpisodeList,
) : DiscoverScreenUiState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ private fun EpisodeDetails(
first = {
Thumbnail(
podcast = episodeToPodcast.podcast,
size = JetcasterAppDefaults.thumbnailSize.episode
size = JetcasterAppDefaults.thumbnailSize.episodeDetails
)
},
second = {
Expand Down
Loading