@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,346 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import com.bumptech.glide.load.engine.GlideException
4
+ import com.bumptech.glide.signature.ObjectKey
5
+ import org.junit.Assert.assertEquals
6
+ import org.junit.Assert.assertFalse
7
+ import org.junit.Assert.assertNotEquals
8
+ import org.junit.Assert.assertThrows
9
+ import org.junit.Assert.assertTrue
10
+ import org.junit.Test
11
+ import java.io.ByteArrayInputStream
12
+ import java.io.ByteArrayOutputStream
13
+ import java.io.IOException
14
+ import java.security.MessageDigest
15
+
16
+ class OneKeyImageSafetyTest {
17
+ @Test
18
+ fun contentLengthAllowsBoundaryAndRejectsNextByte() {
19
+ OneKeyImageSafety.requireEncodedLength(OneKeyImageSafety.MAX_ENCODED_BYTES)
20
+
21
+ assertThrows(OneKeyImageSafetyException::class.java) {
22
+ OneKeyImageSafety.requireEncodedLength(OneKeyImageSafety.MAX_ENCODED_BYTES + 1L)
23
+ }
24
+ }
25
+
26
+ @Test
27
+ fun limitedStreamChecksActualReadBytes() {
28
+ val stream = OneKeyLimitedInputStream(
29
+ ByteArrayInputStream(ByteArray(6)),
30
+ maximumBytes = 4L,
31
+ )
32
+ val firstChunk = ByteArray(4)
33
+
34
+ assertEquals(4, stream.read(firstChunk))
35
+ assertThrows(OneKeyImageSafetyException::class.java) {
36
+ stream.read()
37
+ }
38
+ }
39
+
40
+ @Test
41
+ fun limitedStreamCountsSkippedBytes() {
42
+ val stream = OneKeyLimitedInputStream(
43
+ ByteArrayInputStream(ByteArray(6)),
44
+ maximumBytes = 4L,
45
+ )
46
+
47
+ assertEquals(4L, stream.skip(4L))
48
+ assertThrows(OneKeyImageSafetyException::class.java) {
49
+ stream.skip(1L)
50
+ }
51
+ }
52
+
53
+ @Test
54
+ fun dataUriByteCountHandlesPaddingWithoutDecoding() {
55
+ val padded = "data:image/png;base64,QUJDRA=="
56
+ val unpadded = "data:image/png;base64,QUJDRA"
57
+
58
+ assertEquals(4L, OneKeyImageSafety.dataUriDecodedByteCount(padded, padded.indexOf(',') + 1))
59
+ assertEquals(4L, OneKeyImageSafety.dataUriDecodedByteCount(unpadded, unpadded.indexOf(',') + 1))
60
+ }
61
+
62
+ @Test
63
+ fun dataUriRejectsEncodedTextThatCouldAllocateBeyondDecodedLimit() {
64
+ val maximumEncodedCharacters =
65
+ ((OneKeyImageSafety.MAX_DATA_URI_DECODED_BYTES + 2L) / 3L * 4L).toInt()
66
+ val prefix = "data:image/png;base64,"
67
+ val oversized = prefix + "A".repeat(maximumEncodedCharacters + 1)
68
+
69
+ assertThrows(OneKeyImageSafetyException::class.java) {
70
+ OneKeyImageSafety.dataUriDecodedByteCount(oversized, prefix.length)
71
+ }
72
+ }
73
+
74
+ @Test
75
+ fun staticDimensionGuardChecksPixelsAndSides() {
76
+ OneKeyImageSafety.requireStaticDimensions(10_000, 10_000)
77
+
78
+ assertThrows(OneKeyImageSafetyException::class.java) {
79
+ OneKeyImageSafety.requireStaticDimensions(10_001, 10_000)
80
+ }
81
+ assertThrows(OneKeyImageSafetyException::class.java) {
82
+ OneKeyImageSafety.requireStaticDimensions(32_769, 1)
83
+ }
84
+ }
85
+
86
+ @Test
87
+ fun animatedGuardsBoundTheUnavoidableCanvasAndFrameCount() {
88
+ OneKeyImageSafety.requireAnimatedDimensions(2048, 2048)
89
+ OneKeyImageSafety.requireAnimationFrameCount(1000)
90
+
91
+ assertThrows(OneKeyImageSafetyException::class.java) {
92
+ OneKeyImageSafety.requireAnimatedDimensions(2049, 2048)
93
+ }
94
+ assertThrows(OneKeyImageSafetyException::class.java) {
95
+ OneKeyImageSafety.requireAnimationFrameCount(1001)
96
+ }
97
+ }
98
+
99
+ @Test
100
+ fun avifBrandIsRejectedFromFtypBox() {
101
+ assertThrows(OneKeyImageSafetyException::class.java) {
102
+ OneKeyEncodedImageInspector.inspect(
103
+ ByteArrayInputStream(bmffBox("ftyp", ftypPayload("avif"))),
104
+ )
105
+ }
106
+
107
+ val nonAvif = OneKeyEncodedImageInspector.inspect(
108
+ ByteArrayInputStream(bmffBox("ftyp", ftypPayload("isom"))),
109
+ )
110
+ assertFalse(nonAvif.animated)
111
+ }
112
+
113
+ @Test
114
+ fun avifCannotHideBehindLeadingOrExtendedBmffBoxes() {
115
+ val leadingFree = bmffBox("free", byteArrayOf()) +
116
+ bmffBox("ftyp", ftypPayload("mif1", "avif"))
117
+ val leadingExtended = bmffBox("free", byteArrayOf(), extended = true) +
118
+ bmffBox("ftyp", ftypPayload("avis"))
119
+ val leadingUnknown = bmffBox("junk", byteArrayOf()) +
120
+ bmffBox("ftyp", ftypPayload("avif"))
121
+
122
+ assertThrows(OneKeyImageSafetyException::class.java) {
123
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(leadingFree))
124
+ }
125
+ assertThrows(OneKeyImageSafetyException::class.java) {
126
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(leadingExtended))
127
+ }
128
+ assertThrows(OneKeyImageSafetyException::class.java) {
129
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(leadingUnknown))
130
+ }
131
+ }
132
+
133
+ @Test
134
+ fun ambiguousOrOverlongBmffFailsClosed() {
135
+ assertThrows(OneKeyImageSafetyException::class.java) {
136
+ OneKeyEncodedImageInspector.inspect(
137
+ ByteArrayInputStream(bmffBox("free", byteArrayOf())),
138
+ )
139
+ }
140
+ val overlong = ByteArrayOutputStream().apply {
141
+ writeIntBe(this, 300_000)
142
+ write("free".toByteArray())
143
+ }.toByteArray()
144
+ assertThrows(OneKeyImageSafetyException::class.java) {
145
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(overlong))
146
+ }
147
+ val unknownOverlong = ByteArrayOutputStream().apply {
148
+ writeIntBe(this, 300_000)
149
+ write("junk".toByteArray())
150
+ }.toByteArray()
151
+ assertThrows(OneKeyImageSafetyException::class.java) {
152
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(unknownOverlong))
153
+ }
154
+ }
155
+
156
+ @Test
157
+ fun xmlAndSvgHeadersAreNotMisclassifiedAsBmff() {
158
+ val xml = OneKeyEncodedImageInspector.inspect(
159
+ ByteArrayInputStream("<?xml version=\"1.0\"?><svg/>".toByteArray()),
160
+ )
161
+ val svg = OneKeyEncodedImageInspector.inspect(
162
+ ByteArrayInputStream("<svg xmlns=\"http://www.w3.org/2000/svg\"/>".toByteArray()),
163
+ )
164
+
165
+ assertFalse(xml.animated)
166
+ assertFalse(svg.animated)
167
+ }
168
+
169
+ @Test
170
+ fun gifMetadataIsAnimatedAndUnsafeCanvasIsRejected() {
171
+ val safe = gifHeader(2048, 2048)
172
+ val metadata = OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(safe))
173
+
174
+ assertTrue(metadata.animated)
175
+ assertEquals(2048, metadata.width)
176
+ assertThrows(OneKeyImageSafetyException::class.java) {
177
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(gifHeader(3000, 2000)))
178
+ }
179
+ }
180
+
181
+ @Test
182
+ fun webpRequiresAnimationFlagAndChecksVp8xCanvas() {
183
+ val animated = OneKeyEncodedImageInspector.inspect(
184
+ ByteArrayInputStream(webpVp8x(2048, 2048, animated = true)),
185
+ )
186
+ val static = OneKeyEncodedImageInspector.inspect(
187
+ ByteArrayInputStream(webpVp8x(2048, 2048, animated = false)),
188
+ )
189
+
190
+ assertTrue(animated.animated)
191
+ assertFalse(static.animated)
192
+ assertThrows(OneKeyImageSafetyException::class.java) {
193
+ OneKeyEncodedImageInspector.inspect(
194
+ ByteArrayInputStream(webpVp8x(3000, 2000, animated = true)),
195
+ )
196
+ }
197
+ val ambiguous = webpVp8x(32, 32, animated = false).also {
198
+ "JUNK".toByteArray().copyInto(it, 12)
199
+ }
200
+ assertThrows(OneKeyImageSafetyException::class.java) {
201
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(ambiguous))
202
+ }
203
+ }
204
+
205
+ @Test
206
+ fun apngMetadataChecksCanvasAndFrameCountBeforeIdat() {
207
+ val metadata = OneKeyEncodedImageInspector.inspect(
208
+ ByteArrayInputStream(png(2048, 2048, frameCount = 12)),
209
+ )
210
+
211
+ assertTrue(metadata.animated)
212
+ assertEquals(12, metadata.frameCount)
213
+ assertThrows(OneKeyImageSafetyException::class.java) {
214
+ OneKeyEncodedImageInspector.inspect(
215
+ ByteArrayInputStream(png(2048, 2048, frameCount = 1001)),
216
+ )
217
+ }
218
+ assertThrows(OneKeyImageSafetyException::class.java) {
219
+ OneKeyEncodedImageInspector.inspect(
220
+ ByteArrayInputStream(png(3000, 2000, frameCount = 2)),
221
+ )
222
+ }
223
+ }
224
+
225
+ @Test
226
+ fun pngWithOverlongMetadataFailsClosed() {
227
+ val image = ByteArrayOutputStream().apply {
228
+ write(png(32, 32, frameCount = null).copyOfRange(0, 33))
229
+ writeIntBe(this, 300_000)
230
+ write("tEXt".toByteArray())
231
+ }.toByteArray()
232
+
233
+ assertThrows(OneKeyImageSafetyException::class.java) {
234
+ OneKeyEncodedImageInspector.inspect(ByteArrayInputStream(image))
235
+ }
236
+ }
237
+
238
+ @Test
239
+ fun safetyVersionParticipatesInMemoryAndDiskCacheIdentity() {
240
+ assertEquals("onekey-image-safety-v2", OneKeyImageSafetyVersionedKey.CACHE_VERSION)
241
+ val original = ObjectKey("custom-cache-key")
242
+ val first = OneKeyImageSafetyVersionedKey(original)
243
+ val same = OneKeyImageSafetyVersionedKey(ObjectKey("custom-cache-key"))
244
+ val originalDigest = MessageDigest.getInstance("SHA-256").apply {
245
+ original.updateDiskCacheKey(this)
246
+ }.digest()
247
+ val versionedDigest = MessageDigest.getInstance("SHA-256").apply {
248
+ first.updateDiskCacheKey(this)
249
+ }.digest()
250
+
251
+ assertEquals(first, same)
252
+ assertNotEquals(first, original)
253
+ assertFalse(originalDigest.contentEquals(versionedDigest))
254
+ }
255
+
256
+ @Test
257
+ fun safetyFailureIsFoundInsideGlideRootsAndOrdinaryFailureIsNot() {
258
+ val safety = GlideException(
259
+ "request failed",
260
+ listOf(OneKeyImageSafetyException("too large")),
261
+ )
262
+
263
+ assertTrue(OneKeyImageSafety.isSafetyFailure(safety))
264
+ assertFalse(OneKeyImageSafety.isSafetyFailure(IOException("corrupt image")))
265
+ }
266
+
267
+ private fun gifHeader(width: Int, height: Int): ByteArray = ByteArray(10).also { bytes ->
268
+ "GIF89a".toByteArray().copyInto(bytes)
269
+ bytes[6] = width.toByte()
270
+ bytes[7] = (width ushr 8).toByte()
271
+ bytes[8] = height.toByte()
272
+ bytes[9] = (height ushr 8).toByte()
273
+ }
274
+
275
+ private fun webpVp8x(width: Int, height: Int, animated: Boolean): ByteArray =
276
+ ByteArray(30).also { bytes ->
277
+ "RIFF".toByteArray().copyInto(bytes, 0)
278
+ "WEBP".toByteArray().copyInto(bytes, 8)
279
+ "VP8X".toByteArray().copyInto(bytes, 12)
280
+ bytes[16] = 10
281
+ bytes[20] = if (animated) 0x02 else 0x00
282
+ write24Le(bytes, 24, width - 1)
283
+ write24Le(bytes, 27, height - 1)
284
+ }
285
+
286
+ private fun png(width: Int, height: Int, frameCount: Int?): ByteArray =
287
+ ByteArrayOutputStream().apply {
288
+ write(byteArrayOf(0x89.toByte(), 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a))
289
+ val ihdr = ByteArrayOutputStream().apply {
290
+ writeIntBe(this, width)
291
+ writeIntBe(this, height)
292
+ write(byteArrayOf(8, 6, 0, 0, 0))
293
+ }.toByteArray()
294
+ writeChunk(this, "IHDR", ihdr)
295
+ if (frameCount != null) {
296
+ val animation = ByteArrayOutputStream().apply {
297
+ writeIntBe(this, frameCount)
298
+ writeIntBe(this, 0)
299
+ }.toByteArray()
300
+ writeChunk(this, "acTL", animation)
301
+ } else {
302
+ writeChunk(this, "IDAT", byteArrayOf())
303
+ }
304
+ }.toByteArray()
305
+
306
+ private fun writeChunk(output: ByteArrayOutputStream, type: String, data: ByteArray) {
307
+ writeIntBe(output, data.size)
308
+ output.write(type.toByteArray())
309
+ output.write(data)
310
+ output.write(byteArrayOf(0, 0, 0, 0))
311
+ }
312
+
313
+ private fun writeIntBe(output: ByteArrayOutputStream, value: Int) {
314
+ output.write((value ushr 24) and 0xff)
315
+ output.write((value ushr 16) and 0xff)
316
+ output.write((value ushr 8) and 0xff)
317
+ output.write(value and 0xff)
318
+ }
319
+
320
+ private fun write24Le(bytes: ByteArray, offset: Int, value: Int) {
321
+ bytes[offset] = value.toByte()
322
+ bytes[offset + 1] = (value ushr 8).toByte()
323
+ bytes[offset + 2] = (value ushr 16).toByte()
324
+ }
325
+
326
+ private fun ftypPayload(majorBrand: String, vararg compatibleBrands: String): ByteArray =
327
+ ByteArrayOutputStream().apply {
328
+ write(majorBrand.toByteArray())
329
+ write(byteArrayOf(0, 0, 0, 0))
330
+ compatibleBrands.forEach { write(it.toByteArray()) }
331
+ }.toByteArray()
332
+
333
+ private fun bmffBox(type: String, payload: ByteArray, extended: Boolean = false): ByteArray =
334
+ ByteArrayOutputStream().apply {
335
+ if (extended) {
336
+ writeIntBe(this, 1)
337
+ write(type.toByteArray())
338
+ writeIntBe(this, 0)
339
+ writeIntBe(this, 16 + payload.size)
340
+ } else {
341
+ writeIntBe(this, 8 + payload.size)
342
+ write(type.toByteArray())
343
+ }
344
+ write(payload)
345
+ }.toByteArray()
346
+ }
@@ -0,0 +1,43 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertSame
5
+ import org.junit.Assert.assertThrows
6
+ import org.junit.Test
7
+ import java.io.IOException
8
+
9
+ class OneKeyResourceCleanupTest {
10
+ @Test
11
+ fun ordinaryDecoderExceptionsRecycleAndRethrowTheSameFailure() {
12
+ listOf<Exception>(IOException("corrupt"), IllegalStateException("invalid"))
13
+ .forEach { expected ->
14
+ var recycleCount = 0
15
+
16
+ val thrown = assertThrows(expected.javaClass) {
17
+ OneKeyResourceCleanup.recycleOnException(
18
+ recycle = { recycleCount += 1 },
19
+ block = { throw expected },
20
+ )
21
+ }
22
+
23
+ assertSame(expected, thrown)
24
+ assertEquals(1, recycleCount)
25
+ }
26
+ }
27
+
28
+ @Test
29
+ fun errorsAreNotCaughtOrRecycled() {
30
+ val expected = AssertionError("fatal")
31
+ var recycleCount = 0
32
+
33
+ val thrown = assertThrows(AssertionError::class.java) {
34
+ OneKeyResourceCleanup.recycleOnException(
35
+ recycle = { recycleCount += 1 },
36
+ block = { throw expected },
37
+ )
38
+ }
39
+
40
+ assertSame(expected, thrown)
41
+ assertEquals(0, recycleCount)
42
+ }
43
+ }
@@ -0,0 +1,38 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertThrows
5
+ import org.junit.Test
6
+
7
+ class OneKeyTerminalEventPairTest {
8
+ @Test
9
+ fun terminalPrimaryAndSnapshottedEndStayPairedDuringReentry() {
10
+ val events = mutableListOf<String>()
11
+ var currentEnd: () -> Unit = { events += "old-end" }
12
+ val snapshottedEnd = currentEnd
13
+
14
+ OneKeyTerminalEventPair.dispatch(
15
+ primary = {
16
+ events += "load"
17
+ currentEnd = { events += "new-end" }
18
+ },
19
+ onLoadEnd = snapshottedEnd,
20
+ )
21
+
22
+ assertEquals(listOf("load", "old-end"), events)
23
+ }
24
+
25
+ @Test
26
+ fun terminalEndStillRunsExactlyOnceWhenPrimaryThrows() {
27
+ var endCount = 0
28
+
29
+ assertThrows(IllegalStateException::class.java) {
30
+ OneKeyTerminalEventPair.dispatch(
31
+ primary = { throw IllegalStateException("callback failed") },
32
+ onLoadEnd = { endCount += 1 },
33
+ )
34
+ }
35
+
36
+ assertEquals(1, endCount)
37
+ }
38
+ }
@@ -0,0 +1,82 @@
1
+ package com.margelo.nitro.onekeyimage
2
+
3
+ import org.junit.Assert.assertEquals
4
+ import org.junit.Assert.assertTrue
5
+ import org.junit.Test
6
+
7
+ class OneKeyTosUrlTest {
8
+ @Test
9
+ fun selectsFirstBucketCoveringPhysicalOverscannedWidth() {
10
+ assertEquals(256, OneKeyTosUrl.selectWidthBucket(100, 2f, 1.1))
11
+ assertEquals(160, OneKeyTosUrl.selectWidthBucket(160, 1f, 1.0))
12
+ }
13
+
14
+ @Test
15
+ fun capsDprAndSelectionAtLargestBucket() {
16
+ assertEquals(480, OneKeyTosUrl.selectWidthBucket(100, 5f, 1.1))
17
+ assertEquals(1280, OneKeyTosUrl.selectWidthBucket(2000, 3f, 1.1))
18
+ }
19
+
20
+ @Test
21
+ fun normalizesNonFiniteDensityAndOverscan() {
22
+ assertEquals(128, OneKeyTosUrl.selectWidthBucket(100, Float.NaN, 1.1))
23
+ assertEquals(128, OneKeyTosUrl.selectWidthBucket(100, Float.POSITIVE_INFINITY, 1.1))
24
+ assertEquals(200, OneKeyTosUrl.selectWidthBucket(100, 2f, Double.NaN))
25
+ assertEquals(200, OneKeyTosUrl.selectWidthBucket(100, 2f, Double.POSITIVE_INFINITY))
26
+ assertEquals(1280, OneKeyTosUrl.selectWidthBucket(100, 2f, Double.MAX_VALUE))
27
+ }
28
+
29
+ @Test
30
+ fun optimizesOnlyApprovedOneKeyHosts() {
31
+ val allowedHosts = listOf(
32
+ "app-assets.onekey.so",
33
+ "uni.onekey-asset.com",
34
+ "uni-test.onekey-asset.com",
35
+ "common.onekey-asset.com",
36
+ "asset.onekey-asset.com",
37
+ )
38
+ allowedHosts.forEach { host ->
39
+ val result = OneKeyTosUrl.optimized(
40
+ "https://$host/token.png",
41
+ 100,
42
+ 2f,
43
+ 1.1,
44
+ false,
45
+ )
46
+ assertTrue(result.contains("x-tos-process="))
47
+ assertTrue(result.contains("w_256"))
48
+ }
49
+ assertEquals(
50
+ "https://web.onekey-asset.com/token.png",
51
+ OneKeyTosUrl.optimized(
52
+ "https://web.onekey-asset.com/token.png",
53
+ 100,
54
+ 2f,
55
+ 1.1,
56
+ false,
57
+ ),
58
+ )
59
+ }
60
+
61
+ @Test
62
+ fun skipsProtectedQueriesAndUnsupportedMedia() {
63
+ val protectedKeys = listOf(
64
+ "expires", "policy", "signature", "token", "auth_key", "accesskeyid",
65
+ "ossaccesskeyid", "security-token", "x-amz-signature", "x-oss-signature",
66
+ "x-tos-process",
67
+ )
68
+ protectedKeys.forEach { key ->
69
+ val raw = "https://common.onekey-asset.com/token.png?$key=value"
70
+ assertEquals(raw, OneKeyTosUrl.optimized(raw, 100, 2f, 1.1, false))
71
+ }
72
+ listOf("svg", "mp4", "webm", "m4v", "mov", "avi").forEach { extension ->
73
+ val raw = "https://common.onekey-asset.com/media.$extension"
74
+ assertEquals(raw, OneKeyTosUrl.optimized(raw, 100, 2f, 1.1, false))
75
+ }
76
+ val customIdentity = "https://common.onekey-asset.com/token.png"
77
+ assertEquals(
78
+ customIdentity,
79
+ OneKeyTosUrl.optimized(customIdentity, 100, 2f, 1.1, true),
80
+ )
81
+ }
82
+ }