Browse Source

Update images in Jetsnack example: host them locally and use Resources library (#5131)

We used to rely on an external service to fetch the images. But they are
not available anymore
pull/5135/head
Oleksandr Karpovich 2 months ago committed by GitHub
parent
commit
4c156252bd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      examples/jetsnack/android/src/main/AndroidManifest.xml
  2. 1
      examples/jetsnack/common/build.gradle.kts
  3. 76
      examples/jetsnack/common/src/androidMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt
  4. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/almonds.jpg
  5. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/apple_chips.jpg
  6. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/apple_juice.jpg
  7. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/apple_pie.jpg
  8. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/apple_sauce.jpg
  9. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/apples.jpg
  10. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/cheese.jpg
  11. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/chips.jpg
  12. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/cupcake.jpg
  13. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/desserts.jpg
  14. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/donut.jpg
  15. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/eclair.jpg
  16. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/froyo.jpg
  17. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/fruit.jpg
  18. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/gingerbread.jpg
  19. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/gluten_free.jpg
  20. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/grapes.jpg
  21. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/honeycomb.jpg
  22. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/ice_cream_sandwich.jpg
  23. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/jelly_bean.jpg
  24. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/kitkat.jpg
  25. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/kiwi.jpg
  26. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/lollipop.jpg
  27. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/mango.jpg
  28. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/marshmallow.jpg
  29. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/nougat.jpg
  30. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/nuts.jpg
  31. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/oreo.jpg
  32. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/organic.jpg
  33. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/paleo.jpg
  34. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/pie.jpg
  35. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/placeholder.jpg
  36. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/popcorn.jpg
  37. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/pretzels.jpg
  38. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/smoothies.jpg
  39. BIN
      examples/jetsnack/common/src/commonMain/composeResources/files/vegan.jpg
  40. 20
      examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Search.kt
  41. 57
      examples/jetsnack/common/src/commonMain/kotlin/com/example/jetsnack/model/Snack.kt
  42. 6
      examples/jetsnack/common/src/desktopMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt
  43. 35
      examples/jetsnack/common/src/iosMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.ios.kt
  44. 16
      examples/jetsnack/common/src/wasmJsMain/kotlin/com/example/jetsnack/ui/components/SnackAsyncImage.kt
  45. 15
      examples/jetsnack/web/src/wasmJsMain/kotlin/Main.kt

2
examples/jetsnack/android/src/main/AndroidManifest.xml

@ -7,7 +7,7 @@
android:allowBackup="false"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<activity android:name=".MainActivity" android:exported="true">
<activity android:name="com.example.android.MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>

1
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)
}
}

76
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<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)
}

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/almonds.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/apple_chips.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/apple_juice.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/apple_pie.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/apple_sauce.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/apples.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/cheese.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/chips.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 248 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/cupcake.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/desserts.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/donut.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/eclair.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/froyo.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/fruit.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/gingerbread.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/gluten_free.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/grapes.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/honeycomb.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/ice_cream_sandwich.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/jelly_bean.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/kitkat.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/kiwi.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/lollipop.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/mango.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/marshmallow.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/nougat.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/nuts.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/oreo.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/organic.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/paleo.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/pie.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/placeholder.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/popcorn.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/pretzels.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/smoothies.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
examples/jetsnack/common/src/commonMain/composeResources/files/vegan.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

20
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"
)
)
)

57
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
)
)

6
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<String, ImageBitmap>()
@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
}

35
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<String, ImageBitmap>()
@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()
}
}

16
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<String, ImageBitmap>()
@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<Job>()
// 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)
}

15
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
}
}

Loading…
Cancel
Save