Skip to content

Commit

Permalink
perf: Improved image loading
Browse files Browse the repository at this point in the history
Signed-off-by: Gabriel Fontán <[email protected]>
  • Loading branch information
BobbyESP committed Jul 27, 2024
1 parent 0441e72 commit 9da060d
Show file tree
Hide file tree
Showing 17 changed files with 86 additions and 174 deletions.
2 changes: 0 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,6 @@ dependencies {

//-------------------Image Loading-------------------//
implementation(libs.landscapist.coil)
implementation(libs.coil)
implementation(libs.compose.coil)

//-------------------FIREBASE-------------------//
"playstoreImplementation"(platform(libs.firebase.bom))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ fun AppLocalSettingsProvider(
val appSettingsState = AppMainSettingsStateFlow.collectAsStateWithLifecycle().value
val bottomSheetNavigator = rememberBottomSheetNavigator()
val navController = rememberNavController(bottomSheetNavigator)

val imageLoader = ImageLoader.Builder(context)
.memoryCache {
MemoryCache.Builder(context)
Expand All @@ -75,9 +76,10 @@ fun AppLocalSettingsProvider(
.respectCacheHeaders(false)
.allowHardware(true)
.crossfade(true)
.bitmapFactoryMaxParallelism(8)
.bitmapFactoryMaxParallelism(12)
.dispatcher(Dispatchers.IO)
.build()

val config = LocalConfiguration.current
val snackbarHostState = remember { SnackbarHostState() }
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ 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 com.bobbyesp.metadator.presentation.components.image.ArtworkAsyncImage
import com.bobbyesp.metadator.presentation.components.image.AsyncImage
import com.bobbyesp.metadator.presentation.theme.MetadatorTheme
import com.bobbyesp.ui.components.text.MarqueeText
import com.bobbyesp.utilities.Time
Expand All @@ -39,11 +39,11 @@ fun HorizontalSongCard(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
ArtworkAsyncImage(
AsyncImage(
modifier = Modifier
.size(64.dp)
.padding(4.dp),
artworkPath = song.artworkPath
imageModel = song.artworkPath
)
Column(
horizontalAlignment = Alignment.Start, modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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 com.bobbyesp.metadator.presentation.components.image.ArtworkAsyncImage
import com.bobbyesp.metadator.presentation.components.image.AsyncImage
import com.bobbyesp.metadator.presentation.theme.MetadatorTheme
import com.bobbyesp.ui.components.text.MarqueeText
import com.bobbyesp.utilities.model.Song
Expand All @@ -32,11 +32,11 @@ fun VerticalSongCard(
onClick = onClick
) {
Column {
ArtworkAsyncImage(
AsyncImage(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(1f),
artworkPath = song.artworkPath
imageModel = song.artworkPath
)
Column(
horizontalAlignment = Alignment.Start, modifier = Modifier.padding(8.dp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.adamratzman.spotify.models.Track
import com.bobbyesp.metadator.ext.formatArtistsName
import com.bobbyesp.metadator.presentation.components.image.ArtworkAsyncImage
import com.bobbyesp.metadator.presentation.components.image.AsyncImage
import com.bobbyesp.ui.components.text.MarqueeText

@OptIn(ExperimentalFoundationApi::class)
Expand Down Expand Up @@ -63,9 +63,9 @@ fun SpotifyHorizontalSongCard(
)
}
Box(contentAlignment = Alignment.CenterStart) {
ArtworkAsyncImage(
AsyncImage(
modifier = imageModifier.size(64.dp),
artworkPath = albumArtPath,
imageModel = albumArtPath,
shape = MaterialTheme.shapes.extraSmall
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ package com.bobbyesp.metadator.presentation.components.image
import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ErrorOutline
import androidx.compose.material.icons.rounded.MusicNote
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
Expand All @@ -18,168 +18,73 @@ 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.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import coil.ImageLoader
import coil.compose.AsyncImage
import coil.compose.AsyncImagePainter
import coil.request.ImageRequest
import coil.request.SuccessResult
import com.bobbyesp.metadator.R
import com.bobbyesp.ui.components.others.PlaceholderCreator
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.coil.CoilImage
import com.skydoves.landscapist.coil.CoilImageState
import com.skydoves.landscapist.coil.LocalCoilImageLoader

@Composable
fun AsyncImageImpl(
model: Any,
contentDescription: String?,
modifier: Modifier = Modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
transform: (AsyncImagePainter.State) -> AsyncImagePainter.State = AsyncImagePainter.DefaultTransform,
onState: ((AsyncImagePainter.State) -> Unit)? = null,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
isPreview: Boolean = false,
context: Context = LocalContext.current
) {
// Create an ImageLoader if it doesn't exist yet and remember it with the current context.
val imageLoader = LocalCoilImageLoader.current

if (isPreview) {
Image(
painter = painterResource(R.drawable.metadator_logo_foreground),
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
)
} else {
AsyncImage(
model = model,
imageLoader = imageLoader ?: ImageLoader.Builder(context).build(),
onState = onState,
filterQuality = filterQuality,
transform = transform,
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
)
}
}


@Composable
fun AsyncImageImpl(
model: Any,
contentDescription: String?,
modifier: Modifier = Modifier,
error: Painter? = null,
placeholder: Painter? = null,
fallback: Painter? = null,
onLoading: ((AsyncImagePainter.State.Loading) -> Unit)? = null,
onSuccess: ((AsyncImagePainter.State.Success) -> Unit)? = null,
onError: ((AsyncImagePainter.State.Error) -> Unit)? = null,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DrawScope.DefaultFilterQuality,
) {

val context = LocalContext.current

val imageLoader = LocalCoilImageLoader.current

val placeholderPainter = placeholder ?: painterResource(R.drawable.metadator_logo_foreground)

AsyncImage(
model = model,
imageLoader = imageLoader ?: ImageLoader.Builder(context).build(),
filterQuality = filterQuality,
onError = onError,
onLoading = onLoading,
onSuccess = onSuccess,
fallback = fallback,
error = error,
placeholder = placeholderPainter,
contentDescription = contentDescription,
modifier = modifier,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter
)
}

@Composable
fun ArtworkAsyncImage(
fun AsyncImage(
modifier: Modifier = Modifier,
imageModel: Any? = null,
imageModifier: Modifier = Modifier,
shape: Shape = MaterialTheme.shapes.small,
artworkPath: Any? = null
placeholder: ImageVector = Icons.Rounded.MusicNote,
context: Context = LocalContext.current,
imageLoader: ImageLoader? = LocalCoilImageLoader.current,
onSuccessData: (CoilImageState.Success) -> Unit = { _ -> }
) {
var showArtwork by remember { mutableStateOf(true) }

val model by remember(artworkPath) {
mutableStateOf(artworkPath)
val imageUrl: Any? by remember(imageModel) {
mutableStateOf(imageModel)
}

LaunchedEffect(model) {
showArtwork = model != null
}

if (model != null && showArtwork) {
Box(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
AsyncImageImpl(
modifier = imageModifier
.fillMaxSize()
.clip(shape),
model = model!!,
onState = { state ->
//if it was successful, don't show the placeholder, else show it
showArtwork =
state !is AsyncImagePainter.State.Error && state !is AsyncImagePainter.State.Empty
},
contentDescription = "Song cover",
contentScale = ContentScale.Crop,
isPreview = false
)
}
} else {
Box(
modifier = modifier,
contentAlignment = Alignment.Center,
) {
PlaceholderCreator(
modifier = imageModifier
.fillMaxSize()
.clip(shape),
icon = Icons.Rounded.MusicNote,
colorful = false,
contentDescription = "Song cover placeholder"
)
}
Box(
modifier = modifier.clip(shape),
contentAlignment = Alignment.Center,
) {
CoilImage(
modifier = Modifier.fillMaxSize(),
imageModel = { imageUrl },
imageOptions = ImageOptions(
contentDescription = null,
contentScale = ContentScale.Crop
),
onImageStateChanged = { state ->
if (state is CoilImageState.Success) {
onSuccessData(state)
}
},
loading = {
PlaceholderCreator(
modifier = imageModifier
.fillMaxSize(),
icon = placeholder,
colorful = false,
contentDescription = "Song cover placeholder"
)
},
failure = {
PlaceholderCreator(
modifier = imageModifier
.fillMaxSize(),
icon = Icons.Rounded.ErrorOutline,
colorful = false,
contentDescription = "Song cover failed to load"
)
},
imageLoader = { imageLoader ?: ImageLoader(context) },
)
}
}


@Composable
fun loadBitmapFromUrl(url: String): Bitmap? {
var bitmap by remember { mutableStateOf<Bitmap?>(null) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ fun MediaStorePage(
LazyVerticalGridScrollbar(
state = lazyGridState,
settings = ScrollbarSettings(
thumbUnselectedColor = MaterialTheme.colorScheme.onSurfaceVariant,
thumbSelectedColor = MaterialTheme.colorScheme.primary,
selectionActionable = ScrollbarSelectionActionable.WhenVisible,
)
Expand Down Expand Up @@ -101,6 +102,7 @@ fun MediaStorePage(
LazyColumnScrollbar(
state = lazyListState,
settings = ScrollbarSettings(
thumbUnselectedColor = MaterialTheme.colorScheme.onSurfaceVariant,
thumbSelectedColor = MaterialTheme.colorScheme.primary,
selectionActionable = ScrollbarSelectionActionable.WhenVisible,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ fun MediaplayerPage(
LazyColumnScrollbar(
state = mediaStoreLazyColumnState,
settings = ScrollbarSettings(
thumbUnselectedColor = MaterialTheme.colorScheme.onSurfaceVariant,
thumbSelectedColor = MaterialTheme.colorScheme.primary,
selectionActionable = ScrollbarSelectionActionable.WhenVisible,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class MediaplayerViewModel @Inject constructor(
calculateProgressValues(mediaState.progress)
}
}

is MediaState.Playing -> mutableMediaplayerPageState.update {
(it.uiState as? PlayerState.Ready)?.let { readyState ->
it.copy(
Expand All @@ -80,6 +81,7 @@ class MediaplayerViewModel @Inject constructor(
calculateProgressValues(mediaState.progress)
}
}

is MediaState.Ready -> {
mutableMediaplayerPageState.update {
it.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import androidx.compose.ui.unit.dp
import com.bobbyesp.ui.motion.materialSharedAxisXIn
import com.bobbyesp.ui.motion.materialSharedAxisXOut

val CollapsedPlayerHeight = 78.dp
val CollapsedPlayerHeight = 84.dp
val SeekToButtonSize = 48.dp
val PlayerCommandsButtonSize = 48.dp

Expand Down
Loading

0 comments on commit 9da060d

Please sign in to comment.