@onekeyfe/react-native-image 3.0.129 → 3.0.131
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/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImage.kt +94 -13
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageDecodeDimensions.kt +3 -2
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageReusableView.kt +6 -0
- package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageDecodeDimensionsTest.kt +9 -0
- package/package.json +2 -2
|
@@ -11,6 +11,7 @@ import android.graphics.Paint
|
|
|
11
11
|
import android.graphics.drawable.Animatable
|
|
12
12
|
import android.graphics.drawable.Drawable
|
|
13
13
|
import android.os.Build
|
|
14
|
+
import android.os.Looper
|
|
14
15
|
import android.os.SystemClock
|
|
15
16
|
import android.provider.Settings
|
|
16
17
|
import android.view.View
|
|
@@ -175,6 +176,8 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
175
176
|
private var pendingDisplayGeneration: Long? = null
|
|
176
177
|
private var fallbackRunnable: Runnable? = null
|
|
177
178
|
private var requestStartedAtMs: Long? = null
|
|
179
|
+
private var dropCleanupPending = false
|
|
180
|
+
private var dropCleanupFinished = false
|
|
178
181
|
|
|
179
182
|
/**
|
|
180
183
|
* Native reusable containers own their request cleanup explicitly, so their
|
|
@@ -268,10 +271,10 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
268
271
|
|
|
269
272
|
init {
|
|
270
273
|
OneKeyImageGlideRegistry.ensureRegistered(context)
|
|
271
|
-
hostView.onReadyForRequest = {
|
|
274
|
+
hostView.onReadyForRequest = { loadForCurrentLayout() }
|
|
272
275
|
hostView.onAttachmentChanged = { attached ->
|
|
273
276
|
if (attached) {
|
|
274
|
-
|
|
277
|
+
loadForCurrentLayout()
|
|
275
278
|
schedulePendingDisplayIfNeeded()
|
|
276
279
|
} else {
|
|
277
280
|
displayRunnable?.let(hostView::removeCallbacks)
|
|
@@ -284,12 +287,12 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
284
287
|
|
|
285
288
|
override fun afterUpdate() {
|
|
286
289
|
super.afterUpdate()
|
|
287
|
-
|
|
290
|
+
loadForCurrentLayout()
|
|
288
291
|
}
|
|
289
292
|
|
|
290
293
|
override fun reload() {
|
|
291
294
|
lastSignature = null
|
|
292
|
-
|
|
295
|
+
loadForCurrentLayout(force = true)
|
|
293
296
|
}
|
|
294
297
|
|
|
295
298
|
override fun cancel() {
|
|
@@ -302,21 +305,27 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
302
305
|
}
|
|
303
306
|
|
|
304
307
|
override fun onDropView() {
|
|
305
|
-
|
|
306
|
-
disposed = true
|
|
307
|
-
hostView.onReadyForRequest = null
|
|
308
|
-
hostView.onAttachmentChanged = null
|
|
308
|
+
beginDrop()
|
|
309
309
|
super.onDropView()
|
|
310
310
|
}
|
|
311
311
|
|
|
312
312
|
override fun dispose() {
|
|
313
|
-
|
|
314
|
-
cancelCurrent(invalidateGeneration = true)
|
|
315
|
-
hostView.onReadyForRequest = null
|
|
316
|
-
hostView.onAttachmentChanged = null
|
|
313
|
+
beginDrop()
|
|
317
314
|
super.dispose()
|
|
318
315
|
}
|
|
319
316
|
|
|
317
|
+
internal fun isDisplaying(
|
|
318
|
+
sourceUri: String?,
|
|
319
|
+
sourceHeadersJson: String?,
|
|
320
|
+
recyclingKey: String,
|
|
321
|
+
): Boolean =
|
|
322
|
+
!disposed &&
|
|
323
|
+
displayState == DisplayState.IMAGE &&
|
|
324
|
+
hostView.drawable != null &&
|
|
325
|
+
this.sourceUri == sourceUri &&
|
|
326
|
+
this.sourceHeadersJson == sourceHeadersJson &&
|
|
327
|
+
this.recyclingKey == recyclingKey
|
|
328
|
+
|
|
320
329
|
private fun requestIdentityChanged() {
|
|
321
330
|
if (suppressPropEffects || disposed) return
|
|
322
331
|
lastSignature = null
|
|
@@ -361,11 +370,29 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
361
370
|
private fun scheduleLoad(force: Boolean = false) {
|
|
362
371
|
if (disposed) return
|
|
363
372
|
loadRunnable?.let(hostView::removeCallbacks)
|
|
364
|
-
val runnable = Runnable {
|
|
373
|
+
val runnable = Runnable {
|
|
374
|
+
loadRunnable = null
|
|
375
|
+
startLoad(force)
|
|
376
|
+
}
|
|
365
377
|
loadRunnable = runnable
|
|
366
378
|
hostView.post(runnable)
|
|
367
379
|
}
|
|
368
380
|
|
|
381
|
+
private fun loadForCurrentLayout(force: Boolean = false) {
|
|
382
|
+
if (disposed) return
|
|
383
|
+
loadRunnable?.let(hostView::removeCallbacks)
|
|
384
|
+
loadRunnable = null
|
|
385
|
+
if (
|
|
386
|
+
Looper.myLooper() == Looper.getMainLooper() &&
|
|
387
|
+
hostView.width > 0 &&
|
|
388
|
+
hostView.height > 0
|
|
389
|
+
) {
|
|
390
|
+
startLoad(force)
|
|
391
|
+
} else {
|
|
392
|
+
scheduleLoad(force)
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
369
396
|
private fun startLoad(force: Boolean) {
|
|
370
397
|
if (disposed) return
|
|
371
398
|
if (hostView.width <= 0 || hostView.height <= 0) {
|
|
@@ -662,6 +689,60 @@ class HybridOneKeyImage(private val context: ThemedReactContext) :
|
|
|
662
689
|
hostView.invalidate()
|
|
663
690
|
}
|
|
664
691
|
|
|
692
|
+
private fun beginDrop() {
|
|
693
|
+
if (dropCleanupPending || dropCleanupFinished) return
|
|
694
|
+
disposed = true
|
|
695
|
+
hostView.onReadyForRequest = null
|
|
696
|
+
cancelPendingCallbacksForDrop()
|
|
697
|
+
clearEventCallbacks()
|
|
698
|
+
hostView.autoplayEnabled = false
|
|
699
|
+
hostView.skeletonRequested = false
|
|
700
|
+
hostView.syncPlayback()
|
|
701
|
+
if (
|
|
702
|
+
!usesApplicationRequestManager &&
|
|
703
|
+
hostView.isAttachedToWindow &&
|
|
704
|
+
hostView.drawable != null
|
|
705
|
+
) {
|
|
706
|
+
dropCleanupPending = true
|
|
707
|
+
hostView.onAttachmentChanged = { attached ->
|
|
708
|
+
if (!attached) finishDrop()
|
|
709
|
+
}
|
|
710
|
+
} else {
|
|
711
|
+
finishDrop()
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
private fun finishDrop() {
|
|
716
|
+
if (dropCleanupFinished) return
|
|
717
|
+
dropCleanupPending = false
|
|
718
|
+
dropCleanupFinished = true
|
|
719
|
+
resetForReuse()
|
|
720
|
+
hostView.onReadyForRequest = null
|
|
721
|
+
hostView.onAttachmentChanged = null
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
private fun cancelPendingCallbacksForDrop() {
|
|
725
|
+
loadRunnable?.let(hostView::removeCallbacks)
|
|
726
|
+
loadRunnable = null
|
|
727
|
+
displayRunnable?.let(hostView::removeCallbacks)
|
|
728
|
+
displayRunnable = null
|
|
729
|
+
pendingDisplayGeneration = null
|
|
730
|
+
fallbackRunnable?.let(hostView::removeCallbacks)
|
|
731
|
+
fallbackRunnable = null
|
|
732
|
+
requestActive = false
|
|
733
|
+
hostView.skeletonRequested = false
|
|
734
|
+
requestStartedAtMs = null
|
|
735
|
+
resetImageTransition()
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
private fun clearEventCallbacks() {
|
|
739
|
+
onLoadStart = null
|
|
740
|
+
onLoad = null
|
|
741
|
+
onDisplay = null
|
|
742
|
+
onError = null
|
|
743
|
+
onLoadEnd = null
|
|
744
|
+
}
|
|
745
|
+
|
|
665
746
|
private fun applyVariant() {
|
|
666
747
|
when (displayState) {
|
|
667
748
|
DisplayState.IMAGE -> Unit
|
|
@@ -145,7 +145,8 @@ internal data class OneKeyImageDecodeDimensions(
|
|
|
145
145
|
/**
|
|
146
146
|
* Downsamples static bitmaps for the target while independently enforcing the
|
|
147
147
|
* 16 MiB area cap. `override()` alone cannot enforce an area cap for very wide
|
|
148
|
-
* or tall sources because Glide's default strategy follows target edges.
|
|
148
|
+
* or tall sources because Glide's default strategy follows target edges. Quality
|
|
149
|
+
* rounding keeps Glide's power-of-two sample from decoding below the target.
|
|
149
150
|
*/
|
|
150
151
|
internal object OneKeyImageSafeDownsampleStrategy : DownsampleStrategy() {
|
|
151
152
|
override fun getScaleFactor(
|
|
@@ -165,5 +166,5 @@ internal object OneKeyImageSafeDownsampleStrategy : DownsampleStrategy() {
|
|
|
165
166
|
sourceHeight: Int,
|
|
166
167
|
requestedWidth: Int,
|
|
167
168
|
requestedHeight: Int,
|
|
168
|
-
): SampleSizeRounding = SampleSizeRounding.
|
|
169
|
+
): SampleSizeRounding = SampleSizeRounding.QUALITY
|
|
169
170
|
}
|
|
@@ -68,6 +68,12 @@ class OneKeyImageReusableView(context: ThemedReactContext) : FrameLayout(context
|
|
|
68
68
|
image.afterUpdate()
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
fun isDisplaying(
|
|
72
|
+
sourceUri: String?,
|
|
73
|
+
sourceHeadersJson: String?,
|
|
74
|
+
recyclingKey: String,
|
|
75
|
+
): Boolean = image.isDisplaying(sourceUri, sourceHeadersJson, recyclingKey)
|
|
76
|
+
|
|
71
77
|
fun prepareForReuse() {
|
|
72
78
|
image.prepareForRecycle()
|
|
73
79
|
}
|
package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageDecodeDimensionsTest.kt
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.margelo.nitro.onekeyimage
|
|
2
2
|
|
|
3
|
+
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy.SampleSizeRounding
|
|
3
4
|
import org.junit.Assert.assertEquals
|
|
4
5
|
import org.junit.Assert.assertTrue
|
|
5
6
|
import org.junit.Test
|
|
@@ -69,6 +70,14 @@ class OneKeyImageDecodeDimensionsTest {
|
|
|
69
70
|
assertTrue(decodedBytes <= OneKeyImageDecodeDimensions.MAX_DECODE_BYTES)
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
@Test
|
|
74
|
+
fun safeDownsampleStrategyPrefersQualityOverUndersizedDecodes() {
|
|
75
|
+
assertEquals(
|
|
76
|
+
SampleSizeRounding.QUALITY,
|
|
77
|
+
OneKeyImageSafeDownsampleStrategy.getSampleSizeRounding(160, 160, 55, 55),
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
72
81
|
@Test
|
|
73
82
|
fun safeDownsampleStrategyIsSingletonForStableMemoryCacheIdentity() {
|
|
74
83
|
val strategyClass = Class.forName(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onekeyfe/react-native-image",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.131",
|
|
4
4
|
"description": "High-performance native image view for OneKey",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
"typescript": "^5.9.2"
|
|
82
82
|
},
|
|
83
83
|
"peerDependencies": {
|
|
84
|
-
"@onekeyfe/react-native-skeleton": "3.0.
|
|
84
|
+
"@onekeyfe/react-native-skeleton": "3.0.131",
|
|
85
85
|
"react": "*",
|
|
86
86
|
"react-native": "*",
|
|
87
87
|
"react-native-nitro-modules": "0.37.0"
|