fix some padding + add image caching

This commit is contained in:
Owen LeJeune
2023-06-29 10:49:51 -04:00
parent e6f8931c74
commit 272f7ce4ff
9 changed files with 220 additions and 65 deletions

View File

@@ -41,7 +41,6 @@ import com.owenlejeune.tvtime.R
import kotlinx.coroutines.launch
import kotlin.math.roundToInt
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ContentCard(
modifier: Modifier = Modifier,
@@ -72,7 +71,6 @@ fun ContentCard(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ExpandableContentCard(
modifier: Modifier = Modifier,
@@ -120,7 +118,6 @@ fun ExpandableContentCard(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListContentCard(
modifier: Modifier = Modifier,
@@ -157,6 +154,7 @@ fun TwoLineImageTextCard(
title: String,
modifier: Modifier = Modifier,
subtitle: String? = null,
hideSubtitle: Boolean = false,
imageUrl: String? = null,
placeholder: ImageVector = Icons.Filled.Person,
titleTextColor: Color = MaterialTheme.colorScheme.onSurfaceVariant,
@@ -184,32 +182,16 @@ fun TwoLineImageTextCard(
style = MaterialTheme.typography.bodyMedium,
overflow = TextOverflow.Ellipsis
)
Text(
modifier = Modifier.fillMaxWidth(),
minLines = 2,
maxLines = 2,
text = subtitle ?: "",
style = MaterialTheme.typography.bodySmall,
color = subtitleTextColor,
overflow = TextOverflow.Ellipsis
)
// MinLinesText(
// modifier = Modifier
// .fillMaxWidth()
// .padding(top = 5.dp),
// minLines = 2,
// text = title,
// color = titleTextColor,
// style = MaterialTheme.typography.bodyMedium
// )
// subtitle?.let {
// MinLinesText(
// modifier = Modifier.fillMaxWidth(),
// minLines = 2,
// text = subtitle,
// style = MaterialTheme.typography.bodySmall,
// color = subtitleTextColor
// )
// }
if (!hideSubtitle) {
Text(
modifier = Modifier.fillMaxWidth(),
minLines = 2,
maxLines = 2,
text = subtitle ?: "",
style = MaterialTheme.typography.bodySmall,
color = subtitleTextColor,
overflow = TextOverflow.Ellipsis
)
}
}
}

View File

@@ -28,6 +28,8 @@ import androidx.compose.ui.unit.sp
import androidx.constraintlayout.compose.ConstraintLayout
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
@@ -110,6 +112,65 @@ fun DetailHeader(
}
}
@OptIn(ExperimentalPagerApi::class)
@Composable
fun DetailHeader2(
modifier: Modifier = Modifier,
showGalleryOverlay: MutableState<Boolean>? = null,
imageCollection: ImageCollection? = null,
backdropUrl: String? = null,
posterUrl: String? = null,
backdropContentDescription: String? = null,
posterContentDescription: String? = null,
rating: Float? = null,
pagerState: PagerState? = null,
elevation: Dp = 20.dp
) {
Box(
modifier = modifier.then(
Modifier.fillMaxWidth().wrapContentHeight()
)
) {
if (imageCollection != null) {
BackdropGallery(
modifier = Modifier
.clickable {
showGalleryOverlay?.value = true
},
imageCollection = imageCollection,
state = pagerState
)
} else {
Backdrop(
imageUrl = backdropUrl,
contentDescription = backdropContentDescription
)
}
Row(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(start = 16.dp),
horizontalArrangement = Arrangement.spacedBy(20.dp),
verticalAlignment = Alignment.Bottom
) {
PosterItem(
url = posterUrl,
title = posterContentDescription,
elevation = elevation,
overrideShowTitle = false,
enabled = false
)
rating?.let {
RatingView(
progress = rating
)
}
}
}
}
@Composable
private fun BackdropContainer(
modifier: Modifier = Modifier,
@@ -146,8 +207,14 @@ private fun Backdrop(
modifier = modifier
) { sizeImage ->
if (imageUrl != null) {
val model = ImageRequest.Builder(LocalContext.current)
.data(imageUrl)
.diskCacheKey(imageUrl)
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = imageUrl,
model = model,
placeholder = rememberAsyncImagePainter(model = R.drawable.placeholder),
contentDescription = contentDescription,
modifier = Modifier.onGloballyPositioned { sizeImage.value = it.size },
@@ -183,8 +250,15 @@ fun BackdropGallery(
modifier = Modifier.fillMaxWidth()
) { page ->
val backdrop = imageCollection.backdrops[page]
val url = TmdbUtils.getFullBackdropPath(backdrop)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = TmdbUtils.getFullBackdropPath(backdrop),
model = model,
placeholder = rememberAsyncImagePainter(model = R.drawable.placeholder),
contentDescription = "",
modifier = Modifier.onGloballyPositioned { sizeImage.value = it.size },

View File

@@ -20,6 +20,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
@@ -31,9 +32,12 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
@@ -55,7 +59,7 @@ fun TapGallery(
val scope = rememberCoroutineScope()
var showControls by remember { mutableStateOf(false) }
var lastTappedTime by remember { mutableStateOf(System.currentTimeMillis()) }
var lastTappedTime by remember { mutableLongStateOf(System.currentTimeMillis()) }
var job: Job? = null
LaunchedEffect(lastTappedTime) {
@@ -86,8 +90,14 @@ fun TapGallery(
.wrapContentHeight()
.onGloballyPositioned { sizeImage.value = it.size }
) { page ->
val model = ImageRequest.Builder(LocalContext.current)
.data(models[page])
.diskCacheKey(models[page]?.toString() ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = models[page],
model = model,
contentDescription = null,
contentScale = ContentScale.FillWidth
)

View File

@@ -12,6 +12,7 @@ import androidx.compose.ui.draw.blur
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
@@ -20,6 +21,8 @@ import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.Dimension
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.ui.navigation.AppNavItem
import com.owenlejeune.tvtime.utils.types.MediaViewType
@@ -55,8 +58,14 @@ fun MediaResultCard(
modifier = Modifier.height(112.dp)
) {
backdropPath?.let {
val model = ImageRequest.Builder(LocalContext.current)
.data(backdropPath)
.diskCacheKey(backdropPath.toString())
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = backdropPath,
model = model,
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier
@@ -79,6 +88,12 @@ fun MediaResultCard(
) {
val (poster, content, ratingView) = createRefs()
val model = ImageRequest.Builder(LocalContext.current)
.data(posterPath)
.diskCacheKey(posterPath?.toString() ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
modifier = Modifier
.constrainAs(poster) {
@@ -89,7 +104,7 @@ fun MediaResultCard(
}
.aspectRatio(0.7f)
.clip(RoundedCornerShape(10.dp)),
model = posterPath ?: R.drawable.placeholder_transparent,
model = model,
contentDescription = title
)

View File

@@ -28,6 +28,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntSize
@@ -35,6 +36,8 @@ import androidx.compose.ui.unit.dp
import androidx.paging.LoadState
import androidx.paging.compose.LazyPagingItems
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.HomePagePerson
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Person
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.TmdbItem
@@ -202,6 +205,12 @@ fun PosterItem(
endY = sizeImage.height.toFloat()
)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
modifier = Modifier
.width(width = width)
@@ -214,7 +223,7 @@ fun PosterItem(
Log.d("Poster", "Error loading: $url")
}
},
model = url,
model = model,
contentDescription = title,
contentScale = ContentScale.FillBounds,
onSuccess = { backgroundColor = Color.Transparent }

View File

@@ -63,6 +63,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavHostController
import coil.compose.AsyncImage
import coil.compose.rememberAsyncImagePainter
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.accompanist.flowlayout.FlowRow
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.AuthorDetails
@@ -601,6 +603,13 @@ fun FullScreenThumbnailVideoPlayer(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
val url = "https://img.youtube.com/vi/${key}/hqdefault.jpg"
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
modifier = Modifier
.clickable(
@@ -611,7 +620,7 @@ fun FullScreenThumbnailVideoPlayer(
context.startActivity(intent)
}
),
model = "https://img.youtube.com/vi/${key}/hqdefault.jpg",
model = model,
contentDescription = "",
placeholder = rememberAsyncImagePainter(model = R.drawable.placeholder)
)
@@ -684,11 +693,18 @@ fun AvatarImage(
modifier: Modifier = Modifier
) {
if (author.avatarPath != null) {
val url = TmdbUtils.getFullAvatarPath(author)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
modifier = modifier
.size(size)
.clip(CircleShape),
model = TmdbUtils.getFullAvatarPath(author),
model = model,
contentDescription = ""
)
} else {
@@ -766,8 +782,14 @@ fun AccountIcon(
UserInitials(size = size, name = name)
} else {
Box(modifier = Modifier.size(size)) {
val model = ImageRequest.Builder(LocalContext.current)
.data(avatarUrl)
.diskCacheKey(avatarUrl)
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = avatarUrl,
model = model,
contentDescription = "",
modifier = Modifier
.size(60.dp)

View File

@@ -32,6 +32,8 @@ import androidx.constraintlayout.compose.Dimension
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v4.ListV4Service
@@ -143,11 +145,18 @@ private fun ListHeader(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
val url = TmdbUtils.getAccountGravatarUrl(list.createdBy.gravatarHash)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
modifier = Modifier
.clip(CircleShape)
.size(50.dp),
model = TmdbUtils.getAccountGravatarUrl(list.createdBy.gravatarHash),
model = model,
contentDescription = null,
)
Text(
@@ -446,8 +455,15 @@ private fun ListItemView(
modifier = Modifier.height(112.dp)
) {
listItem.backdropPath?.let {
val url = TmdbUtils.getFullBackdropPath(listItem.backdropPath)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = TmdbUtils.getFullBackdropPath(listItem.backdropPath),
model = model,
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier
@@ -470,6 +486,13 @@ private fun ListItemView(
) {
val (poster, content, ratingView) = createRefs()
val url = TmdbUtils.getFullPosterPath(listItem.posterPath)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
modifier = Modifier
.constrainAs(poster) {
@@ -480,7 +503,7 @@ private fun ListItemView(
}
.aspectRatio(0.7f)
.clip(RoundedCornerShape(10.dp)),
model = TmdbUtils.getFullPosterPath(listItem.posterPath) ?: R.drawable.placeholder_transparent,
model = model,
contentDescription = listItem.title
)

View File

@@ -31,6 +31,8 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import androidx.paging.compose.collectAsLazyPagingItems
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.accompanist.flowlayout.FlowRow
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.PagerState
@@ -165,7 +167,7 @@ private fun MediaViewContent(
.verticalScroll(state = rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
DetailHeader(
DetailHeader2(
posterUrl = TmdbUtils.getFullPosterPath(mediaItem?.posterPath),
posterContentDescription = mediaItem?.title,
backdropUrl = TmdbUtils.getFullBackdropPath(mediaItem?.backdropPath),
@@ -741,22 +743,22 @@ fun SimilarContentCard(
val similarContent = similarContentMap[itemId]
val pagingItems = similarContent?.collectAsLazyPagingItems()
ContentCard(
modifier = modifier,
title = stringResource(id = R.string.recommended_label)
) {
LazyRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
pagingItems?.let {
ContentCard(
modifier = modifier,
title = stringResource(id = R.string.recommended_label)
) {
item {
Spacer(modifier = Modifier.width(8.dp))
}
pagingItems?.let {
lazyPagingItems(it) { item ->
LazyRow(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
item {
Spacer(modifier = Modifier.width(8.dp))
}
lazyPagingItems(pagingItems) { item ->
item?.let {
TwoLineImageTextCard(
title = item.title,
@@ -769,13 +771,14 @@ fun SimilarContentCard(
AppNavItem.DetailView.withArgs(mediaType, item.id)
)
},
placeholder = Icons.Filled.Movie
placeholder = Icons.Filled.Movie,
hideSubtitle = true
)
}
}
}
item {
Spacer(modifier = Modifier.width(8.dp))
item {
Spacer(modifier = Modifier.width(8.dp))
}
}
}
}
@@ -964,8 +967,15 @@ private fun WatchProviderContainer(
context.startActivity(intent)
}
) {
val url = TmdbUtils.fullLogoPath(item.logoPath)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = TmdbUtils.fullLogoPath(item.logoPath),
model = model,
contentDescription = null,
modifier = Modifier
.size(48.dp)

View File

@@ -51,6 +51,7 @@ import androidx.compose.ui.draw.rotate
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextOverflow
@@ -59,6 +60,8 @@ import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController
import coil.compose.AsyncImage
import coil.request.CachePolicy
import coil.request.ImageRequest
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.owenlejeune.tvtime.R
import com.owenlejeune.tvtime.api.tmdb.api.v3.model.Episode
@@ -213,8 +216,15 @@ private fun EpisodeItem(episode: Episode) {
// Cloudy(
// modifier = Modifier.background(Color.Black.copy(alpha = 0.4f))
// ) {
val url = TmdbUtils.getFullEpisodeStillPath(it)
val model = ImageRequest.Builder(LocalContext.current)
.data(url)
.diskCacheKey(url ?: "")
.networkCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.build()
AsyncImage(
model = TmdbUtils.getFullEpisodeStillPath(it),
model = model,
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier