Skip to content

Commit

Permalink
Migrate to Coil3
Browse files Browse the repository at this point in the history
Unify image loading with wrapper composable
  • Loading branch information
yasinkacmaz committed Sep 19, 2024
1 parent 7ba4cb8 commit 8fce480
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 132 deletions.
6 changes: 3 additions & 3 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ android {

dependencies {
implementation(libs.bundles.androidX)
implementation(libs.bundles.io)
implementation(libs.bundles.koin)
implementation(libs.bundles.coil)
implementation(compose.runtime)
implementation(compose.foundation)
implementation(compose.ui)
Expand All @@ -70,9 +73,6 @@ dependencies {
implementation(libs.compose.paging)
implementation(libs.compose.activity)
implementation(libs.compose.navigation)
implementation(libs.bundles.io)
implementation(libs.bundles.koin)
implementation(libs.coil)
debugImplementation(libs.androidX.tracing)
debugImplementation(libs.compose.testManifest)
testImplementation(libs.bundles.test)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.navigation.compose.rememberNavController
import coil3.ImageLoader
import coil3.annotation.ExperimentalCoilApi
import coil3.compose.setSingletonImageLoaderFactory
import coil3.request.CachePolicy
import coil3.request.crossfade
import coil3.util.DebugLogger
import com.yasinkacmaz.jetflix.ui.theme.JetflixTheme
import org.koin.compose.KoinContext

Expand All @@ -35,6 +42,7 @@ class MainActivity : ComponentActivity() {
detectDarkMode = { isDarkTheme.value },
)
enableEdgeToEdge(statusBarStyle = systemBarStyle, navigationBarStyle = systemBarStyle)
SetupCoilImageLoader()
JetflixTheme(isDarkTheme = isDarkTheme.value) {
KoinContext {
CompositionLocalProvider(
Expand All @@ -47,3 +55,16 @@ class MainActivity : ComponentActivity() {
}
}
}

@OptIn(ExperimentalCoilApi::class)
@Composable
private fun SetupCoilImageLoader() {
setSingletonImageLoaderFactory { context ->
ImageLoader.Builder(context)
.crossfade(true)
.memoryCachePolicy(CachePolicy.ENABLED)
.diskCachePolicy(CachePolicy.ENABLED)
.logger(DebugLogger())
.build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import androidx.compose.animation.Animatable
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationVector4D
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll
Expand Down Expand Up @@ -48,7 +47,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout
Expand All @@ -64,11 +62,6 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter.State.Error
import coil.compose.AsyncImagePainter.State.Loading
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import com.yasinkacmaz.jetflix.R
import com.yasinkacmaz.jetflix.ui.common.error.ErrorColumn
import com.yasinkacmaz.jetflix.ui.common.loading.LoadingColumn
Expand All @@ -80,6 +73,7 @@ import com.yasinkacmaz.jetflix.ui.navigation.Screen
import com.yasinkacmaz.jetflix.ui.theme.spacing
import com.yasinkacmaz.jetflix.ui.widget.BottomArcShape
import com.yasinkacmaz.jetflix.util.GetVibrantColorFromPoster
import com.yasinkacmaz.jetflix.util.JetflixImage
import com.yasinkacmaz.jetflix.util.animation.AnimationDuration
import com.yasinkacmaz.jetflix.util.animation.springAnimation
import com.yasinkacmaz.jetflix.util.dpToPx
Expand Down Expand Up @@ -218,14 +212,12 @@ private fun Backdrop(backdropUrl: String, movieName: String, modifier: Modifier)
colors = CardDefaults.cardColors(containerColor = LocalVibrantColor.current.value),
modifier = modifier.height(360.dp),
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(data = backdropUrl)
.crossfade(AnimationDuration.LONG.duration)
.build(),
JetflixImage(
data = backdropUrl,
contentScale = ContentScale.FillHeight,
contentDescription = stringResource(R.string.backdrop_content_description, movieName),
modifier = modifier.fillMaxWidth(),
modifier = Modifier.fillMaxWidth(),
crossfade = AnimationDuration.LONG,
)
}
}
Expand All @@ -244,11 +236,11 @@ private fun Poster(posterUrl: String, movieName: String, modifier: Modifier) {
modifier = modifier.scale(scale),
onClick = { isScaled.value = !isScaled.value },
) {
AsyncImage(
model = posterUrl,
modifier = Modifier.fillMaxSize(),
contentDescription = stringResource(id = R.string.movie_poster_content_description, movieName),
JetflixImage(
Modifier.fillMaxSize(),
data = posterUrl,
contentScale = ContentScale.Fit,
contentDescription = stringResource(R.string.poster_content_description, movieName),
)
}
}
Expand Down Expand Up @@ -427,21 +419,13 @@ private fun MovieImage(image: Image, index: Int) {
.height(200.dp)
.clickable { navController.navigate(Screen.MovieImages(movieId, index)) },
) {
val painter = rememberAsyncImagePainter(
model = image.url,
JetflixImage(
modifier = Modifier.fillMaxSize(),
data = image.url,
placeholder = rememberVectorPainter(Icons.Default.Image),
error = rememberVectorPainter(Icons.Default.BrokenImage),
)
val (colorFilter, contentScale) = when (painter.state) {
is Error, is Loading -> ColorFilter.tint(Color.LightGray) to ContentScale.Fit
else -> null to ContentScale.Crop
}
Image(
modifier = Modifier.fillMaxSize(),
painter = painter,
colorFilter = colorFilter,
contentDescription = stringResource(id = R.string.poster_content_description),
contentScale = contentScale,
contentDescription = stringResource(R.string.poster_content_description),
contentScale = ContentScale.Crop,
)
}
}
Expand All @@ -461,23 +445,15 @@ private fun ProductionCompany(company: ProductionCompany) {
.fillMaxSize()
.padding(MaterialTheme.spacing.xs),
) {
val request = ImageRequest.Builder(LocalContext.current)
.data(company.logoUrl)
.crossfade(true)
.build()
val painter = rememberAsyncImagePainter(
model = request,
JetflixImage(
modifier = Modifier.size(200.dp, 120.dp),
data = company.logoUrl,
contentScale = ContentScale.Fit,
placeholder = painterResource(id = R.drawable.ic_jetflix),
error = painterResource(id = R.drawable.ic_jetflix),
)
Image(
painter = painter,
contentDescription = stringResource(
id = R.string.production_company_logo_content_description,
company.name,
),
contentScale = ContentScale.Fit,
modifier = Modifier.size(200.dp, 120.dp),
)
Text(
text = company.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.yasinkacmaz.jetflix.R
import com.yasinkacmaz.jetflix.ui.theme.spacing
import com.yasinkacmaz.jetflix.util.JetflixImage

@Composable
fun ImagesScreen(images: List<Image>, initialPage: Int) {
Expand Down Expand Up @@ -59,9 +59,8 @@ private fun Poster(image: Image) {
.animateContentSize(),
) {
Box {
AsyncImage(
model = image.url,
contentDescription = null,
JetflixImage(
data = image.url,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.align(Alignment.Center)
Expand All @@ -76,8 +75,8 @@ private fun Poster(image: Image) {

@Composable
private fun BlurImage(url: String) {
AsyncImage(
model = url,
JetflixImage(
data = url,
contentDescription = stringResource(id = R.string.poster_content_description),
contentScale = ContentScale.FillHeight,
modifier = Modifier
Expand All @@ -94,7 +93,7 @@ private fun BoxScope.VoteCount(voteCount: Int) {
.wrapContentSize()
.align(Alignment.BottomStart)
.background(
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.3f),
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.4f),
shape = RoundedCornerShape(bottomStart = 12.dp, topEnd = 12.dp),
)
.padding(MaterialTheme.spacing.xs),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.yasinkacmaz.jetflix.ui.moviedetail.person

import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
Expand All @@ -13,26 +12,20 @@ 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.ColorFilter
import androidx.compose.ui.graphics.vector.rememberVectorPainter
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.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImagePainter.State.Error
import coil.compose.AsyncImagePainter.State.Loading
import coil.compose.rememberAsyncImagePainter
import coil.request.ImageRequest
import com.yasinkacmaz.jetflix.R
import com.yasinkacmaz.jetflix.ui.main.LocalNavController
import com.yasinkacmaz.jetflix.ui.moviedetail.credits.Person
import com.yasinkacmaz.jetflix.ui.moviedetail.credits.placeholderIcon
import com.yasinkacmaz.jetflix.ui.navigation.Screen
import com.yasinkacmaz.jetflix.ui.theme.spacing
import com.yasinkacmaz.jetflix.util.JetflixImage
import com.yasinkacmaz.jetflix.util.transformation.CircleTopCropTransformation

@Composable
Expand All @@ -42,27 +35,16 @@ fun Person(person: Person, modifier: Modifier = Modifier) {
modifier.clickable { navController.navigate(Screen.Profile(person.id)) },
horizontalAlignment = Alignment.CenterHorizontally,
) {
val request = ImageRequest.Builder(LocalContext.current)
.data(person.profilePhotoUrl)
.crossfade(true)
.transformations(CircleTopCropTransformation())
.build()
val placeholderPainter = rememberVectorPainter(person.gender.placeholderIcon)
val painter =
rememberAsyncImagePainter(model = request, error = placeholderPainter, placeholder = placeholderPainter)
val colorFilter = when (painter.state) {
is Error, is Loading -> ColorFilter.tint(Color.LightGray)
else -> null
}
Image(
JetflixImage(
modifier = Modifier
.size(120.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceContainerHighest),
painter = painter,
colorFilter = colorFilter,
data = person.profilePhotoUrl,
placeholder = rememberVectorPainter(person.gender.placeholderIcon),
contentDescription = stringResource(id = R.string.person_content_description, person.name, person.role),
contentScale = ContentScale.FillWidth,
transformations = listOf(CircleTopCropTransformation()),
)
Text(
text = person.name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
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.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
Expand All @@ -38,9 +33,9 @@ import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import com.yasinkacmaz.jetflix.R
import com.yasinkacmaz.jetflix.ui.theme.spacing
import com.yasinkacmaz.jetflix.util.JetflixImage
import com.yasinkacmaz.jetflix.util.rateColor

@Composable
Expand All @@ -64,20 +59,12 @@ fun MovieItem(movie: Movie, modifier: Modifier = Modifier, onMovieClicked: (Int)

@Composable
private fun BoxScope.MoviePoster(posterPath: String, movieName: String) {
var colorFilter by remember { mutableStateOf<ColorFilter?>(null) }
var contentScale by remember { mutableStateOf(ContentScale.Fit) }
AsyncImage(
model = posterPath,
error = rememberVectorPainter(Icons.Filled.BrokenImage),
JetflixImage(
data = posterPath,
placeholder = rememberVectorPainter(Icons.Default.Movie),
onLoading = { colorFilter = ColorFilter.tint(Color.Gray) },
onSuccess = {
colorFilter = null
contentScale = ContentScale.FillBounds
},
error = rememberVectorPainter(Icons.Filled.BrokenImage),
contentDescription = stringResource(id = R.string.movie_poster_content_description, movieName),
colorFilter = colorFilter,
contentScale = contentScale,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.fillMaxSize()
.align(Alignment.Center),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,12 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.yasinkacmaz.jetflix.R
import com.yasinkacmaz.jetflix.ui.common.error.ErrorColumn
import com.yasinkacmaz.jetflix.ui.common.loading.LoadingColumn
import com.yasinkacmaz.jetflix.ui.theme.spacing
import com.yasinkacmaz.jetflix.util.GetVibrantColorFromPoster
import com.yasinkacmaz.jetflix.util.JetflixImage
import com.yasinkacmaz.jetflix.util.openInChromeCustomTab

@Composable
Expand Down Expand Up @@ -104,19 +103,14 @@ private fun Profile(profile: Profile) {
) {
LazyColumn(state = lazyListState) {
item {
AsyncImage(
JetflixImage(
data = profile.profilePhotoUrl,
contentScale = ContentScale.FillWidth,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = LocalConfiguration.current.screenHeightDp.dp * 0.4f)
.background(MaterialTheme.colorScheme.surface)
.animateContentSize(),
model = ImageRequest.Builder(LocalContext.current)
.data(profile.profilePhotoUrl)
.crossfade(true)
.build(),
contentDescription = null,
contentScale = ContentScale.FillWidth,
alignment = Alignment.TopCenter,
)
}

Expand Down
Loading

0 comments on commit 8fce480

Please sign in to comment.