@momo-kits/native-kits 0.162.1-gif.10-debug → 0.162.1-gif.11-debug
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/compose/build.gradle.kts +2 -2
- package/compose/build.gradle.kts.backup +1 -1
- package/compose/compose.podspec +1 -1
- package/compose/src/androidMain/kotlin/vn/momo/kits/utils/SketchGifBridge.android.kt +161 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +5 -28
- package/compose/src/commonMain/kotlin/vn/momo/kits/components/SketchGifImage.kt +81 -0
- package/compose/src/commonMain/kotlin/vn/momo/kits/utils/SketchGifBridge.kt +116 -0
- package/compose/src/iosMain/kotlin/vn/momo/kits/utils/SketchGifBridge.ios.kt +23 -0
- package/gradle/libs.versions.toml +1 -1
- package/gradle.properties +1 -1
- package/package.json +1 -1
package/compose/build.gradle.kts
CHANGED
|
@@ -40,7 +40,7 @@ kotlin {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
cocoapods {
|
|
43
|
-
version = "0.162.1-gif.
|
|
43
|
+
version = "0.162.1-gif.11-debug"
|
|
44
44
|
summary = "IOS Shared module"
|
|
45
45
|
homepage = "https://momo.vn"
|
|
46
46
|
ios.deploymentTarget = "15.0"
|
|
@@ -77,7 +77,7 @@ kotlin {
|
|
|
77
77
|
implementation(libs.kotlinx.datetime)
|
|
78
78
|
api(libs.native.max.api)
|
|
79
79
|
implementation(libs.compottie)
|
|
80
|
-
implementation(libs.sketch.
|
|
80
|
+
implementation(libs.sketch.core)
|
|
81
81
|
implementation(libs.sketch.http.core)
|
|
82
82
|
implementation(libs.sketch.animated.gif)
|
|
83
83
|
}
|
|
@@ -77,7 +77,7 @@ kotlin {
|
|
|
77
77
|
implementation(libs.kotlinx.datetime)
|
|
78
78
|
api(libs.native.max.api)
|
|
79
79
|
implementation(libs.compottie)
|
|
80
|
-
implementation(libs.sketch.
|
|
80
|
+
implementation(libs.sketch.core)
|
|
81
81
|
implementation(libs.sketch.http.core)
|
|
82
82
|
implementation(libs.sketch.animated.gif)
|
|
83
83
|
}
|
package/compose/compose.podspec
CHANGED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
package vn.momo.kits.utils
|
|
2
|
+
|
|
3
|
+
import android.graphics.drawable.Animatable
|
|
4
|
+
import android.graphics.drawable.BitmapDrawable
|
|
5
|
+
import android.graphics.drawable.ColorDrawable
|
|
6
|
+
import android.graphics.drawable.Drawable
|
|
7
|
+
import android.os.Handler
|
|
8
|
+
import android.os.Looper
|
|
9
|
+
import androidx.compose.runtime.Composable
|
|
10
|
+
import androidx.compose.runtime.RememberObserver
|
|
11
|
+
import androidx.compose.runtime.getValue
|
|
12
|
+
import androidx.compose.runtime.mutableIntStateOf
|
|
13
|
+
import androidx.compose.runtime.mutableStateOf
|
|
14
|
+
import androidx.compose.runtime.setValue
|
|
15
|
+
import androidx.compose.ui.geometry.Size
|
|
16
|
+
import androidx.compose.ui.graphics.Color
|
|
17
|
+
import androidx.compose.ui.graphics.ColorFilter
|
|
18
|
+
import androidx.compose.ui.graphics.ImageBitmap
|
|
19
|
+
import androidx.compose.ui.graphics.asAndroidColorFilter
|
|
20
|
+
import androidx.compose.ui.graphics.asImageBitmap
|
|
21
|
+
import androidx.compose.ui.graphics.drawscope.DrawScope
|
|
22
|
+
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
|
23
|
+
import androidx.compose.ui.graphics.nativeCanvas
|
|
24
|
+
import androidx.compose.ui.graphics.painter.BitmapPainter
|
|
25
|
+
import androidx.compose.ui.graphics.painter.ColorPainter
|
|
26
|
+
import androidx.compose.ui.graphics.painter.Painter
|
|
27
|
+
import androidx.compose.ui.graphics.withSave
|
|
28
|
+
import androidx.compose.ui.platform.LocalContext
|
|
29
|
+
import androidx.compose.ui.unit.LayoutDirection
|
|
30
|
+
import com.github.panpf.sketch.Bitmap
|
|
31
|
+
import com.github.panpf.sketch.DrawableImage
|
|
32
|
+
import com.github.panpf.sketch.Image
|
|
33
|
+
import com.github.panpf.sketch.PlatformContext
|
|
34
|
+
import kotlin.math.roundToInt
|
|
35
|
+
|
|
36
|
+
@Composable
|
|
37
|
+
internal actual fun rememberSketchPlatformContext(): PlatformContext {
|
|
38
|
+
return LocalContext.current.applicationContext
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
internal actual fun Bitmap.toComposeImageBitmap(): ImageBitmap {
|
|
42
|
+
return asImageBitmap()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
internal actual fun Image?.toSketchGifPainter(): Painter? {
|
|
46
|
+
return when (this) {
|
|
47
|
+
is DrawableImage -> drawable.toPainter()
|
|
48
|
+
else -> null
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private fun Drawable.toPainter(): Painter {
|
|
53
|
+
return when (this) {
|
|
54
|
+
is Animatable -> AnimatableDrawablePainter(mutate())
|
|
55
|
+
is BitmapDrawable -> BitmapPainter(bitmap.asImageBitmap())
|
|
56
|
+
is ColorDrawable -> ColorPainter(Color(color))
|
|
57
|
+
else -> DrawablePainter(mutate())
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private open class DrawablePainter(
|
|
62
|
+
protected val drawable: Drawable,
|
|
63
|
+
) : Painter(), RememberObserver {
|
|
64
|
+
private var drawTick by mutableIntStateOf(0)
|
|
65
|
+
private var drawableSize by mutableStateOf(drawable.intrinsicSize)
|
|
66
|
+
|
|
67
|
+
private val callback = object : Drawable.Callback {
|
|
68
|
+
override fun invalidateDrawable(who: Drawable) {
|
|
69
|
+
drawTick++
|
|
70
|
+
drawableSize = who.intrinsicSize
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
|
|
74
|
+
mainHandler.postAtTime(what, `when`)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
override fun unscheduleDrawable(who: Drawable, what: Runnable) {
|
|
78
|
+
mainHandler.removeCallbacks(what)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override val intrinsicSize: Size
|
|
83
|
+
get() = drawableSize
|
|
84
|
+
|
|
85
|
+
init {
|
|
86
|
+
if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) {
|
|
87
|
+
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
override fun DrawScope.onDraw() {
|
|
92
|
+
drawTick
|
|
93
|
+
drawIntoCanvas { canvas ->
|
|
94
|
+
canvas.withSave {
|
|
95
|
+
drawable.setBounds(0, 0, size.width.roundToInt(), size.height.roundToInt())
|
|
96
|
+
drawable.draw(canvas.nativeCanvas)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
override fun applyAlpha(alpha: Float): Boolean {
|
|
102
|
+
drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255)
|
|
103
|
+
return true
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
|
|
107
|
+
drawable.colorFilter = colorFilter?.asAndroidColorFilter()
|
|
108
|
+
return true
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean {
|
|
112
|
+
return if (android.os.Build.VERSION.SDK_INT >= 23) {
|
|
113
|
+
drawable.setLayoutDirection(
|
|
114
|
+
if (layoutDirection == LayoutDirection.Rtl) {
|
|
115
|
+
android.view.View.LAYOUT_DIRECTION_RTL
|
|
116
|
+
} else {
|
|
117
|
+
android.view.View.LAYOUT_DIRECTION_LTR
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
} else {
|
|
121
|
+
false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override fun onRemembered() {
|
|
126
|
+
drawable.callback = callback
|
|
127
|
+
drawable.setVisible(true, true)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
override fun onAbandoned() = onForgotten()
|
|
131
|
+
|
|
132
|
+
override fun onForgotten() {
|
|
133
|
+
drawable.setVisible(false, false)
|
|
134
|
+
drawable.callback = null
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
private class AnimatableDrawablePainter(
|
|
139
|
+
drawable: Drawable,
|
|
140
|
+
) : DrawablePainter(drawable) {
|
|
141
|
+
private val animatable = drawable as Animatable
|
|
142
|
+
|
|
143
|
+
override fun onRemembered() {
|
|
144
|
+
super.onRemembered()
|
|
145
|
+
animatable.start()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
override fun onForgotten() {
|
|
149
|
+
animatable.stop()
|
|
150
|
+
super.onForgotten()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private val Drawable.intrinsicSize: Size
|
|
155
|
+
get() = if (intrinsicWidth >= 0 && intrinsicHeight >= 0) {
|
|
156
|
+
Size(intrinsicWidth.toFloat(), intrinsicHeight.toFloat())
|
|
157
|
+
} else {
|
|
158
|
+
Size.Unspecified
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
@@ -17,19 +17,11 @@ import androidx.compose.ui.semantics.contentDescription
|
|
|
17
17
|
import androidx.compose.ui.semantics.semantics
|
|
18
18
|
import androidx.compose.ui.platform.LocalDensity
|
|
19
19
|
import androidx.compose.ui.unit.dp
|
|
20
|
-
import com.github.panpf.sketch.AsyncImage as SketchAsyncImage
|
|
21
|
-
import com.github.panpf.sketch.LocalPlatformContext
|
|
22
|
-
import com.github.panpf.sketch.PainterState
|
|
23
|
-
import com.github.panpf.sketch.fetch.HttpUriFetcher
|
|
24
|
-
import com.github.panpf.sketch.request.ImageRequest as SketchImageRequest
|
|
25
|
-
import com.github.panpf.sketch.rememberAsyncImageState
|
|
26
20
|
import coil3.compose.AsyncImage as CoilAsyncImage
|
|
27
21
|
import vn.momo.kits.application.IsShowBaseLineDebug
|
|
28
22
|
import vn.momo.kits.const.AppTheme
|
|
29
23
|
import vn.momo.kits.const.Colors
|
|
30
24
|
import vn.momo.kits.modifier.conditional
|
|
31
|
-
import vn.momo.kits.utils.GifImageHttpClient
|
|
32
|
-
import vn.momo.kits.utils.KitsKtorHttpStack
|
|
33
25
|
|
|
34
26
|
data class Options(
|
|
35
27
|
val alignment: Alignment = Alignment.TopStart,
|
|
@@ -124,7 +116,6 @@ fun Image(
|
|
|
124
116
|
}
|
|
125
117
|
.semantics { contentDescription = "img|$source" },
|
|
126
118
|
) {
|
|
127
|
-
val platformContext = LocalPlatformContext.current
|
|
128
119
|
val urlToLoad = remember(source, maxWidth, maxHeight, density) {
|
|
129
120
|
when (source) {
|
|
130
121
|
is String -> {
|
|
@@ -141,34 +132,20 @@ fun Image(
|
|
|
141
132
|
}
|
|
142
133
|
|
|
143
134
|
if (isGifUrl(urlToLoad)) {
|
|
144
|
-
val
|
|
145
|
-
val request = remember(platformContext, urlToLoad) {
|
|
146
|
-
SketchImageRequest(platformContext, urlToLoad) {
|
|
147
|
-
components {
|
|
148
|
-
add(HttpUriFetcher.Factory(KitsKtorHttpStack(GifImageHttpClient.client)))
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
}
|
|
135
|
+
val imageState = remember(urlToLoad) { mutableStateOf(ImageState.Loading) }
|
|
152
136
|
|
|
153
|
-
|
|
137
|
+
SketchGifImage(
|
|
154
138
|
modifier = Modifier.matchParentSize(),
|
|
155
|
-
|
|
156
|
-
state = asyncImageState,
|
|
157
|
-
contentDescription = null,
|
|
139
|
+
url = urlToLoad,
|
|
158
140
|
contentScale = imageOptions.contentScale,
|
|
159
141
|
alignment = imageOptions.alignment,
|
|
160
142
|
colorFilter = imageOptions.colorFilter,
|
|
161
143
|
alpha = imageOptions.alpha,
|
|
144
|
+
onStateChanged = { imageState.value = it },
|
|
162
145
|
)
|
|
163
146
|
|
|
164
|
-
val imageState = when (asyncImageState.painterState) {
|
|
165
|
-
is PainterState.Success -> ImageState.Success
|
|
166
|
-
is PainterState.Error -> ImageState.Error
|
|
167
|
-
else -> ImageState.Loading
|
|
168
|
-
}
|
|
169
|
-
|
|
170
147
|
ImageStateContent(
|
|
171
|
-
imageState = imageState,
|
|
148
|
+
imageState = imageState.value,
|
|
172
149
|
loading = loading,
|
|
173
150
|
onLoadingChanged = currentOnLoadingChanged.value
|
|
174
151
|
)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
package vn.momo.kits.components
|
|
2
|
+
|
|
3
|
+
import androidx.compose.foundation.Image
|
|
4
|
+
import androidx.compose.runtime.Composable
|
|
5
|
+
import androidx.compose.runtime.DisposableEffect
|
|
6
|
+
import androidx.compose.runtime.LaunchedEffect
|
|
7
|
+
import androidx.compose.runtime.getValue
|
|
8
|
+
import androidx.compose.runtime.mutableStateOf
|
|
9
|
+
import androidx.compose.runtime.remember
|
|
10
|
+
import androidx.compose.runtime.setValue
|
|
11
|
+
import androidx.compose.ui.Alignment
|
|
12
|
+
import androidx.compose.ui.Modifier
|
|
13
|
+
import androidx.compose.ui.graphics.ColorFilter
|
|
14
|
+
import androidx.compose.ui.graphics.DefaultAlpha
|
|
15
|
+
import androidx.compose.ui.graphics.painter.Painter
|
|
16
|
+
import androidx.compose.ui.layout.ContentScale
|
|
17
|
+
import com.github.panpf.sketch.Sketch
|
|
18
|
+
import com.github.panpf.sketch.decode.supportGif
|
|
19
|
+
import com.github.panpf.sketch.fetch.HttpUriFetcher
|
|
20
|
+
import com.github.panpf.sketch.request.ImageRequest
|
|
21
|
+
import com.github.panpf.sketch.request.ImageResult
|
|
22
|
+
import vn.momo.kits.utils.GifImageHttpClient
|
|
23
|
+
import vn.momo.kits.utils.KitsKtorHttpStack
|
|
24
|
+
import vn.momo.kits.utils.toSketchGifPainter
|
|
25
|
+
import vn.momo.kits.utils.rememberSketchPlatformContext
|
|
26
|
+
|
|
27
|
+
@Composable
|
|
28
|
+
internal fun SketchGifImage(
|
|
29
|
+
url: String,
|
|
30
|
+
modifier: Modifier = Modifier,
|
|
31
|
+
contentScale: ContentScale = ContentScale.Crop,
|
|
32
|
+
alignment: Alignment = Alignment.Center,
|
|
33
|
+
colorFilter: ColorFilter? = null,
|
|
34
|
+
alpha: Float = DefaultAlpha,
|
|
35
|
+
onStateChanged: (ImageState) -> Unit,
|
|
36
|
+
) {
|
|
37
|
+
val platformContext = rememberSketchPlatformContext()
|
|
38
|
+
val sketch = remember(platformContext) { Sketch(platformContext) }
|
|
39
|
+
var painter by remember(url) { mutableStateOf<Painter?>(null) }
|
|
40
|
+
|
|
41
|
+
DisposableEffect(sketch) {
|
|
42
|
+
onDispose { sketch.shutdown() }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
LaunchedEffect(sketch, platformContext, url) {
|
|
46
|
+
painter = null
|
|
47
|
+
onStateChanged(ImageState.Loading)
|
|
48
|
+
|
|
49
|
+
val request = ImageRequest(platformContext, url) {
|
|
50
|
+
components {
|
|
51
|
+
add(HttpUriFetcher.Factory(KitsKtorHttpStack(GifImageHttpClient.client)))
|
|
52
|
+
supportGif()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
when (val result = sketch.execute(request)) {
|
|
57
|
+
is ImageResult.Success -> {
|
|
58
|
+
val gifPainter = result.image.toSketchGifPainter()
|
|
59
|
+
if (gifPainter != null) {
|
|
60
|
+
painter = gifPainter
|
|
61
|
+
onStateChanged(ImageState.Success)
|
|
62
|
+
} else {
|
|
63
|
+
onStateChanged(ImageState.Error)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
is ImageResult.Error -> onStateChanged(ImageState.Error)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
val currentPainter = painter ?: return
|
|
71
|
+
val rememberedPainter = remember(currentPainter) { currentPainter }
|
|
72
|
+
Image(
|
|
73
|
+
modifier = modifier,
|
|
74
|
+
painter = rememberedPainter,
|
|
75
|
+
contentDescription = null,
|
|
76
|
+
contentScale = contentScale,
|
|
77
|
+
alignment = alignment,
|
|
78
|
+
colorFilter = colorFilter,
|
|
79
|
+
alpha = alpha,
|
|
80
|
+
)
|
|
81
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
package vn.momo.kits.utils
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.compose.runtime.RememberObserver
|
|
5
|
+
import androidx.compose.runtime.getValue
|
|
6
|
+
import androidx.compose.runtime.mutableStateOf
|
|
7
|
+
import androidx.compose.runtime.setValue
|
|
8
|
+
import androidx.compose.ui.geometry.Size
|
|
9
|
+
import androidx.compose.ui.graphics.ColorFilter
|
|
10
|
+
import androidx.compose.ui.graphics.DefaultAlpha
|
|
11
|
+
import androidx.compose.ui.graphics.ImageBitmap
|
|
12
|
+
import androidx.compose.ui.graphics.drawscope.DrawScope
|
|
13
|
+
import androidx.compose.ui.graphics.painter.Painter
|
|
14
|
+
import androidx.compose.ui.unit.IntSize
|
|
15
|
+
import com.github.panpf.sketch.AnimatedImage
|
|
16
|
+
import com.github.panpf.sketch.Bitmap
|
|
17
|
+
import com.github.panpf.sketch.Image
|
|
18
|
+
import com.github.panpf.sketch.PlatformContext
|
|
19
|
+
import kotlinx.coroutines.CoroutineScope
|
|
20
|
+
import kotlinx.coroutines.Dispatchers
|
|
21
|
+
import kotlinx.coroutines.Job
|
|
22
|
+
import kotlinx.coroutines.cancel
|
|
23
|
+
import kotlinx.coroutines.delay
|
|
24
|
+
import kotlinx.coroutines.isActive
|
|
25
|
+
import kotlinx.coroutines.launch
|
|
26
|
+
import kotlinx.coroutines.withContext
|
|
27
|
+
import kotlin.math.ceil
|
|
28
|
+
|
|
29
|
+
@Composable
|
|
30
|
+
internal expect fun rememberSketchPlatformContext(): PlatformContext
|
|
31
|
+
|
|
32
|
+
internal expect fun Bitmap.toComposeImageBitmap(): ImageBitmap
|
|
33
|
+
|
|
34
|
+
internal expect fun Image?.toSketchGifPainter(): Painter?
|
|
35
|
+
|
|
36
|
+
internal class SketchAnimatedImagePainter(
|
|
37
|
+
private val animatedImage: AnimatedImage,
|
|
38
|
+
) : Painter(), RememberObserver {
|
|
39
|
+
private var alpha: Float = DefaultAlpha
|
|
40
|
+
private var colorFilter: ColorFilter? = null
|
|
41
|
+
private var frame by mutableStateOf<ImageBitmap?>(null)
|
|
42
|
+
private var scope: CoroutineScope? = null
|
|
43
|
+
private var playJob: Job? = null
|
|
44
|
+
|
|
45
|
+
override val intrinsicSize: Size = Size(
|
|
46
|
+
width = animatedImage.width.toFloat(),
|
|
47
|
+
height = animatedImage.height.toFloat(),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
override fun DrawScope.onDraw() {
|
|
51
|
+
val currentFrame = frame ?: return
|
|
52
|
+
drawImage(
|
|
53
|
+
image = currentFrame,
|
|
54
|
+
dstSize = IntSize(
|
|
55
|
+
width = ceil(size.width).toInt(),
|
|
56
|
+
height = ceil(size.height).toInt(),
|
|
57
|
+
),
|
|
58
|
+
alpha = alpha,
|
|
59
|
+
colorFilter = colorFilter,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
override fun applyAlpha(alpha: Float): Boolean {
|
|
64
|
+
this.alpha = alpha
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
|
|
69
|
+
this.colorFilter = colorFilter
|
|
70
|
+
return true
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
override fun onRemembered() {
|
|
74
|
+
if (scope != null) return
|
|
75
|
+
val newScope = CoroutineScope(Dispatchers.Main)
|
|
76
|
+
scope = newScope
|
|
77
|
+
playJob = newScope.launch {
|
|
78
|
+
play()
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
override fun onAbandoned() = onForgotten()
|
|
83
|
+
|
|
84
|
+
override fun onForgotten() {
|
|
85
|
+
playJob?.cancel()
|
|
86
|
+
playJob = null
|
|
87
|
+
scope?.cancel()
|
|
88
|
+
scope = null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private suspend fun play() {
|
|
92
|
+
val frameCount = animatedImage.frameCount
|
|
93
|
+
if (frameCount <= 0) return
|
|
94
|
+
|
|
95
|
+
var loopIndex = 0
|
|
96
|
+
while (scope?.isActive == true && shouldContinue(loopIndex)) {
|
|
97
|
+
for (frameIndex in 0 until frameCount) {
|
|
98
|
+
if (scope?.isActive != true) return
|
|
99
|
+
frame = readFrame(frameIndex)
|
|
100
|
+
delay(animatedImage.frameDurations.getOrNull(frameIndex)?.toLong()?.takeIf { it > 0 } ?: 100L)
|
|
101
|
+
}
|
|
102
|
+
loopIndex++
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private fun shouldContinue(loopIndex: Int): Boolean {
|
|
107
|
+
val repeatCount = animatedImage.repeatCount
|
|
108
|
+
return repeatCount <= 0 || loopIndex <= repeatCount
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private suspend fun readFrame(frameIndex: Int): ImageBitmap = withContext(Dispatchers.Default) {
|
|
112
|
+
animatedImage.createFrameBitmap().also {
|
|
113
|
+
animatedImage.readFrame(it, frameIndex)
|
|
114
|
+
}.toComposeImageBitmap()
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
package vn.momo.kits.utils
|
|
2
|
+
|
|
3
|
+
import androidx.compose.runtime.Composable
|
|
4
|
+
import androidx.compose.ui.graphics.ImageBitmap
|
|
5
|
+
import androidx.compose.ui.graphics.asComposeImageBitmap
|
|
6
|
+
import androidx.compose.ui.graphics.painter.Painter
|
|
7
|
+
import com.github.panpf.sketch.AnimatedImage
|
|
8
|
+
import com.github.panpf.sketch.Bitmap
|
|
9
|
+
import com.github.panpf.sketch.Image
|
|
10
|
+
import com.github.panpf.sketch.PlatformContext
|
|
11
|
+
|
|
12
|
+
@Composable
|
|
13
|
+
internal actual fun rememberSketchPlatformContext(): PlatformContext {
|
|
14
|
+
return PlatformContext.INSTANCE
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
internal actual fun Bitmap.toComposeImageBitmap(): ImageBitmap {
|
|
18
|
+
return asComposeImageBitmap()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
internal actual fun Image?.toSketchGifPainter(): Painter? {
|
|
22
|
+
return if (this is AnimatedImage) SketchAnimatedImagePainter(this) else null
|
|
23
|
+
}
|
|
@@ -54,7 +54,7 @@ androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "a
|
|
|
54
54
|
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
|
|
55
55
|
material = { module = "com.google.android.material:material", version.ref = "material" }
|
|
56
56
|
|
|
57
|
-
sketch-
|
|
57
|
+
sketch-core = { module = "io.github.panpf.sketch4:sketch-core", version.ref = "sketch" }
|
|
58
58
|
sketch-http-core = { module = "io.github.panpf.sketch4:sketch-http-core", version.ref = "sketch" }
|
|
59
59
|
sketch-animated-gif = { module = "io.github.panpf.sketch4:sketch-animated-gif", version.ref = "sketch" }
|
|
60
60
|
[plugins]
|
package/gradle.properties
CHANGED