We used to rely on an external service to fetch the images. But they are not available anymorepull/5135/head
@ -1,25 +1,69 @@
|
||||
package com.example.jetsnack.ui.components |
||||
|
||||
import androidx.compose.foundation.layout.fillMaxSize |
||||
import android.annotation.SuppressLint |
||||
import android.graphics.Bitmap |
||||
import android.graphics.BitmapFactory |
||||
import androidx.compose.animation.AnimatedContent |
||||
import androidx.compose.animation.ExperimentalAnimationApi |
||||
import androidx.compose.animation.core.TweenSpec |
||||
import androidx.compose.animation.fadeIn |
||||
import androidx.compose.animation.fadeOut |
||||
import androidx.compose.animation.with |
||||
import androidx.compose.foundation.Image |
||||
import androidx.compose.foundation.layout.Box |
||||
import androidx.compose.runtime.Composable |
||||
import androidx.compose.runtime.LaunchedEffect |
||||
import androidx.compose.runtime.getValue |
||||
import androidx.compose.runtime.mutableStateOf |
||||
import androidx.compose.runtime.remember |
||||
import androidx.compose.runtime.setValue |
||||
import androidx.compose.ui.Modifier |
||||
import androidx.compose.ui.graphics.ImageBitmap |
||||
import androidx.compose.ui.graphics.asImageBitmap |
||||
import androidx.compose.ui.layout.ContentScale |
||||
import androidx.compose.ui.platform.LocalContext |
||||
import androidx.compose.ui.res.painterResource |
||||
import coil.compose.AsyncImage |
||||
import coil.request.ImageRequest |
||||
import com.example.jetsnack.R |
||||
import com.example.common.generated.resources.Res |
||||
import kotlinx.coroutines.Dispatchers |
||||
import kotlinx.coroutines.withContext |
||||
import org.jetbrains.compose.resources.ExperimentalResourceApi |
||||
|
||||
private val imagesCache = mutableMapOf<String, ImageBitmap>() |
||||
|
||||
@SuppressLint("UnusedContentLambdaTargetStateParameter") |
||||
@OptIn(ExperimentalAnimationApi::class, ExperimentalResourceApi::class) |
||||
@Composable |
||||
actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifier: Modifier) { |
||||
AsyncImage( |
||||
model = ImageRequest.Builder(LocalContext.current) |
||||
.data(imageUrl) |
||||
.crossfade(true) |
||||
.build(), |
||||
contentDescription = contentDescription, |
||||
placeholder = painterResource(R.drawable.placeholder), |
||||
modifier = Modifier.fillMaxSize(), |
||||
contentScale = ContentScale.Crop, |
||||
) |
||||
var img: ImageBitmap? by remember(imageUrl) { mutableStateOf(null) } |
||||
|
||||
|
||||
AnimatedContent(img, transitionSpec = { |
||||
fadeIn(TweenSpec()) with fadeOut(TweenSpec()) |
||||
}) { |
||||
if (img != null) { |
||||
Image(img!!, contentDescription = contentDescription, modifier = modifier, contentScale = ContentScale.Crop) |
||||
} else { |
||||
Box(modifier = modifier) |
||||
} |
||||
} |
||||
|
||||
LaunchedEffect(imageUrl) { |
||||
if (imagesCache.contains(imageUrl)) { |
||||
img = imagesCache[imageUrl] |
||||
} else { |
||||
withContext(Dispatchers.IO) { |
||||
img = try { |
||||
Res.readBytes(imageUrl).toAndroidBitmap().asImageBitmap().also { |
||||
imagesCache[imageUrl] = it |
||||
img = it |
||||
} |
||||
} catch (e: Throwable) { |
||||
e.printStackTrace() |
||||
null |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
fun ByteArray.toAndroidBitmap(): Bitmap { |
||||
return BitmapFactory.decodeByteArray(this, 0, size) |
||||
} |
After Width: | Height: | Size: 332 KiB |
After Width: | Height: | Size: 228 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 67 KiB |
After Width: | Height: | Size: 199 KiB |
After Width: | Height: | Size: 248 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 201 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 382 KiB |
After Width: | Height: | Size: 653 KiB |
After Width: | Height: | Size: 496 KiB |
After Width: | Height: | Size: 212 KiB |
After Width: | Height: | Size: 380 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 198 KiB |
After Width: | Height: | Size: 198 KiB |
After Width: | Height: | Size: 250 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 137 KiB |
After Width: | Height: | Size: 182 KiB |
After Width: | Height: | Size: 300 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 217 KiB |
After Width: | Height: | Size: 218 KiB |
After Width: | Height: | Size: 135 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 152 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 187 KiB |