@onekeyfe/react-native-image 3.0.98

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 (186) hide show
  1. package/LICENSE +21 -0
  2. package/OneKeyImage.podspec +32 -0
  3. package/README.md +88 -0
  4. package/android/CMakeLists.txt +12 -0
  5. package/android/build.gradle +85 -0
  6. package/android/consumer-rules.pro +10 -0
  7. package/android/gradle.properties +5 -0
  8. package/android/src/main/AndroidManifest.xml +1 -0
  9. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  10. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImage.kt +616 -0
  11. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageCache.kt +131 -0
  12. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageDecodeDimensions.kt +169 -0
  13. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageGlideRegistry.kt +285 -0
  14. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageModel.kt +68 -0
  15. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImagePackage.kt +28 -0
  16. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageSafety.kt +1120 -0
  17. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyTosUrl.kt +93 -0
  18. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyAnimationTimelineInspectorTest.kt +210 -0
  19. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageCacheTypeTest.kt +16 -0
  20. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageDecodeDimensionsTest.kt +70 -0
  21. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageModelTest.kt +65 -0
  22. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageRequestSignatureTest.kt +33 -0
  23. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageSafetyTest.kt +346 -0
  24. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyResourceCleanupTest.kt +43 -0
  25. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyTerminalEventPairTest.kt +38 -0
  26. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyTosUrlTest.kt +82 -0
  27. package/ios/OneKeyImage.swift +636 -0
  28. package/ios/OneKeyImageCache.swift +215 -0
  29. package/ios/OneKeyImageRequestContext.swift +781 -0
  30. package/ios/OneKeyTosURL.swift +69 -0
  31. package/ios/tests/OneKeyImageInfrastructureTests.swift +555 -0
  32. package/ios/tests/OneKeyTosURLTests.swift +142 -0
  33. package/lib/module/OneKeyImage.nitro.js +30 -0
  34. package/lib/module/index.js +223 -0
  35. package/lib/module/package.json +1 -0
  36. package/lib/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  37. package/lib/nitrogen/generated/android/c++/JFunc_void_double_double_OneKeyImageCacheType.hpp +77 -0
  38. package/lib/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  39. package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageCacheSpec.cpp +130 -0
  40. package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageCacheSpec.hpp +66 -0
  41. package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +255 -0
  42. package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +93 -0
  43. package/lib/nitrogen/generated/android/c++/JOneKeyImageCachePolicy.hpp +64 -0
  44. package/lib/nitrogen/generated/android/c++/JOneKeyImageCacheType.hpp +61 -0
  45. package/lib/nitrogen/generated/android/c++/JOneKeyImageContentFit.hpp +64 -0
  46. package/lib/nitrogen/generated/android/c++/JOneKeyImageLoadingStrategy.hpp +61 -0
  47. package/lib/nitrogen/generated/android/c++/JOneKeyImagePreloadSource.hpp +88 -0
  48. package/lib/nitrogen/generated/android/c++/JOneKeyImageVariant.hpp +64 -0
  49. package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +144 -0
  50. package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.hpp +54 -0
  51. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/Func_void.kt +78 -0
  52. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/Func_void_double_double_OneKeyImageCacheType.kt +78 -0
  53. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/Func_void_std__string.kt +78 -0
  54. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageCacheSpec.kt +69 -0
  55. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +189 -0
  56. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageCachePolicy.kt +25 -0
  57. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageCacheType.kt +24 -0
  58. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageContentFit.kt +25 -0
  59. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageLoadingStrategy.kt +24 -0
  60. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImagePreloadSource.kt +86 -0
  61. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageVariant.kt +25 -0
  62. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/onekeyimageOnLoad.kt +35 -0
  63. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyImageManager.kt +96 -0
  64. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyImageStateUpdater.kt +23 -0
  65. package/lib/nitrogen/generated/android/onekeyimage+autolinking.cmake +85 -0
  66. package/lib/nitrogen/generated/android/onekeyimage+autolinking.gradle +27 -0
  67. package/lib/nitrogen/generated/android/onekeyimageOnLoad.cpp +78 -0
  68. package/lib/nitrogen/generated/android/onekeyimageOnLoad.hpp +34 -0
  69. package/lib/nitrogen/generated/ios/OneKeyImage+autolinking.rb +62 -0
  70. package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +90 -0
  71. package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +405 -0
  72. package/lib/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +71 -0
  73. package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.mm +41 -0
  74. package/lib/nitrogen/generated/ios/OneKeyImageAutolinking.swift +38 -0
  75. package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageCacheSpecSwift.cpp +11 -0
  76. package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageCacheSpecSwift.hpp +114 -0
  77. package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.cpp +11 -0
  78. package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +206 -0
  79. package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +228 -0
  80. package/lib/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  81. package/lib/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  82. package/lib/nitrogen/generated/ios/swift/Func_void_double_double_OneKeyImageCacheType.swift +46 -0
  83. package/lib/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  84. package/lib/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  85. package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageCacheSpec.swift +58 -0
  86. package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageCacheSpec_cxx.swift +202 -0
  87. package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +70 -0
  88. package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +539 -0
  89. package/lib/nitrogen/generated/ios/swift/OneKeyImageCachePolicy.swift +48 -0
  90. package/lib/nitrogen/generated/ios/swift/OneKeyImageCacheType.swift +44 -0
  91. package/lib/nitrogen/generated/ios/swift/OneKeyImageContentFit.swift +48 -0
  92. package/lib/nitrogen/generated/ios/swift/OneKeyImageLoadingStrategy.swift +44 -0
  93. package/lib/nitrogen/generated/ios/swift/OneKeyImagePreloadSource.swift +148 -0
  94. package/lib/nitrogen/generated/ios/swift/OneKeyImageVariant.swift +48 -0
  95. package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageCacheSpec.cpp +24 -0
  96. package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageCacheSpec.hpp +68 -0
  97. package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +52 -0
  98. package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +108 -0
  99. package/lib/nitrogen/generated/shared/c++/OneKeyImageCachePolicy.hpp +84 -0
  100. package/lib/nitrogen/generated/shared/c++/OneKeyImageCacheType.hpp +80 -0
  101. package/lib/nitrogen/generated/shared/c++/OneKeyImageContentFit.hpp +84 -0
  102. package/lib/nitrogen/generated/shared/c++/OneKeyImageLoadingStrategy.hpp +80 -0
  103. package/lib/nitrogen/generated/shared/c++/OneKeyImagePreloadSource.hpp +114 -0
  104. package/lib/nitrogen/generated/shared/c++/OneKeyImageVariant.hpp +84 -0
  105. package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +62 -0
  106. package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +132 -0
  107. package/lib/nitrogen/generated/shared/json/OneKeyImageConfig.json +24 -0
  108. package/lib/typescript/package.json +1 -0
  109. package/lib/typescript/src/OneKeyImage.nitro.d.ts +67 -0
  110. package/lib/typescript/src/index.d.ts +55 -0
  111. package/nitro.json +31 -0
  112. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  113. package/nitrogen/generated/android/c++/JFunc_void_double_double_OneKeyImageCacheType.hpp +77 -0
  114. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  115. package/nitrogen/generated/android/c++/JHybridOneKeyImageCacheSpec.cpp +130 -0
  116. package/nitrogen/generated/android/c++/JHybridOneKeyImageCacheSpec.hpp +66 -0
  117. package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +255 -0
  118. package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +93 -0
  119. package/nitrogen/generated/android/c++/JOneKeyImageCachePolicy.hpp +64 -0
  120. package/nitrogen/generated/android/c++/JOneKeyImageCacheType.hpp +61 -0
  121. package/nitrogen/generated/android/c++/JOneKeyImageContentFit.hpp +64 -0
  122. package/nitrogen/generated/android/c++/JOneKeyImageLoadingStrategy.hpp +61 -0
  123. package/nitrogen/generated/android/c++/JOneKeyImagePreloadSource.hpp +88 -0
  124. package/nitrogen/generated/android/c++/JOneKeyImageVariant.hpp +64 -0
  125. package/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +144 -0
  126. package/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.hpp +54 -0
  127. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/Func_void.kt +78 -0
  128. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/Func_void_double_double_OneKeyImageCacheType.kt +78 -0
  129. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/Func_void_std__string.kt +78 -0
  130. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageCacheSpec.kt +69 -0
  131. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +189 -0
  132. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageCachePolicy.kt +25 -0
  133. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageCacheType.kt +24 -0
  134. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageContentFit.kt +25 -0
  135. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageLoadingStrategy.kt +24 -0
  136. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImagePreloadSource.kt +86 -0
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/OneKeyImageVariant.kt +25 -0
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/onekeyimageOnLoad.kt +35 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyImageManager.kt +96 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/views/HybridOneKeyImageStateUpdater.kt +23 -0
  141. package/nitrogen/generated/android/onekeyimage+autolinking.cmake +85 -0
  142. package/nitrogen/generated/android/onekeyimage+autolinking.gradle +27 -0
  143. package/nitrogen/generated/android/onekeyimageOnLoad.cpp +78 -0
  144. package/nitrogen/generated/android/onekeyimageOnLoad.hpp +34 -0
  145. package/nitrogen/generated/ios/OneKeyImage+autolinking.rb +62 -0
  146. package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.cpp +90 -0
  147. package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Bridge.hpp +405 -0
  148. package/nitrogen/generated/ios/OneKeyImage-Swift-Cxx-Umbrella.hpp +71 -0
  149. package/nitrogen/generated/ios/OneKeyImageAutolinking.mm +41 -0
  150. package/nitrogen/generated/ios/OneKeyImageAutolinking.swift +38 -0
  151. package/nitrogen/generated/ios/c++/HybridOneKeyImageCacheSpecSwift.cpp +11 -0
  152. package/nitrogen/generated/ios/c++/HybridOneKeyImageCacheSpecSwift.hpp +114 -0
  153. package/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.cpp +11 -0
  154. package/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +206 -0
  155. package/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +228 -0
  156. package/nitrogen/generated/ios/swift/Func_void.swift +46 -0
  157. package/nitrogen/generated/ios/swift/Func_void_bool.swift +46 -0
  158. package/nitrogen/generated/ios/swift/Func_void_double_double_OneKeyImageCacheType.swift +46 -0
  159. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +46 -0
  160. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +46 -0
  161. package/nitrogen/generated/ios/swift/HybridOneKeyImageCacheSpec.swift +58 -0
  162. package/nitrogen/generated/ios/swift/HybridOneKeyImageCacheSpec_cxx.swift +202 -0
  163. package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +70 -0
  164. package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +539 -0
  165. package/nitrogen/generated/ios/swift/OneKeyImageCachePolicy.swift +48 -0
  166. package/nitrogen/generated/ios/swift/OneKeyImageCacheType.swift +44 -0
  167. package/nitrogen/generated/ios/swift/OneKeyImageContentFit.swift +48 -0
  168. package/nitrogen/generated/ios/swift/OneKeyImageLoadingStrategy.swift +44 -0
  169. package/nitrogen/generated/ios/swift/OneKeyImagePreloadSource.swift +148 -0
  170. package/nitrogen/generated/ios/swift/OneKeyImageVariant.swift +48 -0
  171. package/nitrogen/generated/shared/c++/HybridOneKeyImageCacheSpec.cpp +24 -0
  172. package/nitrogen/generated/shared/c++/HybridOneKeyImageCacheSpec.hpp +68 -0
  173. package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +52 -0
  174. package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +108 -0
  175. package/nitrogen/generated/shared/c++/OneKeyImageCachePolicy.hpp +84 -0
  176. package/nitrogen/generated/shared/c++/OneKeyImageCacheType.hpp +80 -0
  177. package/nitrogen/generated/shared/c++/OneKeyImageContentFit.hpp +84 -0
  178. package/nitrogen/generated/shared/c++/OneKeyImageLoadingStrategy.hpp +80 -0
  179. package/nitrogen/generated/shared/c++/OneKeyImagePreloadSource.hpp +114 -0
  180. package/nitrogen/generated/shared/c++/OneKeyImageVariant.hpp +84 -0
  181. package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +62 -0
  182. package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +132 -0
  183. package/nitrogen/generated/shared/json/OneKeyImageConfig.json +24 -0
  184. package/package.json +128 -0
  185. package/src/OneKeyImage.nitro.ts +86 -0
  186. package/src/index.tsx +376 -0
