@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.
- package/README.md +3 -0
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImage.kt +293 -20
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageCache.kt +34 -4
- package/android/src/main/java/com/margelo/nitro/onekeyimage/OneKeyImageMemoryVariants.kt +119 -0
- package/android/src/test/java/com/margelo/nitro/onekeyimage/OneKeyImageMemoryVariantsTest.kt +149 -0
- package/ios/OneKeyImage.swift +268 -38
- package/ios/OneKeyImageCache.swift +40 -3
- package/ios/OneKeyImageMemoryVariants.swift +137 -0
- package/ios/OneKeyImageRequestContext.swift +55 -0
- package/ios/tests/OneKeyImageInfrastructureTests.swift +218 -0
- package/lib/module/index.js +24 -3
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +9 -0
- package/lib/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +2 -0
- package/lib/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +5 -0
- package/lib/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +6 -0
- package/lib/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +7 -0
- package/lib/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +6 -0
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +1 -0
- package/lib/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +24 -0
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +2 -0
- package/lib/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +2 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +2 -0
- package/lib/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +3 -0
- package/lib/nitrogen/generated/shared/json/OneKeyImageConfig.json +1 -0
- package/lib/typescript/src/OneKeyImage.nitro.d.ts +6 -0
- package/lib/typescript/src/index.d.ts +1 -1
- package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.cpp +9 -0
- package/nitrogen/generated/android/c++/JHybridOneKeyImageSpec.hpp +2 -0
- package/nitrogen/generated/android/c++/views/JHybridOneKeyImageStateUpdater.cpp +5 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/onekeyimage/HybridOneKeyImageSpec.kt +6 -0
- package/nitrogen/generated/ios/c++/HybridOneKeyImageSpecSwift.hpp +7 -0
- package/nitrogen/generated/ios/c++/views/HybridOneKeyImageComponent.mm +6 -0
- package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec.swift +1 -0
- package/nitrogen/generated/ios/swift/HybridOneKeyImageSpec_cxx.swift +24 -0
- package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.cpp +2 -0
- package/nitrogen/generated/shared/c++/HybridOneKeyImageSpec.hpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.cpp +2 -0
- package/nitrogen/generated/shared/c++/views/HybridOneKeyImageComponent.hpp +3 -0
- package/nitrogen/generated/shared/json/OneKeyImageConfig.json +1 -0
- package/package.json +2 -2
- package/src/OneKeyImage.nitro.ts +6 -0
- 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
|
+
}
|