diff --git a/examples/jetsnack/android/src/main/AndroidManifest.xml b/examples/jetsnack/android/src/main/AndroidManifest.xml index d456c02f9d8..bc60a6ee288 100644 --- a/examples/jetsnack/android/src/main/AndroidManifest.xml +++ b/examples/jetsnack/android/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ android:allowBackup="false" android:supportsRtl="true" android:theme="@style/Theme.AppCompat.Light.NoActionBar"> - + diff --git a/examples/jetsnack/common/build.gradle.kts b/examples/jetsnack/common/build.gradle.kts index 53e38380ee9..1b493a005b8 100644 --- a/examples/jetsnack/common/build.gradle.kts +++ b/examples/jetsnack/common/build.gradle.kts @@ -39,6 +39,7 @@ kotlin { api(compose.runtime) api(compose.foundation) api(compose.material) + implementation(compose.components.resources) implementation(libs.kotlinx.coroutines) } } diff --git a/examples/jetsnack/common/src/androidMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt b/examples/jetsnack/common/src/androidMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt index fe715cb6527..bfd48c50787 100644 --- a/examples/jetsnack/common/src/androidMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt +++ b/examples/jetsnack/common/src/androidMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt @@ -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() + +@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) } \ No newline at end of file diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/almonds.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/almonds.jpg new file mode 100644 index 00000000000..0f1eef57e38 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/almonds.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/apple_chips.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_chips.jpg new file mode 100644 index 00000000000..37c805d169d Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_chips.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/apple_juice.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_juice.jpg new file mode 100644 index 00000000000..d519dbcd460 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_juice.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/apple_pie.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_pie.jpg new file mode 100644 index 00000000000..41096b22161 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_pie.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/apple_sauce.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_sauce.jpg new file mode 100644 index 00000000000..7f5331ee245 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/apple_sauce.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/apples.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/apples.jpg new file mode 100644 index 00000000000..cc729f1646f Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/apples.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/cheese.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/cheese.jpg new file mode 100644 index 00000000000..c5c6dce61ce Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/cheese.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/chips.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/chips.jpg new file mode 100644 index 00000000000..04d5fe2cd39 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/chips.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/cupcake.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/cupcake.jpg new file mode 100644 index 00000000000..42e766d8439 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/cupcake.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/desserts.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/desserts.jpg new file mode 100644 index 00000000000..6d449907656 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/desserts.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/donut.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/donut.jpg new file mode 100644 index 00000000000..076896a812c Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/donut.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/eclair.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/eclair.jpg new file mode 100644 index 00000000000..56017803457 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/eclair.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/froyo.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/froyo.jpg new file mode 100644 index 00000000000..e1bb068cc99 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/froyo.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/fruit.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/fruit.jpg new file mode 100644 index 00000000000..41224731846 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/fruit.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/gingerbread.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/gingerbread.jpg new file mode 100644 index 00000000000..ac069de1037 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/gingerbread.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/gluten_free.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/gluten_free.jpg new file mode 100644 index 00000000000..0745457a36b Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/gluten_free.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/grapes.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/grapes.jpg new file mode 100644 index 00000000000..3b573787cf0 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/grapes.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/honeycomb.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/honeycomb.jpg new file mode 100644 index 00000000000..ea632bd25d9 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/honeycomb.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/ice_cream_sandwich.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/ice_cream_sandwich.jpg new file mode 100644 index 00000000000..fd77631e9aa Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/ice_cream_sandwich.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/jelly_bean.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/jelly_bean.jpg new file mode 100644 index 00000000000..84a10208c9b Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/jelly_bean.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/kitkat.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/kitkat.jpg new file mode 100644 index 00000000000..75b2e44abb9 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/kitkat.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/kiwi.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/kiwi.jpg new file mode 100644 index 00000000000..2197fbd48f7 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/kiwi.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/lollipop.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/lollipop.jpg new file mode 100644 index 00000000000..98d1db7d2fd Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/lollipop.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/mango.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/mango.jpg new file mode 100644 index 00000000000..717773d7b5c Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/mango.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/marshmallow.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/marshmallow.jpg new file mode 100644 index 00000000000..cdc11592262 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/marshmallow.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/nougat.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/nougat.jpg new file mode 100644 index 00000000000..1a844d9b9f1 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/nougat.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/nuts.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/nuts.jpg new file mode 100644 index 00000000000..03556767ec0 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/nuts.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/oreo.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/oreo.jpg new file mode 100644 index 00000000000..cf2c3e534c9 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/oreo.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/organic.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/organic.jpg new file mode 100644 index 00000000000..2847abf4f07 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/organic.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/paleo.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/paleo.jpg new file mode 100644 index 00000000000..750fcd58e74 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/paleo.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/pie.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/pie.jpg new file mode 100644 index 00000000000..439c18cfbbd Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/pie.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/placeholder.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/placeholder.jpg new file mode 100644 index 00000000000..31e05faacbe Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/placeholder.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/popcorn.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/popcorn.jpg new file mode 100644 index 00000000000..02713ffdbfe Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/popcorn.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/pretzels.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/pretzels.jpg new file mode 100644 index 00000000000..d31d4aa4eba Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/pretzels.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/smoothies.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/smoothies.jpg new file mode 100644 index 00000000000..f2eaa31bf1d Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/smoothies.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/composeResources/files/vegan.jpg b/examples/jetsnack/common/src/commonMain/composeResources/files/vegan.jpg new file mode 100644 index 00000000000..29276a68b70 Binary files /dev/null and b/examples/jetsnack/common/src/commonMain/composeResources/files/vegan.jpg differ diff --git a/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Search.kt b/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Search.kt index d22a41edb47..cc949b01502 100644 --- a/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Search.kt +++ b/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Search.kt @@ -65,19 +65,19 @@ private val searchCategoryCollections = listOf( categories = listOf( SearchCategory( name = "Chips & crackers", - imageUrl = "https://source.unsplash.com/UsSdMZ78Q3E" + imageUrl = "files/chips.jpg" ), SearchCategory( name = "Fruit snacks", - imageUrl = "https://source.unsplash.com/SfP1PtM9Qa8" + imageUrl = "files/fruit.jpg" ), SearchCategory( name = "Desserts", - imageUrl = "https://source.unsplash.com/_jk8KIyN_uA" + imageUrl = "files/desserts.jpg" ), SearchCategory( name = "Nuts ", - imageUrl = "https://source.unsplash.com/UsSdMZ78Q3E" + imageUrl = "files/nuts.jpg" ) ) ), @@ -87,27 +87,27 @@ private val searchCategoryCollections = listOf( categories = listOf( SearchCategory( name = "Organic", - imageUrl = "https://source.unsplash.com/7meCnGCJ5Ms" + imageUrl = "files/organic.jpg" ), SearchCategory( name = "Gluten Free", - imageUrl = "https://source.unsplash.com/m741tj4Cz7M" + imageUrl = "files/gluten_free.jpg" ), SearchCategory( name = "Paleo", - imageUrl = "https://source.unsplash.com/dt5-8tThZKg" + imageUrl = "files/paleo.jpg" ), SearchCategory( name = "Vegan", - imageUrl = "https://source.unsplash.com/ReXxkS1m1H0" + imageUrl = "files/vegan.jpg" ), SearchCategory( name = "Vegitarian", - imageUrl = "https://source.unsplash.com/IGfIGP5ONV0" + imageUrl = "files/grapes.jpg" ), SearchCategory( name = "Whole30", - imageUrl = "https://source.unsplash.com/9MzCd76xLGk" + imageUrl = "files/popcorn.jpg" ) ) ) diff --git a/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Snack.kt b/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Snack.kt index ae2ddb00cc8..a01ced20837 100644 --- a/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Snack.kt +++ b/examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Snack.kt @@ -17,6 +17,7 @@ package com.example.jetsnack.model import androidx.compose.runtime.Immutable +import com.example.common.generated.resources.Res @Immutable data class Snack( @@ -37,190 +38,190 @@ val snacks = listOf( id = 1L, name = "Cupcake", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/pGM4sjt_BdQ", + imageUrl = "files/cupcake.jpg", price = 299 ), Snack( id = 2L, name = "Donut", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/Yc5sL-ejk6U", + imageUrl = "files/donut.jpg", price = 290 ), Snack( id = 3L, name = "Eclair", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/-LojFX9NfPY", + imageUrl = "files/eclair.jpg", price = 289 ), Snack( id = 4L, name = "Froyo", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/3U2V5WqK1PQ", + imageUrl = "files/froyo.jpg", price = 288 ), Snack( id = 5L, name = "Gingerbread", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/Y4YR9OjdIMk", + imageUrl = "files/gingerbread.jpg", price = 499 ), Snack( id = 6L, name = "Honeycomb", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/bELvIg_KZGU", + imageUrl = "files/honeycomb.jpg", price = 309 ), Snack( id = 7L, name = "Ice Cream Sandwich", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/YgYJsFDd4AU", + imageUrl = "files/ice_cream_sandwich.jpg", price = 1299 ), Snack( id = 8L, name = "Jellybean", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/0u_vbeOkMpk", + imageUrl = "files/jelly_bean.jpg", price = 109 ), Snack( id = 9L, name = "KitKat", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/yb16pT5F_jE", + imageUrl = "files/kitkat.jpg", price = 549 ), Snack( id = 10L, name = "Lollipop", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/AHF_ZktTL6Q", + imageUrl = "files/lollipop.jpg", price = 209 ), Snack( id = 11L, name = "Marshmallow", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/rqFm0IgMVYY", + imageUrl = "files/marshmallow.jpg", price = 219 ), Snack( id = 12L, name = "Nougat", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/qRE_OpbVPR8", + imageUrl = "files/nougat.jpg", price = 309 ), Snack( id = 13L, name = "Oreo", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/33fWPnyN6tU", + imageUrl = "files/oreo.jpg", price = 339 ), Snack( id = 14L, name = "Pie", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/aX_ljOOyWJY", + imageUrl = "files/pie.jpg", price = 249 ), Snack( id = 15L, name = "Chips", - imageUrl = "https://source.unsplash.com/UsSdMZ78Q3E", + imageUrl = "files/chips.jpg", price = 277 ), Snack( id = 16L, name = "Pretzels", - imageUrl = "https://source.unsplash.com/7meCnGCJ5Ms", + imageUrl = "files/pretzels.jpg", price = 154 ), Snack( id = 17L, name = "Smoothies", - imageUrl = "https://source.unsplash.com/m741tj4Cz7M", + imageUrl = "files/smoothies.jpg", price = 257 ), Snack( id = 18L, name = "Popcorn", - imageUrl = "https://source.unsplash.com/iuwMdNq0-s4", + imageUrl = "files/popcorn.jpg", price = 167 ), Snack( id = 19L, name = "Almonds", - imageUrl = "https://source.unsplash.com/qgWWQU1SzqM", + imageUrl = "files/almonds.jpg", price = 123 ), Snack( id = 20L, name = "Cheese", - imageUrl = "https://source.unsplash.com/9MzCd76xLGk", + imageUrl = "files/cheese.jpg", price = 231 ), Snack( id = 21L, name = "Apples", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/1d9xXWMtQzQ", + imageUrl = "files/apples.jpg", price = 221 ), Snack( id = 22L, name = "Apple sauce", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/wZxpOw84QTU", + imageUrl = "files/apple_sauce.jpg", price = 222 ), Snack( id = 23L, name = "Apple chips", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/okzeRxm_GPo", + imageUrl = "files/apple_chips.jpg", price = 231 ), Snack( id = 24L, name = "Apple juice", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/l7imGdupuhU", + imageUrl = "files/apple_juice.jpg", price = 241 ), Snack( id = 25L, name = "Apple pie", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/bkXzABDt08Q", + imageUrl = "files/apple_pie.jpg", price = 225 ), Snack( id = 26L, name = "Grapes", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/y2MeW00BdBo", + imageUrl = "files/grapes.jpg", price = 266 ), Snack( id = 27L, name = "Kiwi", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/1oMGgHn-M8k", + imageUrl = "files/kiwi.jpg", price = 127 ), Snack( id = 28L, name = "Mango", tagline = "A tag line", - imageUrl = "https://source.unsplash.com/TIGDsyy0TK4", + imageUrl = "files/mango.jpg", price = 128 ) ) diff --git a/examples/jetsnack/common/src/desktopMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt b/examples/jetsnack/common/src/desktopMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt index 2aa7db91473..cb465b829a8 100644 --- a/examples/jetsnack/common/src/desktopMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt +++ b/examples/jetsnack/common/src/desktopMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt @@ -9,15 +9,17 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.layout.ContentScale +import com.example.common.generated.resources.Res import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import org.jetbrains.compose.resources.ExperimentalResourceApi import java.net.URL import javax.imageio.ImageIO private val imagesCache = mutableMapOf() -@OptIn(ExperimentalAnimationApi::class) +@OptIn(ExperimentalAnimationApi::class, ExperimentalResourceApi::class) @Composable actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifier: Modifier) { var img: ImageBitmap? by remember(imageUrl) { mutableStateOf(null) } @@ -39,7 +41,7 @@ actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifi } else { withContext(Dispatchers.IO) { img = try { - ImageIO.read(URL(imageUrl)).toComposeImageBitmap().also { + org.jetbrains.skia.Image.makeFromEncoded(Res.readBytes(imageUrl)).toComposeImageBitmap().also { imagesCache[imageUrl] = it img = it } diff --git a/examples/jetsnack/common/src/iosMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.ios.kt b/examples/jetsnack/common/src/iosMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.ios.kt index 1e8bdc408d6..fe33db7749d 100644 --- a/examples/jetsnack/common/src/iosMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.ios.kt +++ b/examples/jetsnack/common/src/iosMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.ios.kt @@ -9,10 +9,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.layout.ContentScale +import com.example.common.generated.resources.Res import kotlinx.cinterop.ExperimentalForeignApi import kotlinx.cinterop.addressOf import kotlinx.cinterop.usePinned import kotlinx.coroutines.* +import org.jetbrains.compose.resources.ExperimentalResourceApi import org.jetbrains.skia.Image import platform.Foundation.* import platform.posix.memcpy @@ -21,7 +23,7 @@ import kotlin.coroutines.resumeWithException private val imagesCache = mutableMapOf() -@OptIn(ExperimentalAnimationApi::class) +@OptIn(ExperimentalAnimationApi::class, ExperimentalResourceApi::class) @Composable actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifier: Modifier) { var img: ImageBitmap? by remember(imageUrl) { mutableStateOf(null) } @@ -43,7 +45,7 @@ actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifi } else { withContext(Dispatchers.IO) { img = try { - loadImage(imageUrl).also { + Image.makeFromEncoded(Res.readBytes(imageUrl)).toComposeImageBitmap().also { imagesCache[imageUrl] = it img = it } @@ -54,33 +56,4 @@ actual fun SnackAsyncImage(imageUrl: String, contentDescription: String?, modifi } } } -} - -@OptIn(ExperimentalForeignApi::class) -suspend fun loadImage(url: String): ImageBitmap = suspendCancellableCoroutine { continuation -> - val nsUrl = NSURL(string = url) - val task = NSURLSession.sharedSession.dataTaskWithURL(nsUrl) { data, response, error -> - if (data != null) { - val byteArray = ByteArray(data.length.toInt()).apply { - usePinned { - memcpy( - it.addressOf(0), - data.bytes, - data.length - ) - } - } - - continuation.resume(Image.makeFromEncoded(byteArray).toComposeImageBitmap()) - } else { - error?.let { - continuation.resumeWithException(Exception(it.localizedDescription)) - } - } - } - - task.resume() - continuation.invokeOnCancellation { - task.cancel() - } } \ No newline at end of file diff --git a/examples/jetsnack/common/src/wasmJsMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt b/examples/jetsnack/common/src/wasmJsMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt index c06206cae75..4204802ced2 100644 --- a/examples/jetsnack/common/src/wasmJsMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt +++ b/examples/jetsnack/common/src/wasmJsMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt @@ -6,11 +6,14 @@ import androidx.compose.foundation.Image import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.layout.ContentScale +import com.example.common.generated.resources.Res import kotlinx.coroutines.* import com.example.jetsnack.model.snacks +import org.jetbrains.compose.resources.ExperimentalResourceApi val imagesCache = mutableMapOf() +@OptIn(ExperimentalResourceApi::class) @Composable actual fun SnackAsyncImage( imageUrl: String, @@ -28,21 +31,22 @@ actual fun SnackAsyncImage( if (imagesCache.contains(imageUrl)) { bitmap = imagesCache[imageUrl]!! } else { - val arrayBuffer = loadImage(imageUrl) - val skiaImg = org.jetbrains.skia.Image.makeFromEncoded(arrayBuffer.toByteArray()) - imagesCache[imageUrl] = skiaImg.toComposeImageBitmap() + imagesCache[imageUrl] = org.jetbrains.skia.Image.makeFromEncoded( + Res.readBytes(imageUrl) + ).toComposeImageBitmap() bitmap = imagesCache[imageUrl] } } } +@OptIn(ExperimentalResourceApi::class) suspend fun CoroutineScope.prepareImagesCache() { val jobs = mutableListOf() // We have not many images, so we can prepare and cache them upfront snacks.forEach { val j = launch { - val arrayBuffer = loadImage(it.imageUrl) - val skiaImg = org.jetbrains.skia.Image.makeFromEncoded(arrayBuffer.toByteArray()) - imagesCache[it.imageUrl] = skiaImg.toComposeImageBitmap() + imagesCache[it.imageUrl] = org.jetbrains.skia.Image.makeFromEncoded( + Res.readBytes(it.imageUrl) + ).toComposeImageBitmap() } jobs.add(j) } diff --git a/examples/jetsnack/web/src/wasmJsMain/kotlin/Main.kt b/examples/jetsnack/web/src/wasmJsMain/kotlin/Main.kt index 38faff4ffdf..cc02636306d 100644 --- a/examples/jetsnack/web/src/wasmJsMain/kotlin/Main.kt +++ b/examples/jetsnack/web/src/wasmJsMain/kotlin/Main.kt @@ -13,6 +13,8 @@ import com.example.jetsnack.ui.components.loadImage import com.example.jetsnack.ui.components.toByteArray import com.example.jetsnack.ui.theme.Karla import com.example.jetsnack.ui.theme.Montserrat +import kotlinx.coroutines.joinAll +import kotlinx.coroutines.launch import org.jetbrains.compose.resources.* @OptIn(ExperimentalComposeUiApi::class, ExperimentalResourceApi::class) @@ -31,9 +33,16 @@ fun main() { } LaunchedEffect(Unit) { - loadMontserratFont() - loadKarlaFont() - prepareImagesCache() + val j1 = launch { + loadMontserratFont() + } + val j2 = launch { + loadKarlaFont() + } + val j3 = launch { + prepareImagesCache() + } + joinAll(j1, j2, j3) loading = false } }