@@ -0,0 +1,616 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import android.graphics.Canvas
4
+ import android.graphics.Color
5
+ import android.graphics.Paint
6
+ import android.graphics.drawable.Animatable
7
+ import android.graphics.drawable.Drawable
8
+ import android.view.View
9
+ import android.widget.ImageView
10
+ import com.bumptech.glide.Glide
11
+ import com.bumptech.glide.load.DataSource
12
+ import com.bumptech.glide.load.engine.DiskCacheStrategy
13
+ import com.bumptech.glide.load.engine.GlideException
14
+ import com.bumptech.glide.request.RequestListener
15
+ import com.bumptech.glide.request.RequestOptions
16
+ import com.bumptech.glide.request.target.CustomViewTarget
17
+ import com.bumptech.glide.request.target.Target
18
+ import com.bumptech.glide.request.transition.Transition
19
+ import com.facebook.proguard.annotations.DoNotStrip
20
+ import com.facebook.react.uimanager.ThemedReactContext
21
+ import com.margelo.nitro.skeleton.OneKeySkeletonRenderer
22
+ import com.margelo.nitro.views.RecyclableView
23
+ import kotlin.math.ceil
24
+
25
+ private class OneKeyImageHostView(context: ThemedReactContext) : ImageView(context) {
26
+ private val fallbackPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
27
+ color = Color.rgb(110, 110, 120)
28
+ textAlign = Paint.Align.CENTER
29
+ }
30
+ private val skeleton = OneKeySkeletonRenderer { postInvalidateOnAnimation() }
31
+ var drawStateSymbol = false
32
+ var stateSymbol = "◇"
33
+ var autoplayEnabled = false
34
+ private var aggregatedVisible = true
35
+ var skeletonRequested = false
36
+ set(value) {
37
+ field = value
38
+ syncPlayback()
39
+ }
40
+ var onReadyForRequest: (() -> Unit)? = null
41
+
42
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
43
+ super.onSizeChanged(w, h, oldw, oldh)
44
+ skeleton.updateBounds(w, h)
45
+ fallbackPaint.textSize = minOf(w, h) * 0.35f
46
+ if (w > 0 && h > 0) onReadyForRequest?.invoke()
47
+ }
48
+
49
+ override fun onDraw(canvas: Canvas) {
50
+ super.onDraw(canvas)
51
+ if (drawStateSymbol) {
52
+ val baseline = height / 2f - (fallbackPaint.ascent() + fallbackPaint.descent()) / 2f
53
+ canvas.drawText(stateSymbol, width / 2f, baseline, fallbackPaint)
54
+ }
55
+ if (skeletonRequested) skeleton.draw(canvas)
56
+ }
57
+
58
+ override fun onAttachedToWindow() {
59
+ super.onAttachedToWindow()
60
+ syncPlayback()
61
+ }
62
+
63
+ override fun onDetachedFromWindow() {
64
+ super.onDetachedFromWindow()
65
+ syncPlayback()
66
+ }
67
+
68
+ override fun onVisibilityAggregated(isVisible: Boolean) {
69
+ super.onVisibilityAggregated(isVisible)
70
+ aggregatedVisible = isVisible
71
+ syncPlayback()
72
+ }
73
+
74
+ fun updateSkeletonStyle() {
75
+ skeleton.updateStyle(null, 3.0)
76
+ skeleton.updateBounds(width, height)
77
+ }
78
+
79
+ fun syncPlayback() {
80
+ val canRun = isAttachedToWindow && aggregatedVisible
81
+ if (skeletonRequested && canRun) skeleton.start() else skeleton.stop()
82
+ val animated = drawable as? Animatable
83
+ if (autoplayEnabled && canRun) animated?.start() else animated?.stop()
84
+ }
85
+ }
86
+
87
+ internal fun DataSource.toOneKeyImageCacheType(): OneKeyImageCacheType = when (this) {
88
+ DataSource.MEMORY_CACHE -> OneKeyImageCacheType.MEMORY
89
+ DataSource.DATA_DISK_CACHE, DataSource.RESOURCE_DISK_CACHE -> OneKeyImageCacheType.DISK
90
+ DataSource.LOCAL, DataSource.REMOTE -> OneKeyImageCacheType.NONE
91
+ }
92
+
93
+ internal fun oneKeyImageRequestSignature(
94
+ rawUrl: String,
95
+ sourceHeadersJson: String?,
96
+ recyclingKey: String?,
97
+ cachePolicy: OneKeyImageCachePolicy?,
98
+ contentFit: OneKeyImageContentFit?,
99
+ optimizeTos: Boolean?,
100
+ overscan: Double?,
101
+ width: Int,
102
+ height: Int,
103
+ density: Float,
104
+ ): String = listOf(
105
+ rawUrl,
106
+ sourceHeadersJson.orEmpty(),
107
+ recyclingKey.orEmpty(),
108
+ cachePolicy?.name.orEmpty(),
109
+ (contentFit ?: OneKeyImageContentFit.COVER).name,
110
+ optimizeTos.toString(),
111
+ overscan.toString(),
112
+ width.toString(),
113
+ height.toString(),
114
+ density.toString(),
115
+ ).joinToString("|")
116
+
117
+ @DoNotStrip
118
+ class HybridOneKeyImage(private val context: ThemedReactContext) :
119
+ HybridOneKeyImageSpec(), RecyclableView {
120
+ private enum class DisplayState { LOADING, IMAGE, ERROR, FALLBACK }
121
+
122
+ private val hostView = OneKeyImageHostView(context)
123
+ private var loadRunnable: Runnable? = null
124
+ private var currentTarget: CustomViewTarget<OneKeyImageHostView, Drawable>? = null
125
+ private var generation = 0L
126
+ private var lastSignature: String? = null
127
+ private var disposed = false
128
+ private var suppressPropEffects = false
129
+ private var requestActive = false
130
+ private var displayState = DisplayState.LOADING
131
+ private var displayRunnable: Runnable? = null
132
+ private var fallbackRunnable: Runnable? = null
133
+
134
+ override val view: View = hostView
135
+
136
+ override var sourceUri: String? = null
137
+ set(value) {
138
+ if (field == value) return
139
+ field = value
140
+ requestIdentityChanged()
141
+ }
142
+ override var sourceHeadersJson: String? = null
143
+ set(value) {
144
+ if (field == value) return
145
+ field = value
146
+ requestIdentityChanged()
147
+ }
148
+ override var variant: OneKeyImageVariant? = OneKeyImageVariant.GENERIC
149
+ set(value) {
150
+ field = value
151
+ if (!suppressPropEffects) applyVariant()
152
+ }
153
+ override var contentFit: OneKeyImageContentFit? = OneKeyImageContentFit.COVER
154
+ set(value) {
155
+ val changed = field != value
156
+ field = value
157
+ if (suppressPropEffects) return
158
+ applyContentFit()
159
+ if (changed) requestIdentityChanged()
160
+ }
161
+ override var cachePolicy: OneKeyImageCachePolicy? = OneKeyImageCachePolicy.MEMORY_DISK
162
+ set(value) {
163
+ if (field == value) return
164
+ field = value
165
+ requestIdentityChanged()
166
+ }
167
+ override var autoplay: Boolean? = false
168
+ set(value) {
169
+ field = value
170
+ if (!suppressPropEffects) applyAutoplay()
171
+ }
172
+ override var recyclingKey: String? = null
173
+ set(value) {
174
+ if (field == value) return
175
+ field = value
176
+ requestIdentityChanged()
177
+ }
178
+ override var optimizeTos: Boolean? = true
179
+ set(value) {
180
+ if (field == value) return
181
+ field = value
182
+ requestIdentityChanged()
183
+ }
184
+ override var overscan: Double? = 1.1
185
+ set(value) {
186
+ if (field == value) return
187
+ field = value
188
+ requestIdentityChanged()
189
+ }
190
+ override var loadingStrategy: OneKeyImageLoadingStrategy? = OneKeyImageLoadingStrategy.STATIC
191
+ set(value) {
192
+ field = value
193
+ if (!suppressPropEffects) applyVariant()
194
+ }
195
+ override var onLoadStart: (() -> Unit)? = null
196
+ override var onLoad: ((width: Double, height: Double, cacheType: OneKeyImageCacheType) -> Unit)? = null
197
+ override var onDisplay: (() -> Unit)? = null
198
+ override var onError: ((message: String) -> Unit)? = null
199
+ override var onLoadEnd: (() -> Unit)? = null
200
+
201
+ init {
202
+ OneKeyImageGlideRegistry.ensureRegistered(context)
203
+ hostView.updateSkeletonStyle()
204
+ hostView.onReadyForRequest = { scheduleLoad() }
205
+ applyContentFit()
206
+ applyVariant()
207
+ }
208
+
209
+ override fun afterUpdate() {
210
+ super.afterUpdate()
211
+ scheduleLoad()
212
+ }
213
+
214
+ override fun reload() {
215
+ lastSignature = null
216
+ scheduleLoad(force = true)
217
+ }
218
+
219
+ override fun cancel() {
220
+ cancelCurrent(invalidateGeneration = true)
221
+ showLoading(requestIsActive = false)
222
+ }
223
+
224
+ override fun prepareForRecycle() {
225
+ resetForReuse()
226
+ }
227
+
228
+ override fun onDropView() {
229
+ resetForReuse()
230
+ disposed = true
231
+ hostView.onReadyForRequest = null
232
+ super.onDropView()
233
+ }
234
+
235
+ override fun dispose() {
236
+ disposed = true
237
+ cancelCurrent(invalidateGeneration = true)
238
+ hostView.onReadyForRequest = null
239
+ super.dispose()
240
+ }
241
+
242
+ private fun requestIdentityChanged() {
243
+ if (suppressPropEffects || disposed) return
244
+ lastSignature = null
245
+ cancelCurrent(invalidateGeneration = true)
246
+ if (sourceUri.isNullOrBlank()) showFallback() else showLoading(requestIsActive = false)
247
+ scheduleLoad()
248
+ }
249
+
250
+ private fun resetForReuse() {
251
+ cancelCurrent(invalidateGeneration = true)
252
+ lastSignature = null
253
+ suppressPropEffects = true
254
+ sourceUri = null
255
+ sourceHeadersJson = null
256
+ variant = OneKeyImageVariant.GENERIC
257
+ contentFit = OneKeyImageContentFit.COVER
258
+ cachePolicy = OneKeyImageCachePolicy.MEMORY_DISK
259
+ autoplay = false
260
+ recyclingKey = null
261
+ optimizeTos = true
262
+ overscan = 1.1
263
+ loadingStrategy = OneKeyImageLoadingStrategy.STATIC
264
+ onLoadStart = null
265
+ onLoad = null
266
+ onDisplay = null
267
+ onError = null
268
+ onLoadEnd = null
269
+ suppressPropEffects = false
270
+ requestActive = false
271
+ displayState = DisplayState.LOADING
272
+ hostView.setImageDrawable(null)
273
+ hostView.drawStateSymbol = false
274
+ hostView.skeletonRequested = false
275
+ hostView.setBackgroundColor(Color.TRANSPARENT)
276
+ applyContentFit()
277
+ applyAutoplay()
278
+ hostView.invalidate()
279
+ }
280
+
281
+ private fun scheduleLoad(force: Boolean = false) {
282
+ if (disposed) return
283
+ loadRunnable?.let(hostView::removeCallbacks)
284
+ val runnable = Runnable { startLoad(force) }
285
+ loadRunnable = runnable
286
+ hostView.post(runnable)
287
+ }
288
+
289
+ private fun startLoad(force: Boolean) {
290
+ if (disposed) return
291
+ if (hostView.width <= 0 || hostView.height <= 0) {
292
+ cancelCurrent(invalidateGeneration = true)
293
+ lastSignature = null
294
+ showLoading(requestIsActive = false)
295
+ return
296
+ }
297
+ val rawUrl = sourceUri?.takeIf { it.isNotBlank() }
298
+ if (rawUrl == null) {
299
+ cancelCurrent(invalidateGeneration = true)
300
+ lastSignature = null
301
+ showFallback()
302
+ return
303
+ }
304
+ val signature = oneKeyImageRequestSignature(
305
+ rawUrl = rawUrl,
306
+ sourceHeadersJson = sourceHeadersJson,
307
+ recyclingKey = recyclingKey,
308
+ cachePolicy = cachePolicy,
309
+ contentFit = contentFit,
310
+ optimizeTos = optimizeTos,
311
+ overscan = overscan,
312
+ width = hostView.width,
313
+ height = hostView.height,
314
+ density = hostView.resources.displayMetrics.density,
315
+ )
316
+ if (!force && signature == lastSignature) return
317
+ lastSignature = signature
318
+
319
+ cancelCurrent(invalidateGeneration = true)
320
+ val requestGeneration = generation
321
+ onLoadStart?.invoke()
322
+
323
+ val customIdentity = OneKeyImageModel.headers(sourceHeadersJson).isNotEmpty()
324
+ val optimizedUrl = if (optimizeTos != false) {
325
+ val density = hostView.resources.displayMetrics.density.coerceAtLeast(1f)
326
+ OneKeyTosUrl.optimized(
327
+ rawUrl,
328
+ ceil(maxOf(hostView.width, hostView.height) / density.toDouble()).toInt(),
329
+ density,
330
+ overscan ?: 1.1,
331
+ customIdentity,
332
+ )
333
+ } else rawUrl
334
+ val decodeDimensions = OneKeyImageDecodeDimensions.forRender(hostView.width, hostView.height)
335
+ performRequest(
336
+ requestUrl = optimizedUrl,
337
+ rawUrl = rawUrl,
338
+ requestGeneration = requestGeneration,
339
+ mayFallbackToRaw = optimizedUrl != rawUrl,
340
+ headersJson = sourceHeadersJson,
341
+ policy = cachePolicy ?: OneKeyImageCachePolicy.MEMORY_DISK,
342
+ decodeDimensions = decodeDimensions,
343
+ )
344
+ }
345
+
346
+ private fun performRequest(
347
+ requestUrl: String,
348
+ rawUrl: String,
349
+ requestGeneration: Long,
350
+ mayFallbackToRaw: Boolean,
351
+ headersJson: String?,
352
+ policy: OneKeyImageCachePolicy,
353
+ decodeDimensions: OneKeyImageDecodeDimensions,
354
+ ) {
355
+ if (requestGeneration != generation || disposed) return
356
+ clearCurrentTarget()
357
+ var resolvedCacheType = OneKeyImageCacheType.NONE
358
+ var failureMessage = "Image request failed: $requestUrl"
359
+ var safetyFailure = false
360
+ val target = object : CustomViewTarget<OneKeyImageHostView, Drawable>(hostView) {
361
+ override fun onResourceLoading(placeholder: Drawable?) {
362
+ if (requestGeneration != generation || disposed) return
363
+ showLoading(requestIsActive = true)
364
+ }
365
+
366
+ override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
367
+ if (requestGeneration != generation || disposed) return
368
+ requestActive = false
369
+ displayState = DisplayState.IMAGE
370
+ hostView.skeletonRequested = false
371
+ hostView.drawStateSymbol = false
372
+ hostView.setBackgroundColor(Color.TRANSPARENT)
373
+ hostView.setImageDrawable(resource)
374
+ applyAutoplay()
375
+ val loadCallback = onLoad
376
+ val loadEndCallback = onLoadEnd
377
+ OneKeyTerminalEventPair.dispatch(
378
+ primary = loadCallback?.let { callback ->
379
+ {
380
+ callback(
381
+ resource.intrinsicWidth.coerceAtLeast(0).toDouble(),
382
+ resource.intrinsicHeight.coerceAtLeast(0).toDouble(),
383
+ resolvedCacheType,
384
+ )
385
+ }
386
+ },
387
+ onLoadEnd = loadEndCallback,
388
+ )
389
+ if (requestGeneration != generation || disposed) return
390
+ scheduleOnDisplay(requestGeneration)
391
+ }
392
+
393
+ override fun onResourceCleared(placeholder: Drawable?) {
394
+ if (requestGeneration == generation && currentTarget === this) {
395
+ requestActive = false
396
+ hostView.skeletonRequested = false
397
+ hostView.setImageDrawable(null)
398
+ }
399
+ }
400
+
401
+ override fun onLoadFailed(errorDrawable: Drawable?) {
402
+ if (requestGeneration != generation || disposed) return
403
+ if (mayFallbackToRaw && !safetyFailure) {
404
+ // Glide forbids clear()/into() while executing a Target or listener
405
+ // callback. Move the raw retry to the host queue so the failed
406
+ // optimized request has fully left its callback first.
407
+ val failedTarget = this
408
+ val runnable = Runnable {
409
+ fallbackRunnable = null
410
+ if (
411
+ requestGeneration == generation &&
412
+ !disposed &&
413
+ currentTarget === failedTarget
414
+ ) {
415
+ performRequest(
416
+ requestUrl = rawUrl,
417
+ rawUrl = rawUrl,
418
+ requestGeneration = requestGeneration,
419
+ mayFallbackToRaw = false,
420
+ headersJson = headersJson,
421
+ policy = policy,
422
+ decodeDimensions = decodeDimensions,
423
+ )
424
+ }
425
+ }
426
+ fallbackRunnable?.let(hostView::removeCallbacks)
427
+ fallbackRunnable = runnable
428
+ hostView.post(runnable)
429
+ return
430
+ }
431
+ finishWithError(failureMessage, requestGeneration)
432
+ }
433
+ }
434
+ currentTarget = target
435
+ Glide.with(hostView)
436
+ .asDrawable()
437
+ .load(OneKeyImageModel.build(requestUrl, headersJson))
438
+ .apply(requestOptions(policy))
439
+ .override(decodeDimensions.width, decodeDimensions.height)
440
+ .listener(object : RequestListener<Drawable> {
441
+ override fun onLoadFailed(
442
+ error: GlideException?,
443
+ model: Any?,
444
+ target: Target<Drawable>,
445
+ isFirstResource: Boolean,
446
+ ): Boolean {
447
+ failureMessage = error?.message ?: failureMessage
448
+ safetyFailure = OneKeyImageSafety.isSafetyFailure(error)
449
+ return false
450
+ }
451
+
452
+ override fun onResourceReady(
453
+ resource: Drawable,
454
+ model: Any,
455
+ target: Target<Drawable>?,
456
+ dataSource: DataSource,
457
+ isFirstResource: Boolean,
458
+ ): Boolean {
459
+ resolvedCacheType = dataSource.toOneKeyImageCacheType()
460
+ return false
461
+ }
462
+ })
463
+ .into(target)
464
+ }
465
+
466
+ private fun requestOptions(policy: OneKeyImageCachePolicy): RequestOptions {
467
+ val options = RequestOptions()
468
+ .dontTransform()
469
+ .downsample(OneKeyImageSafeDownsampleStrategy())
470
+ return when (policy) {
471
+ OneKeyImageCachePolicy.MEMORY_DISK -> options.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
472
+ OneKeyImageCachePolicy.MEMORY -> options.diskCacheStrategy(DiskCacheStrategy.NONE)
473
+ OneKeyImageCachePolicy.DISK -> options
474
+ .diskCacheStrategy(DiskCacheStrategy.DATA)
475
+ .skipMemoryCache(true)
476
+ OneKeyImageCachePolicy.NONE -> options
477
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
478
+ .skipMemoryCache(true)
479
+ }
480
+ }
481
+
482
+ private fun finishWithError(message: String, requestGeneration: Long) {
483
+ if (requestGeneration != generation || disposed) return
484
+ showError()
485
+ val errorCallback = onError
486
+ val loadEndCallback = onLoadEnd
487
+ OneKeyTerminalEventPair.dispatch(
488
+ primary = errorCallback?.let { callback -> { callback(message) } },
489
+ onLoadEnd = loadEndCallback,
490
+ )
491
+ }
492
+
493
+ private fun cancelCurrent(invalidateGeneration: Boolean) {
494
+ loadRunnable?.let(hostView::removeCallbacks)
495
+ loadRunnable = null
496
+ displayRunnable?.let(hostView::removeCallbacks)
497
+ displayRunnable = null
498
+ fallbackRunnable?.let(hostView::removeCallbacks)
499
+ fallbackRunnable = null
500
+ clearCurrentTarget()
501
+ requestActive = false
502
+ hostView.skeletonRequested = false
503
+ if (invalidateGeneration) generation++
504
+ }
505
+
506
+ private fun clearCurrentTarget() {
507
+ val target = currentTarget ?: return
508
+ // Clear the field first so onResourceCleared from our own cancellation
509
+ // cannot erase state belonging to the next generation/request.
510
+ currentTarget = null
511
+ Glide.with(hostView).clear(target)
512
+ }
513
+
514
+ private fun scheduleOnDisplay(requestGeneration: Long) {
515
+ displayRunnable?.let(hostView::removeCallbacks)
516
+ val runnable = Runnable {
517
+ displayRunnable = null
518
+ if (
519
+ requestGeneration == generation &&
520
+ !disposed &&
521
+ displayState == DisplayState.IMAGE
522
+ ) {
523
+ onDisplay?.invoke()
524
+ }
525
+ }
526
+ displayRunnable = runnable
527
+ hostView.postOnAnimation(runnable)
528
+ }
529
+
530
+ private fun showLoading(requestIsActive: Boolean) {
531
+ displayState = DisplayState.LOADING
532
+ requestActive = requestIsActive
533
+ hostView.setImageDrawable(null)
534
+ hostView.drawStateSymbol = false
535
+ applyLoadingAppearance()
536
+ }
537
+
538
+ private fun showError() {
539
+ showTerminalState(DisplayState.ERROR)
540
+ }
541
+
542
+ private fun showFallback() {
543
+ showTerminalState(DisplayState.FALLBACK)
544
+ }
545
+
546
+ private fun showTerminalState(state: DisplayState) {
547
+ displayState = state
548
+ requestActive = false
549
+ hostView.setImageDrawable(null)
550
+ hostView.skeletonRequested = false
551
+ hostView.drawStateSymbol = true
552
+ hostView.stateSymbol = stateSymbol()
553
+ hostView.setBackgroundColor(placeholderColor())
554
+ hostView.invalidate()
555
+ }
556
+
557
+ private fun applyVariant() {
558
+ when (displayState) {
559
+ DisplayState.IMAGE -> Unit
560
+ DisplayState.LOADING -> applyLoadingAppearance()
561
+ DisplayState.ERROR, DisplayState.FALLBACK -> {
562
+ hostView.setBackgroundColor(placeholderColor())
563
+ hostView.stateSymbol = stateSymbol()
564
+ hostView.invalidate()
565
+ }
566
+ }
567
+ }
568
+
569
+ private fun applyLoadingAppearance() {
570
+ val strategy = loadingStrategy ?: OneKeyImageLoadingStrategy.STATIC
571
+ hostView.setBackgroundColor(
572
+ if (strategy == OneKeyImageLoadingStrategy.NONE) Color.TRANSPARENT else placeholderColor(),
573
+ )
574
+ hostView.skeletonRequested =
575
+ requestActive && strategy == OneKeyImageLoadingStrategy.SKELETON
576
+ hostView.invalidate()
577
+ }
578
+
579
+ private fun stateSymbol(): String = when (variant ?: OneKeyImageVariant.GENERIC) {
580
+ OneKeyImageVariant.GENERIC -> "◇"
581
+ OneKeyImageVariant.TOKEN -> "◈"
582
+ OneKeyImageVariant.NETWORK -> "◎"
583
+ OneKeyImageVariant.AVATAR -> "●"
584
+ }
585
+
586
+ private fun placeholderColor(): Int = when (variant ?: OneKeyImageVariant.GENERIC) {
587
+ OneKeyImageVariant.GENERIC -> Color.rgb(232, 232, 232)
588
+ OneKeyImageVariant.TOKEN -> Color.rgb(232, 237, 249)
589
+ OneKeyImageVariant.NETWORK -> Color.rgb(229, 240, 245)
590
+ OneKeyImageVariant.AVATAR -> Color.rgb(234, 234, 240)
591
+ }
592
+
593
+ private fun applyContentFit() {
594
+ hostView.scaleType = when (contentFit ?: OneKeyImageContentFit.COVER) {
595
+ OneKeyImageContentFit.COVER -> ImageView.ScaleType.CENTER_CROP
596
+ OneKeyImageContentFit.CONTAIN -> ImageView.ScaleType.FIT_CENTER
597
+ OneKeyImageContentFit.FILL -> ImageView.ScaleType.FIT_XY
598
+ OneKeyImageContentFit.CENTER -> ImageView.ScaleType.CENTER
599
+ }
600
+ }
601
+
602
+ private fun applyAutoplay() {
603
+ hostView.autoplayEnabled = autoplay == true
604
+ hostView.syncPlayback()
605
+ }
606
+ }
607
+
608
+ internal object OneKeyTerminalEventPair {
609
+ fun dispatch(primary: (() -> Unit)?, onLoadEnd: (() -> Unit)?) {
610
+ try {
611
+ primary?.invoke()
612
+ } finally {
613
+ onLoadEnd?.invoke()
614
+ }
615
+ }
616
+ }