@onekeyfe/react-native-image 3.0.156 → 3.0.157-alpha.249

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 (42) hide show
  1. package/README.md +3 -0
  2. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImage.kt +293 -20
  3. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageCache.kt +34 -4
  4. package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageMemoryVariants.kt +119 -0
  5. package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageMemoryVariantsTest.kt +149 -0
  6. package/ios/OneKeyImage.swift +268 -38
  7. package/ios/OneKeyImageCache.swift +40 -3
  8. package/ios/OneKeyImageMemoryVariants.swift +137 -0
  9. package/ios/OneKeyImageRequestContext.swift +55 -0
  10. package/ios/tests/OneKeyImageInfrastructureTests.swift +218 -0
  11. package/lib/module/index.js +24 -3
  12. package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +9 -0
  13. package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +2 -0
  14. package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +5 -0
  15. package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +6 -0
  16. package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +7 -0
  17. package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +6 -0
  18. package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +1 -0
  19. package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +24 -0
  20. package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +2 -0
  21. package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +2 -0
  22. package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +2 -0
  23. package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +3 -0
  24. package/lib/nitrogen/generated/shared/json/OneKeyImageConfig.json +1 -0
  25. package/lib/typescript/src/OneKeyImage.nitro.d.ts +6 -0
  26. package/lib/typescript/src/index.d.ts +1 -1
  27. package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +9 -0
  28. package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +2 -0
  29. package/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +5 -0
  30. package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +6 -0
  31. package/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +7 -0
  32. package/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +6 -0
  33. package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +1 -0
  34. package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +24 -0
  35. package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +2 -0
  36. package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +2 -0
  37. package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +2 -0
  38. package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +3 -0
  39. package/nitrogen/generated/shared/json/OneKeyImageConfig.json +1 -0
  40. package/package.json +2 -2
  41. package/src/OneKeyImage.nitro.ts +6 -0
  42. package/src/index.tsx +23 -1
