@momo-kits/native-kits 0.162.1-gif.11-debug → 0.162.1-gif.13-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.
Files changed (83) hide show
  1. package/build.gradle.kts +10 -0
  2. package/compose/build.gradle.kts +3 -2
  3. package/compose/build.gradle.kts.backup +2 -1
  4. package/compose/compose.podspec +1 -1
  5. package/compose/src/commonMain/kotlin/vn/momo/kits/components/Image.kt +27 -5
  6. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/{GifImageHttpClient.kt → KitsSketchHttpStack.kt} +3 -19
  7. package/gradle/libs.versions.toml +1 -0
  8. package/gradle.properties +1 -1
  9. package/package.json +1 -1
  10. package/publish.sh +4 -0
  11. package/settings.gradle.kts +2 -0
  12. package/third_party/sketch-compose-core-patched/META-INF/MANIFEST.MF +2 -0
  13. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/LocalPlatformContext.android.kt +27 -0
  14. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/PainterImage.android.kt +36 -0
  15. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/painter/DrawableAnimatablePainter.kt +83 -0
  16. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/painter/DrawablePainter.kt +238 -0
  17. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/painter/IconAnimatablePainter.android.kt +2076 -0
  18. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/painter/IconPainter.android.kt +2076 -0
  19. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/request/ImageOptions.composeAndroid.kt +33 -0
  20. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/request/ImageRequest.composeAndroid.kt +33 -0
  21. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/ColorDrawableStateImage.composeAndroid.kt +70 -0
  22. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/ColorPainterStateImage.android.kt +23 -0
  23. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/CurrentStateImage.composeAndroid.kt +49 -0
  24. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/DrawableStateImage.composeAndroid.kt +40 -0
  25. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/EquitableDrawable.composeAndroid.kt +39 -0
  26. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/IconAnimatableDrawableStateImage.composeAndroid.kt +642 -0
  27. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/IconAnimatablePainterStateImage.android.kt +2077 -0
  28. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/IconDrawableStateImage.composeAndroid.kt +642 -0
  29. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/state/IconPainterStateImage.android.kt +2077 -0
  30. package/third_party/sketch-compose-core-patched/androidMain/com/github/panpf/sketch/util/compose_core_utils.android.kt +30 -0
  31. package/third_party/sketch-compose-core-patched/build.gradle.kts +74 -0
  32. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/AsyncImage.kt +292 -0
  33. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/AsyncImagePainter.kt +273 -0
  34. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/AsyncImageState.kt +358 -0
  35. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/LocalPlatformContext.common.kt +27 -0
  36. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/PainterImage.kt +86 -0
  37. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/PainterState.kt +51 -0
  38. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/SubcomposeAsyncImage.kt +397 -0
  39. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/internal/AsyncImageContent.kt +77 -0
  40. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/internal/utils.kt +45 -0
  41. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/AnimatablePainter.kt +72 -0
  42. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/AnimatedImagePainter.kt +398 -0
  43. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/CrossfadePainter.kt +295 -0
  44. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/EquitableAnimatablePainter.kt +74 -0
  45. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/EquitablePainter.common.kt +91 -0
  46. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/IconAnimatablePainter.common.kt +268 -0
  47. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/IconPainter.common.kt +341 -0
  48. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/ImageBitmapPainter.kt +98 -0
  49. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/PainterWrapper.kt +68 -0
  50. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/ResizeAnimatablePainter.kt +99 -0
  51. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/ResizePainter.kt +204 -0
  52. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/SketchPainter.kt +29 -0
  53. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/painter/painters.common.kt +79 -0
  54. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/request/ImageOptions.composeCommon.kt +95 -0
  55. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/request/ImageRequest.composeCommon.kt +135 -0
  56. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/request/internal/ComposeRequestDelegate.kt +44 -0
  57. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/request/internal/ComposeRequestManager.kt +42 -0
  58. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/resize/AsyncImageSizeResolver.kt +59 -0
  59. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/resize/ComposeResizeOnDrawHelper.kt +50 -0
  60. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/state/ColorPainterStateImage.kt +72 -0
  61. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/state/ConditionStateImage.compose.kt +64 -0
  62. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/state/CurrentStateImage.compose.kt +38 -0
  63. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/state/IconAnimatablePainterStateImage.common.kt +261 -0
  64. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/state/IconPainterStateImage.common.kt +261 -0
  65. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/state/PainterStateImage.kt +55 -0
  66. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/target/AsyncImageTarget.kt +144 -0
  67. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/target/ComposeTarget.common.kt +88 -0
  68. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/target/GenericComposeTarget.kt +124 -0
  69. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/target/internal/AsyncImageListener.kt +84 -0
  70. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/transition/ComposeCrossfadeTransition.kt +180 -0
  71. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/transition/TransitionComposeTarget.kt +32 -0
  72. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/transition/TransitionPainter.kt +23 -0
  73. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/util/RememberedCounter.kt +51 -0
  74. package/third_party/sketch-compose-core-patched/commonMain/com/github/panpf/sketch/util/compose_core_utils.common.kt +347 -0
  75. package/third_party/sketch-compose-core-patched/nonAndroidMain/com/github/panpf/sketch/LocalPlatformContext.nonAndroid.kt +28 -0
  76. package/third_party/sketch-compose-core-patched/nonAndroidMain/com/github/panpf/sketch/PainterImage.nonAndroid.kt +35 -0
  77. package/third_party/sketch-compose-core-patched/nonAndroidMain/com/github/panpf/sketch/request/ImageOptions.composeNonAndroid.kt +35 -0
  78. package/third_party/sketch-compose-core-patched/nonAndroidMain/com/github/panpf/sketch/request/ImageRequest.composeNonAndroid.kt +35 -0
  79. package/third_party/sketch-compose-core-patched/nonAndroidMain/com/github/panpf/sketch/util/compose_core_utils.nonAndroid.kt +28 -0
  80. package/compose/src/androidMain/kotlin/vn/momo/kits/utils/SketchGifBridge.android.kt +0 -161
  81. package/compose/src/commonMain/kotlin/vn/momo/kits/components/SketchGifImage.kt +0 -81
  82. package/compose/src/commonMain/kotlin/vn/momo/kits/utils/SketchGifBridge.kt +0 -116
  83. package/compose/src/iosMain/kotlin/vn/momo/kits/utils/SketchGifBridge.ios.kt +0 -23