@@ -0,0 +1,119 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import kotlin.math.max
4
+ import kotlin.math.min
5
+
6
+ internal data class OneKeyImageMemoryFamilyKey(
7
+ val rawUrl: String,
8
+ val headersDigest: String?,
9
+ ) {
10
+ companion object {
11
+ fun from(rawUrl: String, headersJson: String?): OneKeyImageMemoryFamilyKey =
12
+ OneKeyImageMemoryFamilyKey(
13
+ rawUrl = rawUrl,
14
+ headersDigest = OneKeyImageModel.remoteHeadersDigest(
15
+ OneKeyImageModel.headers(headersJson),
16
+ ),
17
+ )
18
+ }
19
+ }
20
+
21
+ internal data class OneKeyImageMemoryVariant(
22
+ val requestUrl: String,
23
+ val width: Int,
24
+ val height: Int,
25
+ val round: Boolean,
26
+ ) {
27
+ val area: Long
28
+ get() = width.toLong() * height.toLong()
29
+ }
30
+
31
+ /**
32
+ * A bounded index of size-specific Glide resources that may still be in memory.
33
+ *
34
+ * Glide remains the source of truth. Entries are only hints used to reproduce a
35
+ * candidate EngineKey; a synchronous cache miss removes the stale hint.
36
+ */
37
+ internal object OneKeyImageMemoryVariantRegistry {
38
+ private const val MAX_FAMILIES = 1024
39
+ private const val MAX_VARIANTS_PER_FAMILY = 8
40
+ private const val MAX_ASPECT_RATIO_DRIFT = 1.1
41
+
42
+ private val variantsByFamily =
43
+ LinkedHashMap<OneKeyImageMemoryFamilyKey, MutableList<OneKeyImageMemoryVariant>>(
44
+ 16,
45
+ 0.75f,
46
+ true,
47
+ )
48
+
49
+ @Synchronized
50
+ fun record(family: OneKeyImageMemoryFamilyKey, variant: OneKeyImageMemoryVariant) {
51
+ if (variant.width <= 0 || variant.height <= 0) return
52
+ val variants = variantsByFamily.getOrPut(family) { mutableListOf() }
53
+ variants.remove(variant)
54
+ variants.add(variant)
55
+ while (variants.size > MAX_VARIANTS_PER_FAMILY) variants.removeAt(0)
56
+ while (variantsByFamily.size > MAX_FAMILIES) {
57
+ val eldest = variantsByFamily.entries.firstOrNull()?.key ?: break
58
+ variantsByFamily.remove(eldest)
59
+ }
60
+ }
61
+
62
+ @Synchronized
63
+ fun remove(family: OneKeyImageMemoryFamilyKey, variant: OneKeyImageMemoryVariant) {
64
+ val variants = variantsByFamily[family] ?: return
65
+ variants.remove(variant)
66
+ if (variants.isEmpty()) variantsByFamily.remove(family)
67
+ }
68
+
69
+ @Synchronized
70
+ fun candidates(
71
+ family: OneKeyImageMemoryFamilyKey,
72
+ target: OneKeyImageMemoryVariant,
73
+ ): List<OneKeyImageMemoryVariant> = orderedCandidates(
74
+ variants = variantsByFamily[family].orEmpty(),
75
+ target = target,
76
+ )
77
+
78
+ @Synchronized
79
+ fun clear() {
80
+ variantsByFamily.clear()
81
+ }
82
+
83
+ internal fun orderedCandidates(
84
+ variants: List<OneKeyImageMemoryVariant>,
85
+ target: OneKeyImageMemoryVariant,
86
+ ): List<OneKeyImageMemoryVariant> {
87
+ val compatible = variants
88
+ .asSequence()
89
+ .filter { it != target }
90
+ .filter { candidate -> target.round || !candidate.round }
91
+ .filter { candidate -> aspectRatioIsCompatible(candidate, target) }
92
+ .distinct()
93
+ .toList()
94
+
95
+ val larger = compatible
96
+ .filter { it.width >= target.width && it.height >= target.height }
97
+ .minWithOrNull(
98
+ compareBy<OneKeyImageMemoryVariant> { it.area }
99
+ .thenBy { if (it.round == target.round) 0 else 1 },
100
+ )
101
+ val smaller = compatible
102
+ .filter { it.width <= target.width && it.height <= target.height }
103
+ .maxWithOrNull(
104
+ compareBy<OneKeyImageMemoryVariant> { it.area }
105
+ .thenByDescending { if (it.round == target.round) 0 else 1 },
106
+ )
107
+ return listOfNotNull(larger, smaller).distinct()
108
+ }
109
+
110
+ private fun aspectRatioIsCompatible(
111
+ candidate: OneKeyImageMemoryVariant,
112
+ target: OneKeyImageMemoryVariant,
113
+ ): Boolean {
114
+ val candidateRatio = candidate.width.toDouble() / candidate.height.toDouble()
115
+ val targetRatio = target.width.toDouble() / target.height.toDouble()
116
+ return max(candidateRatio, targetRatio) / min(candidateRatio, targetRatio) <=
117
+ MAX_ASPECT_RATIO_DRIFT
118
+ }
119
+ }
@@ -0,0 +1,149 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import com.bumptech.glide.load.engine.DiskCacheStrategy
4
+ import com.bumptech.glide.request.RequestOptions
5
+ import org.junit.Assert.assertEquals
6
+ import org.junit.Assert.assertFalse
7
+ import org.junit.Assert.assertSame
8
+ import org.junit.Assert.assertTrue
9
+ import org.junit.Test
10
+
11
+ class OneKeyImageMemoryVariantsTest {
12
+ @Test
13
+ fun choosesNearestLargerThenNearestSmallerVariant() {
14
+ val target = variant(96)
15
+ val candidates = OneKeyImageMemoryVariantRegistry.orderedCandidates(
16
+ variants = listOf(variant(48), variant(72), variant(120), variant(144)),
17
+ target = target,
18
+ )
19
+
20
+ assertEquals(listOf(variant(120), variant(72)), candidates)
21
+ }
22
+
23
+ @Test
24
+ fun ignoresExactAndIncompatibleAspectRatios() {
25
+ val target = variant(96)
26
+ val candidates = OneKeyImageMemoryVariantRegistry.orderedCandidates(
27
+ variants = listOf(
28
+ target,
29
+ OneKeyImageMemoryVariant("wide", 120, 60, round = false),
30
+ variant(120),
31
+ ),
32
+ target = target,
33
+ )
34
+
35
+ assertEquals(listOf(variant(120)), candidates)
36
+ }
37
+
38
+ @Test
39
+ fun roundTargetCanPreviewUntransformedResourceButSquareTargetCannotUseRoundResource() {
40
+ val untransformed = variant(120, round = false)
41
+ val transformed = variant(120, round = true)
42
+
43
+ assertEquals(
44
+ listOf(untransformed),
45
+ OneKeyImageMemoryVariantRegistry.orderedCandidates(
46
+ variants = listOf(untransformed),
47
+ target = variant(96, round = true),
48
+ ),
49
+ )
50
+ assertEquals(
51
+ emptyList<OneKeyImageMemoryVariant>(),
52
+ OneKeyImageMemoryVariantRegistry.orderedCandidates(
53
+ variants = listOf(transformed),
54
+ target = variant(96, round = false),
55
+ ),
56
+ )
57
+ }
58
+
59
+ @Test
60
+ fun crossSizeCenterPreviewUsesTemporaryScaling() {
61
+ assertTrue(
62
+ shouldScaleCenterMemoryPreview(
63
+ contentFit = OneKeyImageContentFit.CENTER,
64
+ candidate = variant(48),
65
+ target = variant(96),
66
+ ),
67
+ )
68
+ assertTrue(
69
+ shouldScaleCenterMemoryPreview(
70
+ contentFit = OneKeyImageContentFit.CENTER,
71
+ candidate = variant(144),
72
+ target = variant(96),
73
+ ),
74
+ )
75
+ assertFalse(
76
+ shouldScaleCenterMemoryPreview(
77
+ contentFit = OneKeyImageContentFit.CENTER,
78
+ candidate = variant(96),
79
+ target = variant(96),
80
+ ),
81
+ )
82
+ assertFalse(
83
+ shouldScaleCenterMemoryPreview(
84
+ contentFit = OneKeyImageContentFit.COVER,
85
+ candidate = variant(48),
86
+ target = variant(96),
87
+ ),
88
+ )
89
+ }
90
+
91
+ @Test
92
+ fun missingPreviewDrawableAllowsSameFamilyProbeRetry() {
93
+ assertTrue(
94
+ shouldProbeMemoryPreview(
95
+ hasTarget = true,
96
+ sameFamily = true,
97
+ hasDrawable = false,
98
+ ),
99
+ )
100
+ assertFalse(
101
+ shouldProbeMemoryPreview(
102
+ hasTarget = true,
103
+ sameFamily = true,
104
+ hasDrawable = true,
105
+ ),
106
+ )
107
+ }
108
+
109
+ @Test
110
+ fun replacingMemoryPreviewSkipsWholeViewFade() {
111
+ assertFalse(
112
+ shouldAnimateLoadedImageTransition(
113
+ cacheType = OneKeyImageCacheType.DISK,
114
+ fadeDelayElapsed = true,
115
+ animationsEnabled = true,
116
+ replacingMemoryPreview = true,
117
+ ),
118
+ )
119
+ assertTrue(
120
+ shouldAnimateLoadedImageTransition(
121
+ cacheType = OneKeyImageCacheType.DISK,
122
+ fadeDelayElapsed = true,
123
+ animationsEnabled = true,
124
+ replacingMemoryPreview = false,
125
+ ),
126
+ )
127
+ }
128
+
129
+ @Test
130
+ fun memoryProbeKeepsMemoryEnabledAndDisablesDiskReads() {
131
+ val options = memoryProbeRequestOptions(
132
+ RequestOptions().diskCacheStrategy(DiskCacheStrategy.AUTOMATIC),
133
+ )
134
+
135
+ assertSame(DiskCacheStrategy.NONE, options.diskCacheStrategy)
136
+ assertTrue(options.onlyRetrieveFromCache)
137
+ assertTrue(options.isMemoryCacheable)
138
+ }
139
+
140
+ private fun variant(
141
+ size: Int,
142
+ round: Boolean = false,
143
+ ) = OneKeyImageMemoryVariant(
144
+ requestUrl = "https://example.com/image-$size.png",
145
+ width = size,
146
+ height = size,
147
+ round = round,
148
+ )
149
+ }