package/build.gradle.kts CHANGED
@@ -9,3 +9,13 @@ plugins {
9
9
  alias(libs.plugins.jetbrains.kotlin.serialization) apply false
10
10
  alias(libs.plugins.vanniktech.mavenPublish) apply false
11
11
  }
12
+
13
+ subprojects {
14
+ configurations.configureEach {
15
+ resolutionStrategy.dependencySubstitution {
16
+ substitute(module("io.github.panpf.sketch4:sketch-compose-core"))
17
+ .using(project(":sketch-compose-core"))
18
+ .because("Patch duplicate Kotlin/Native symbols in sketch-compose-core 4.4.0")
19
+ }
20
+ }
21
+ }
@@ -40,7 +40,7 @@ kotlin {
40
40
  }
41
41
 
42
42
  cocoapods {
43
- version = "0.162.1-gif.11-debug"
43
+ version = "0.162.1-gif.13-debug"
44
44
  summary = "IOS Shared module"
45
45
  homepage = "https://momo.vn"
46
46
  ios.deploymentTarget = "15.0"
@@ -77,7 +77,8 @@ kotlin {
77
77
  implementation(libs.kotlinx.datetime)
78
78
  api(libs.native.max.api)
79
79
  implementation(libs.compottie)
80
- implementation(libs.sketch.core)
80
+ implementation(libs.sketch.compose)
81
+ implementation(project(":sketch-compose-core"))
81
82
  implementation(libs.sketch.http.core)
82
83
  implementation(libs.sketch.animated.gif)
83
84
  }
@@ -77,7 +77,8 @@ kotlin {
77
77
  implementation(libs.kotlinx.datetime)
78
78
  api(libs.native.max.api)
79
79
  implementation(libs.compottie)
80
- implementation(libs.sketch.core)
80
+ implementation(libs.sketch.compose)
81
+ implementation(project(":sketch-compose-core"))
81
82
  implementation(libs.sketch.http.core)
82
83
  implementation(libs.sketch.animated.gif)
83
84
  }
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |spec|
2
2
  spec.name = 'compose'
3
- spec.version = '0.162.1-gif.10'
3
+ spec.version = '0.162.1-gif.12'
4
4
  spec.homepage = 'https://momo.vn'
5
5
  spec.source = { :http=> ''}
6
6
  spec.authors = ''
@@ -17,11 +17,18 @@ 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
20
26
  import coil3.compose.AsyncImage as CoilAsyncImage
21
27
  import vn.momo.kits.application.IsShowBaseLineDebug
22
28
  import vn.momo.kits.const.AppTheme
23
29
  import vn.momo.kits.const.Colors
24
30
  import vn.momo.kits.modifier.conditional
31
+ import vn.momo.kits.utils.KitsSketchHttpStack
25
32
 
26
33
  data class Options(
27
34
  val alignment: Alignment = Alignment.TopStart,
@@ -116,6 +123,7 @@ fun Image(
116
123
  }
117
124
  .semantics { contentDescription = "img|$source" },
118
125
  ) {
126
+ val platformContext = LocalPlatformContext.current
119
127
  val urlToLoad = remember(source, maxWidth, maxHeight, density) {
120
128
  when (source) {
121
129
  is String -> {
@@ -132,20 +140,34 @@ fun Image(
132
140
  }
133
141
 
134
142
  if (isGifUrl(urlToLoad)) {
135
- val imageState = remember(urlToLoad) { mutableStateOf(ImageState.Loading) }
143
+ val asyncImageState = rememberAsyncImageState()
144
+ val request = remember(platformContext, urlToLoad) {
145
+ SketchImageRequest(platformContext, urlToLoad) {
146
+ components {
147
+ add(HttpUriFetcher.Factory(KitsSketchHttpStack))
148
+ }
149
+ }
150
+ }
136
151
 
137
- SketchGifImage(
152
+ SketchAsyncImage(
138
153
  modifier = Modifier.matchParentSize(),
139
- url = urlToLoad,
154
+ request = request,
155
+ state = asyncImageState,
156
+ contentDescription = null,
140
157
  contentScale = imageOptions.contentScale,
141
158
  alignment = imageOptions.alignment,
142
159
  colorFilter = imageOptions.colorFilter,
143
160
  alpha = imageOptions.alpha,
144
- onStateChanged = { imageState.value = it },
145
161
  )
146
162
 
163
+ val imageState = when (asyncImageState.painterState) {
164
+ is PainterState.Success -> ImageState.Success
165
+ is PainterState.Error -> ImageState.Error
166
+ else -> ImageState.Loading
167
+ }
168
+
147
169
  ImageStateContent(
148
- imageState = imageState.value,
170
+ imageState = imageState,
149
171
  loading = loading,
150
172
  onLoadingChanged = currentOnLoadingChanged.value
151
173
  )
@@ -14,25 +14,9 @@ import io.ktor.utils.io.ByteReadChannel
14
14
  import io.ktor.utils.io.cancel
15
15
  import io.ktor.utils.io.readAvailable
16
16
 
17
- object GifImageHttpClient {
18
- private val defaultClient by lazy { HttpClient() }
19
- private var customClient: HttpClient? = null
17
+ internal object KitsSketchHttpStack : HttpStack {
18
+ private val client by lazy { HttpClient() }
20
19
 
21
- internal val client: HttpClient
22
- get() = customClient ?: defaultClient
23
-
24
- fun set(client: HttpClient) {
25
- customClient = client
26
- }
27
-
28
- fun clear() {
29
- customClient = null
30
- }
31
- }
32
-
33
- internal class KitsKtorHttpStack(
34
- private val client: HttpClient,
35
- ) : HttpStack {
36
20
  override suspend fun <T> request(
37
21
  url: String,
38
22
  httpHeaders: HttpHeaders?,
@@ -65,7 +49,7 @@ internal class KitsKtorHttpStack(
65
49
  return request(url, httpHeaders, extras) { it }
66
50
  }
67
51
 
68
- override fun toString(): String = "KitsKtorHttpStack"
52
+ override fun toString(): String = "KitsSketchHttpStack"
69
53
 
70
54
  private class Response(
71
55
  private val response: HttpResponse,
@@ -54,6 +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-compose = { module = "io.github.panpf.sketch4:sketch-compose", version.ref = "sketch" }
57
58
  sketch-core = { module = "io.github.panpf.sketch4:sketch-core", version.ref = "sketch" }
58
59
  sketch-http-core = { module = "io.github.panpf.sketch4:sketch-http-core", version.ref = "sketch" }
59
60
  sketch-animated-gif = { module = "io.github.panpf.sketch4:sketch-animated-gif", version.ref = "sketch" }
package/gradle.properties CHANGED
@@ -18,7 +18,7 @@ kotlin.apple.xcodeCompatibility.nowarn=true
18
18
  name="ComposeKits"
19
19
  group=vn.momo.kits
20
20
  artifact.id=kits
21
- version=0.162.1-gif.11
21
+ version=0.162.1-gif.13
22
22
 
23
23
  repo=GitLab
24
24
  url=https://gitlab.mservice.com.vn/api/v4/projects/5400/packages/maven
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@momo-kits/native-kits",
3
- "version": "0.162.1-gif.11-debug",
3
+ "version": "0.162.1-gif.13-debug",
4
4
  "private": false,
5
5
  "dependencies": {},
6
6
  "devDependencies": {},
package/publish.sh CHANGED
@@ -241,6 +241,10 @@ phase_publish_maven() {
241
241
  VERSION=$(node -p "require('./package.json').version")
242
242
  export VERSION="$VERSION"
243
243
 
244
+ echo "📦 Publishing patched sketch-compose-core to Maven..."
245
+ ./gradlew :sketch-compose-core:publishAllPublicationsToGitLabPackagesRepository
246
+ echo "✅ patched sketch-compose-core Maven publishing completed successfully!"
247
+
244
248
  echo "📦 Publishing KMP artifacts to Maven (version ${VERSION})..."
245
249
 
246
250
  ./gradlew :compose:publishAllPublicationsToGitLabPackagesRepository
@@ -59,6 +59,8 @@ dependencyResolutionManagement {
59
59
  }
60
60
 
61
61
  include(":compose")
62
+ include(":sketch-compose-core")
63
+ project(":sketch-compose-core").projectDir = file("third_party/sketch-compose-core-patched")
62
64
 
63
65
  include(":sample:androidApp")
64
66
  include(":sample:shared")
@@ -0,0 +1,2 @@
1
+ Manifest-Version: 1.0
2
+
@@ -0,0 +1,27 @@
1
+ /*
2
+ * Copyright (C) 2024 panpf <panpfpanpf@outlook.com>
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.github.panpf.sketch
18
+
19
+ import androidx.compose.runtime.ProvidableCompositionLocal
20
+ import androidx.compose.ui.platform.LocalContext
21
+
22
+ /**
23
+ * Platform context, used to provide platform-specific context information
24
+ *
25
+ * @see com.github.panpf.sketch.compose.core.android.test.LocalPlatformContextAndroidTest.testLocalPlatformContext
26
+ */
27
+ actual val LocalPlatformContext: ProvidableCompositionLocal<PlatformContext> = LocalContext
@@ -0,0 +1,36 @@
1
+ /*
2
+ * Copyright (C) 2024 panpf <panpfpanpf@outlook.com>
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.github.panpf.sketch
18
+
19
+ import androidx.compose.ui.graphics.FilterQuality
20
+ import androidx.compose.ui.graphics.asImageBitmap
21
+ import androidx.compose.ui.graphics.painter.Painter
22
+ import com.github.panpf.sketch.painter.AnimatedImagePainter
23
+ import com.github.panpf.sketch.painter.asPainter
24
+
25
+ /**
26
+ * Convert the Image to a Painter
27
+ *
28
+ * @see com.github.panpf.sketch.compose.core.android.test.PainterImageAndroidTest.testImageAsPainterOrNull
29
+ */
30
+ actual fun Image.asPainterOrNull(filterQuality: FilterQuality): Painter? = when (this) {
31
+ is PainterImage -> painter
32
+ is BitmapImage -> bitmap.asImageBitmap().asPainter(filterQuality)
33
+ is AnimatedImage -> AnimatedImagePainter(this)
34
+ is DrawableImage -> drawable.asPainter()
35
+ else -> null
36
+ }
@@ -0,0 +1,83 @@
1
+ /*
2
+ * Copyright (C) 2024 panpf <panpfpanpf@outlook.com>
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.github.panpf.sketch.painter
18
+
19
+ import android.graphics.drawable.Animatable
20
+ import android.graphics.drawable.Drawable
21
+ import androidx.compose.runtime.Stable
22
+ import com.github.panpf.sketch.util.toLogString
23
+
24
+ /**
25
+ * Drawable painter that implements the [AnimatablePainter] interface
26
+ *
27
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawableAnimatablePainterTest
28
+ */
29
+ @Stable
30
+ class DrawableAnimatablePainter(
31
+ drawable: Drawable
32
+ ) : DrawablePainter(drawable), AnimatablePainter {
33
+
34
+ private val animatable: Animatable
35
+
36
+ init {
37
+ require(drawable is Animatable) {
38
+ "drawable must be Animatable"
39
+ }
40
+ animatable = drawable
41
+ }
42
+
43
+ override fun onFirstRemembered() {
44
+ super.onFirstRemembered()
45
+ animatable.start()
46
+ }
47
+
48
+ override fun onLastRemembered() {
49
+ super.onLastRemembered()
50
+ animatable.stop()
51
+ }
52
+
53
+ override fun start() {
54
+ if (rememberedCounter.isRemembered) {
55
+ animatable.start()
56
+ }
57
+ }
58
+
59
+ override fun stop() {
60
+ if (rememberedCounter.isRemembered) {
61
+ animatable.stop()
62
+ }
63
+ }
64
+
65
+ override fun isRunning(): Boolean {
66
+ return animatable.isRunning
67
+ }
68
+
69
+ override fun equals(other: Any?): Boolean {
70
+ if (this === other) return true
71
+ if (other == null || this::class != other::class) return false
72
+ other as DrawableAnimatablePainter
73
+ return drawable == other.drawable
74
+ }
75
+
76
+ override fun hashCode(): Int {
77
+ return drawable.hashCode()
78
+ }
79
+
80
+ override fun toString(): String {
81
+ return "DrawableAnimatablePainter(drawable=${drawable.toLogString()})"
82
+ }
83
+ }
@@ -0,0 +1,238 @@
1
+ /*
2
+ * Copyright (C) 2024 panpf <panpfpanpf@outlook.com>
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ package com.github.panpf.sketch.painter
18
+
19
+ import android.graphics.drawable.Animatable
20
+ import android.graphics.drawable.BitmapDrawable
21
+ import android.graphics.drawable.ColorDrawable
22
+ import android.graphics.drawable.Drawable
23
+ import android.graphics.drawable.Drawable.Callback
24
+ import android.os.Build.VERSION
25
+ import android.os.Handler
26
+ import android.os.Looper
27
+ import android.view.View
28
+ import androidx.compose.runtime.Composable
29
+ import androidx.compose.runtime.RememberObserver
30
+ import androidx.compose.runtime.Stable
31
+ import androidx.compose.runtime.getValue
32
+ import androidx.compose.runtime.mutableIntStateOf
33
+ import androidx.compose.runtime.mutableStateOf
34
+ import androidx.compose.runtime.remember
35
+ import androidx.compose.runtime.setValue
36
+ import androidx.compose.ui.geometry.Size
37
+ import androidx.compose.ui.graphics.Color
38
+ import androidx.compose.ui.graphics.ColorFilter
39
+ import androidx.compose.ui.graphics.asAndroidColorFilter
40
+ import androidx.compose.ui.graphics.asImageBitmap
41
+ import androidx.compose.ui.graphics.drawscope.DrawScope
42
+ import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
43
+ import androidx.compose.ui.graphics.nativeCanvas
44
+ import androidx.compose.ui.graphics.painter.BitmapPainter
45
+ import androidx.compose.ui.graphics.painter.ColorPainter
46
+ import androidx.compose.ui.graphics.painter.Painter
47
+ import androidx.compose.ui.graphics.withSave
48
+ import androidx.compose.ui.unit.LayoutDirection
49
+ import androidx.compose.ui.unit.LayoutDirection.Ltr
50
+ import androidx.compose.ui.unit.LayoutDirection.Rtl
51
+ import com.github.panpf.sketch.drawable.EquitableDrawable
52
+ import com.github.panpf.sketch.util.RememberedCounter
53
+ import com.github.panpf.sketch.util.toLogString
54
+ import kotlin.math.ceil
55
+ import kotlin.math.roundToInt
56
+
57
+ /**
58
+ * Remembers [Drawable] wrapped up as a [Painter]. This function attempts to un-wrap the
59
+ * drawable contents and use Compose primitives where possible.
60
+ *
61
+ * If the provided [drawable] is `null`, an empty no-op painter is returned.
62
+ *
63
+ * This function tries to dispatch lifecycle events to [drawable] as much as possible from
64
+ * within Compose.
65
+ *
66
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawablePainterTest.testRememberDrawablePainter
67
+ */
68
+ @Composable
69
+ fun rememberDrawablePainter(drawable: EquitableDrawable): Painter =
70
+ remember(drawable) { drawable.asPainter() }
71
+
72
+ /**
73
+ * Converts a [Drawable] to a [Painter]. This function attempts to un-wrap the drawable contents
74
+ *
75
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawablePainterTest.testAsPainter
76
+ */
77
+ fun Drawable?.asPainter(): Painter {
78
+ return when (this) {
79
+ null -> EmptyPainter
80
+ is Animatable -> DrawableAnimatablePainter(this.mutate())
81
+ is BitmapDrawable -> BitmapPainter(this.bitmap.asImageBitmap())
82
+ is ColorDrawable -> ColorPainter(Color(this.color))
83
+ // Since the DrawablePainter will be remembered and it implements RememberObserver, it
84
+ // will receive the necessary events
85
+ else -> DrawablePainter(this.mutate())
86
+ }
87
+ }
88
+
89
+ /**
90
+ * A [Painter] which draws an Android [Drawable] and supports [Animatable] drawables. Instances
91
+ * should be remembered to be able to start and stop [Animatable] animations.
92
+ *
93
+ * Instances are usually retrieved from [rememberDrawablePainter].
94
+ *
95
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawablePainterTest
96
+ */
97
+ @Stable
98
+ // TODO Synchronize from accompanyist-drawablepainter
99
+ open class DrawablePainter(
100
+ val drawable: Drawable
101
+ ) : Painter(), RememberObserver, SketchPainter {
102
+ private var drawInvalidateTick by mutableIntStateOf(0)
103
+ private var drawableIntrinsicSize by mutableStateOf(drawable.intrinsicSize)
104
+
105
+ private val callback: Callback by lazy {
106
+ object : Callback {
107
+ override fun invalidateDrawable(d: Drawable) {
108
+ // Update the tick so that we get re-drawn
109
+ drawInvalidateTick++
110
+ // Update our intrinsic size too
111
+ drawableIntrinsicSize = drawable.intrinsicSize
112
+ }
113
+
114
+ override fun scheduleDrawable(d: Drawable, what: Runnable, time: Long) {
115
+ MAIN_HANDLER.postAtTime(what, time)
116
+ }
117
+
118
+ override fun unscheduleDrawable(d: Drawable, what: Runnable) {
119
+ MAIN_HANDLER.removeCallbacks(what)
120
+ }
121
+ }
122
+ }
123
+
124
+ internal val rememberedCounter: RememberedCounter = RememberedCounter()
125
+
126
+ override val intrinsicSize: Size get() = drawableIntrinsicSize
127
+
128
+ init {
129
+ if (drawable.intrinsicWidth >= 0 && drawable.intrinsicHeight >= 0) {
130
+ // Update the drawable's bounds to match the intrinsic size
131
+ drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
132
+ }
133
+ }
134
+
135
+ override fun onRemembered() {
136
+ if (!rememberedCounter.remember()) return
137
+ onFirstRemembered()
138
+ }
139
+
140
+ protected open fun onFirstRemembered() {
141
+ drawable.callback = callback
142
+ drawable.setVisible(true, true)
143
+ }
144
+
145
+ override fun onAbandoned() = onForgotten()
146
+ override fun onForgotten() {
147
+ if (!rememberedCounter.forget()) return
148
+ onLastRemembered()
149
+ }
150
+
151
+ protected open fun onLastRemembered() {
152
+ drawable.setVisible(false, false)
153
+ drawable.callback = null
154
+ }
155
+
156
+ override fun applyAlpha(alpha: Float): Boolean {
157
+ drawable.alpha = (alpha * 255).roundToInt().coerceIn(0, 255)
158
+ return true
159
+ }
160
+
161
+ override fun applyColorFilter(colorFilter: ColorFilter?): Boolean {
162
+ drawable.colorFilter = colorFilter?.asAndroidColorFilter()
163
+ return true
164
+ }
165
+
166
+ override fun applyLayoutDirection(layoutDirection: LayoutDirection): Boolean {
167
+ if (VERSION.SDK_INT >= 23) {
168
+ return drawable.setLayoutDirection(
169
+ when (layoutDirection) {
170
+ Ltr -> View.LAYOUT_DIRECTION_LTR
171
+ Rtl -> View.LAYOUT_DIRECTION_RTL
172
+ }
173
+ )
174
+ }
175
+ return false
176
+ }
177
+
178
+ override fun DrawScope.onDraw() {
179
+ drawIntoCanvas { canvas ->
180
+ // Reading this ensures that we invalidate when invalidateDrawable() is called
181
+ drawInvalidateTick
182
+
183
+ // Update the Drawable's bounds
184
+ drawable.setBounds(0, 0, ceil(size.width).toInt(), ceil(size.height).toInt())
185
+
186
+ canvas.withSave {
187
+ drawable.draw(canvas.nativeCanvas)
188
+ }
189
+ }
190
+ }
191
+
192
+ override fun equals(other: Any?): Boolean {
193
+ if (this === other) return true
194
+ if (other == null || this::class != other::class) return false
195
+ other as DrawablePainter
196
+ return drawable == other.drawable
197
+ }
198
+
199
+ override fun hashCode(): Int {
200
+ return drawable.hashCode()
201
+ }
202
+
203
+ override fun toString(): String {
204
+ return "DrawablePainter(drawable=${drawable.toLogString()})"
205
+ }
206
+ }
207
+
208
+ /**
209
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawablePainterTest.testMainHandler
210
+ */
211
+ internal val MAIN_HANDLER by lazy(LazyThreadSafetyMode.NONE) {
212
+ Handler(Looper.getMainLooper())
213
+ }
214
+
215
+ /**
216
+ * Get the intrinsic size of Drawable, if Drawable has no intrinsic size, return [Size.Unspecified]
217
+ *
218
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawablePainterTest.testDrawableIntrinsicSize
219
+ */
220
+ internal val Drawable.intrinsicSize: Size
221
+ get() = when {
222
+ // Only return a finite size if the drawable has an intrinsic size
223
+ intrinsicWidth >= 0 && intrinsicHeight >= 0 -> {
224
+ Size(width = intrinsicWidth.toFloat(), height = intrinsicHeight.toFloat())
225
+ }
226
+
227
+ else -> Size.Unspecified
228
+ }
229
+
230
+ /**
231
+ * Empty Painter
232
+ *
233
+ * @see com.github.panpf.sketch.compose.core.android.test.painter.DrawablePainterTest.testEmptyPainter
234
+ */
235
+ internal object EmptyPainter : Painter() {
236
+ override val intrinsicSize: Size get() = Size.Unspecified
237
+ override fun DrawScope.onDraw() {}
238
+ }