@amazon-devices/expo-image 2.0.1758683737

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 (289) hide show
  1. package/.eslintrc.js +2 -0
  2. package/CHANGELOG.md +285 -0
  3. package/CMakeLists.txt +91 -0
  4. package/LICENSE +7 -0
  5. package/README.kepler.md +123 -0
  6. package/README.md +54 -0
  7. package/THIRD_PARTY_NOTICES.txt +24 -0
  8. package/android/build.gradle +124 -0
  9. package/android/proguard-rules.pro +28 -0
  10. package/android/src/main/AndroidManifest.xml +15 -0
  11. package/android/src/main/java/expo/modules/image/Exceptions.kt +9 -0
  12. package/android/src/main/java/expo/modules/image/ExpoImageAppGlideModule.kt +19 -0
  13. package/android/src/main/java/expo/modules/image/ExpoImageModule.kt +192 -0
  14. package/android/src/main/java/expo/modules/image/ExpoImageView.kt +220 -0
  15. package/android/src/main/java/expo/modules/image/ExpoImageViewWrapper.kt +652 -0
  16. package/android/src/main/java/expo/modules/image/GlideModel.kt +73 -0
  17. package/android/src/main/java/expo/modules/image/ImageUtils.kt +50 -0
  18. package/android/src/main/java/expo/modules/image/ImageViewWrapperTarget.kt +313 -0
  19. package/android/src/main/java/expo/modules/image/ResourceIdHelper.kt +47 -0
  20. package/android/src/main/java/expo/modules/image/ThumbnailRequestCoordinatorExtension.kt +21 -0
  21. package/android/src/main/java/expo/modules/image/YogaUtils.kt +20 -0
  22. package/android/src/main/java/expo/modules/image/blurhash/BlurHashFetcher.kt +32 -0
  23. package/android/src/main/java/expo/modules/image/blurhash/BlurhashDecoder.kt +189 -0
  24. package/android/src/main/java/expo/modules/image/blurhash/BlurhashModelLoader.kt +30 -0
  25. package/android/src/main/java/expo/modules/image/blurhash/BlurhashModelLoaderFactory.kt +14 -0
  26. package/android/src/main/java/expo/modules/image/blurhash/BlurhashModule.kt +17 -0
  27. package/android/src/main/java/expo/modules/image/dataurls/Base64DataFetcher.kt +27 -0
  28. package/android/src/main/java/expo/modules/image/dataurls/Base64ModelLoader.kt +24 -0
  29. package/android/src/main/java/expo/modules/image/dataurls/Base64ModelLoaderFactory.kt +12 -0
  30. package/android/src/main/java/expo/modules/image/dataurls/Base64Module.kt +16 -0
  31. package/android/src/main/java/expo/modules/image/drawing/OutlineProvider.kt +201 -0
  32. package/android/src/main/java/expo/modules/image/enums/ContentFit.kt +89 -0
  33. package/android/src/main/java/expo/modules/image/enums/ImageCacheType.kt +14 -0
  34. package/android/src/main/java/expo/modules/image/enums/Priority.kt +15 -0
  35. package/android/src/main/java/expo/modules/image/events/GlideRequestListener.kt +64 -0
  36. package/android/src/main/java/expo/modules/image/events/OkHttpProgressListener.kt +26 -0
  37. package/android/src/main/java/expo/modules/image/okhttp/ExpoImageOkHttpClientGlideModule.kt +80 -0
  38. package/android/src/main/java/expo/modules/image/okhttp/GlideUrlWrapperLoader.kt +55 -0
  39. package/android/src/main/java/expo/modules/image/records/CachePolicy.kt +10 -0
  40. package/android/src/main/java/expo/modules/image/records/ContentPosition.kt +89 -0
  41. package/android/src/main/java/expo/modules/image/records/ImageTransition.kt +8 -0
  42. package/android/src/main/java/expo/modules/image/records/SourceMap.kt +160 -0
  43. package/android/src/main/java/expo/modules/image/records/events.kt +25 -0
  44. package/android/src/main/java/expo/modules/image/svg/SVGDecoder.kt +33 -0
  45. package/android/src/main/java/expo/modules/image/svg/SVGDrawableTranscoder.kt +37 -0
  46. package/android/src/main/java/expo/modules/image/svg/SVGModule.kt +26 -0
  47. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashDecoder.kt +365 -0
  48. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashFetcher.kt +31 -0
  49. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoader.kt +26 -0
  50. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModelLoaderFactory.kt +14 -0
  51. package/android/src/main/java/expo/modules/image/thumbhash/ThumbhashModule.kt +17 -0
  52. package/build/ExpoImage.d.ts +15 -0
  53. package/build/ExpoImage.d.ts.map +1 -0
  54. package/build/ExpoImage.js +75 -0
  55. package/build/ExpoImage.js.map +1 -0
  56. package/build/ExpoImage.kepler.d.ts +23 -0
  57. package/build/ExpoImage.kepler.d.ts.map +1 -0
  58. package/build/ExpoImage.kepler.js +71 -0
  59. package/build/ExpoImage.kepler.js.map +1 -0
  60. package/build/ExpoImage.web.d.ts +9 -0
  61. package/build/ExpoImage.web.d.ts.map +1 -0
  62. package/build/ExpoImage.web.js +90 -0
  63. package/build/ExpoImage.web.js.map +1 -0
  64. package/build/ExpoImageModules.d.ts +3 -0
  65. package/build/ExpoImageModules.d.ts.map +1 -0
  66. package/build/ExpoImageModules.js +4 -0
  67. package/build/ExpoImageModules.js.map +1 -0
  68. package/build/ExpoImageModules.kepler.d.ts +6 -0
  69. package/build/ExpoImageModules.kepler.d.ts.map +1 -0
  70. package/build/ExpoImageModules.kepler.js +5 -0
  71. package/build/ExpoImageModules.kepler.js.map +1 -0
  72. package/build/Image.d.ts +30 -0
  73. package/build/Image.d.ts.map +1 -0
  74. package/build/Image.js +49 -0
  75. package/build/Image.js.map +1 -0
  76. package/build/Image.types.d.ts +374 -0
  77. package/build/Image.types.d.ts.map +1 -0
  78. package/build/Image.types.js +2 -0
  79. package/build/Image.types.js.map +1 -0
  80. package/build/ImageBackground.d.ts +6 -0
  81. package/build/ImageBackground.d.ts.map +1 -0
  82. package/build/ImageBackground.js +13 -0
  83. package/build/ImageBackground.js.map +1 -0
  84. package/build/ImageViewNativeComponent.d.ts +20 -0
  85. package/build/ImageViewNativeComponent.d.ts.map +1 -0
  86. package/build/ImageViewNativeComponent.js +42 -0
  87. package/build/ImageViewNativeComponent.js.map +1 -0
  88. package/build/NativeImageLoaderKepler.d.ts +18 -0
  89. package/build/NativeImageLoaderKepler.d.ts.map +1 -0
  90. package/build/NativeImageLoaderKepler.js +3 -0
  91. package/build/NativeImageLoaderKepler.js.map +1 -0
  92. package/build/fabric/registerNativeComponent.d.ts +15 -0
  93. package/build/fabric/registerNativeComponent.d.ts.map +1 -0
  94. package/build/fabric/registerNativeComponent.js +47 -0
  95. package/build/fabric/registerNativeComponent.js.map +1 -0
  96. package/build/index.d.ts +4 -0
  97. package/build/index.d.ts.map +1 -0
  98. package/build/index.js +4 -0
  99. package/build/index.js.map +1 -0
  100. package/build/utils/AssetSourceResolver.web.d.ts +24 -0
  101. package/build/utils/AssetSourceResolver.web.d.ts.map +1 -0
  102. package/build/utils/AssetSourceResolver.web.js +67 -0
  103. package/build/utils/AssetSourceResolver.web.js.map +1 -0
  104. package/build/utils/blurhash/base83.d.ts +3 -0
  105. package/build/utils/blurhash/base83.d.ts.map +1 -0
  106. package/build/utils/blurhash/base83.js +103 -0
  107. package/build/utils/blurhash/base83.js.map +1 -0
  108. package/build/utils/blurhash/decode.d.ts +7 -0
  109. package/build/utils/blurhash/decode.d.ts.map +1 -0
  110. package/build/utils/blurhash/decode.js +92 -0
  111. package/build/utils/blurhash/decode.js.map +1 -0
  112. package/build/utils/blurhash/error.d.ts +4 -0
  113. package/build/utils/blurhash/error.d.ts.map +1 -0
  114. package/build/utils/blurhash/error.js +8 -0
  115. package/build/utils/blurhash/error.js.map +1 -0
  116. package/build/utils/blurhash/useBlurhash.d.ts +8 -0
  117. package/build/utils/blurhash/useBlurhash.d.ts.map +1 -0
  118. package/build/utils/blurhash/useBlurhash.js +64 -0
  119. package/build/utils/blurhash/useBlurhash.js.map +1 -0
  120. package/build/utils/blurhash/utils.d.ts +5 -0
  121. package/build/utils/blurhash/utils.d.ts.map +1 -0
  122. package/build/utils/blurhash/utils.js +21 -0
  123. package/build/utils/blurhash/utils.js.map +1 -0
  124. package/build/utils/resolveAssetSource.d.ts +3 -0
  125. package/build/utils/resolveAssetSource.d.ts.map +1 -0
  126. package/build/utils/resolveAssetSource.js +3 -0
  127. package/build/utils/resolveAssetSource.js.map +1 -0
  128. package/build/utils/resolveAssetSource.web.d.ts +9 -0
  129. package/build/utils/resolveAssetSource.web.d.ts.map +1 -0
  130. package/build/utils/resolveAssetSource.web.js +31 -0
  131. package/build/utils/resolveAssetSource.web.js.map +1 -0
  132. package/build/utils/resolveHashString.d.ts +14 -0
  133. package/build/utils/resolveHashString.d.ts.map +1 -0
  134. package/build/utils/resolveHashString.js +31 -0
  135. package/build/utils/resolveHashString.js.map +1 -0
  136. package/build/utils/resolveHashString.web.d.ts +20 -0
  137. package/build/utils/resolveHashString.web.d.ts.map +1 -0
  138. package/build/utils/resolveHashString.web.js +31 -0
  139. package/build/utils/resolveHashString.web.js.map +1 -0
  140. package/build/utils/resolveSources.d.ts +8 -0
  141. package/build/utils/resolveSources.d.ts.map +1 -0
  142. package/build/utils/resolveSources.js +45 -0
  143. package/build/utils/resolveSources.js.map +1 -0
  144. package/build/utils/thumbhash/thumbhash.d.ts +66 -0
  145. package/build/utils/thumbhash/thumbhash.d.ts.map +1 -0
  146. package/build/utils/thumbhash/thumbhash.js +338 -0
  147. package/build/utils/thumbhash/thumbhash.js.map +1 -0
  148. package/build/utils.d.ts +17 -0
  149. package/build/utils.d.ts.map +1 -0
  150. package/build/utils.js +85 -0
  151. package/build/utils.js.map +1 -0
  152. package/build/web/AnimationManager.d.ts +37 -0
  153. package/build/web/AnimationManager.d.ts.map +1 -0
  154. package/build/web/AnimationManager.js +155 -0
  155. package/build/web/AnimationManager.js.map +1 -0
  156. package/build/web/ColorTintFilter.d.ts +12 -0
  157. package/build/web/ColorTintFilter.d.ts.map +1 -0
  158. package/build/web/ColorTintFilter.js +30 -0
  159. package/build/web/ColorTintFilter.js.map +1 -0
  160. package/build/web/ImageWrapper.d.ts +5 -0
  161. package/build/web/ImageWrapper.d.ts.map +1 -0
  162. package/build/web/ImageWrapper.js +44 -0
  163. package/build/web/ImageWrapper.js.map +1 -0
  164. package/build/web/ImageWrapper.types.d.ts +29 -0
  165. package/build/web/ImageWrapper.types.d.ts.map +1 -0
  166. package/build/web/ImageWrapper.types.js +2 -0
  167. package/build/web/ImageWrapper.types.js.map +1 -0
  168. package/build/web/getImageWrapperEventHandler.d.ts +8 -0
  169. package/build/web/getImageWrapperEventHandler.d.ts.map +1 -0
  170. package/build/web/getImageWrapperEventHandler.js +25 -0
  171. package/build/web/getImageWrapperEventHandler.js.map +1 -0
  172. package/build/web/hooks.d.ts +13 -0
  173. package/build/web/hooks.d.ts.map +1 -0
  174. package/build/web/hooks.js +58 -0
  175. package/build/web/hooks.js.map +1 -0
  176. package/build/web/imageStyles.d.ts +2 -0
  177. package/build/web/imageStyles.d.ts.map +1 -0
  178. package/build/web/imageStyles.js +137 -0
  179. package/build/web/imageStyles.js.map +1 -0
  180. package/build/web/positioning.d.ts +11 -0
  181. package/build/web/positioning.d.ts.map +1 -0
  182. package/build/web/positioning.js +35 -0
  183. package/build/web/positioning.js.map +1 -0
  184. package/build/web/useSourceSelection.d.ts +14 -0
  185. package/build/web/useSourceSelection.d.ts.map +1 -0
  186. package/build/web/useSourceSelection.js +98 -0
  187. package/build/web/useSourceSelection.js.map +1 -0
  188. package/expo-module.config.json +9 -0
  189. package/ios/Blurhash.swift +326 -0
  190. package/ios/BlurhashLoader.swift +52 -0
  191. package/ios/ContentFit.swift +55 -0
  192. package/ios/ContentPosition.swift +108 -0
  193. package/ios/ExpoImage.podspec +30 -0
  194. package/ios/ImageCachePolicy.swift +24 -0
  195. package/ios/ImageCacheType.swift +21 -0
  196. package/ios/ImageModule.swift +127 -0
  197. package/ios/ImagePriority.swift +22 -0
  198. package/ios/ImageSource.swift +44 -0
  199. package/ios/ImageTransition.swift +67 -0
  200. package/ios/ImageUtils.swift +227 -0
  201. package/ios/ImageView.swift +469 -0
  202. package/ios/Loaders/PhotoLibraryAssetLoader.swift +146 -0
  203. package/ios/Thumbhash.swift +550 -0
  204. package/ios/ThumbhashLoader.swift +53 -0
  205. package/jest.config.js +3 -0
  206. package/kepler/ExpoImage.cpp +63 -0
  207. package/kepler/ExpoImage.h +29 -0
  208. package/kepler/ExpoImageCacheHandler.cpp +15 -0
  209. package/kepler/ExpoImageCacheHandler.h +28 -0
  210. package/kepler/ExpoImageComponent.cpp +579 -0
  211. package/kepler/ExpoImageComponent.h +199 -0
  212. package/kepler/ImageWrapper/BlurhashImageWrapper.cpp +37 -0
  213. package/kepler/ImageWrapper/BlurhashImageWrapper.h +23 -0
  214. package/kepler/ImageWrapper/CacheImageWrapper.cpp +80 -0
  215. package/kepler/ImageWrapper/CacheImageWrapper.h +41 -0
  216. package/kepler/ImageWrapper/HashImageWrapper.cpp +21 -0
  217. package/kepler/ImageWrapper/HashImageWrapper.h +22 -0
  218. package/kepler/ImageWrapper/ImageWrapper.cpp +24 -0
  219. package/kepler/ImageWrapper/ImageWrapper.h +33 -0
  220. package/kepler/ImageWrapper/LocalImageWrapper.cpp +61 -0
  221. package/kepler/ImageWrapper/LocalImageWrapper.h +30 -0
  222. package/kepler/ImageWrapper/ThumbhashImageWrapper.cpp +33 -0
  223. package/kepler/ImageWrapper/ThumbhashImageWrapper.h +20 -0
  224. package/kepler/KeplerImageViewComponent.h +64 -0
  225. package/kepler/KeplerInit.cpp +35 -0
  226. package/kepler/blurhash/common.h +32 -0
  227. package/kepler/blurhash/decode.cpp +187 -0
  228. package/kepler/blurhash/decode.h +58 -0
  229. package/kepler/contentposition/ImageContentPosition.cpp +86 -0
  230. package/kepler/contentposition/ImageContentPosition.h +48 -0
  231. package/kepler/enums/ImageContentFit.h +17 -0
  232. package/kepler/enums/ImagePriority.h +17 -0
  233. package/kepler/managers/ExpoImageManager.cpp +38 -0
  234. package/kepler/managers/ExpoImageManager.h +23 -0
  235. package/kepler/test/ExpoImage.cpp +73 -0
  236. package/kepler/test/ExpoImageComponent.cpp +290 -0
  237. package/kepler/test/blurhash/decode.cpp +39 -0
  238. package/kepler/test/contentposition/ImageContentPosition.cpp +100 -0
  239. package/kepler/test/managers/ExpoImageManager.cpp +39 -0
  240. package/kepler/test/thumbhash/decode.cpp +18 -0
  241. package/kepler/test/utils/LoaderDelegate.cpp +130 -0
  242. package/kepler/thumbhash/decode.cpp +166 -0
  243. package/kepler/thumbhash/decode.h +22 -0
  244. package/kepler/utils/PlaceholderImageLoaderDelegate.cpp +26 -0
  245. package/kepler/utils/PlaceholderImageLoaderDelegate.h +26 -0
  246. package/kepler/utils/PrefetchImageLoaderDelegate.cpp +55 -0
  247. package/kepler/utils/PrefetchImageLoaderDelegate.h +35 -0
  248. package/kepler/utils/SourceImageLoaderDelegate.cpp +47 -0
  249. package/kepler/utils/SourceImageLoaderDelegate.h +42 -0
  250. package/kepler/utils/SourceImageLoaderDelegateEmitter.cpp +15 -0
  251. package/kepler/utils/SourceImageLoaderDelegateEmitter.h +28 -0
  252. package/kepler-compatibility.json +8 -0
  253. package/package.json +1 -0
  254. package/react-native.config.js +22 -0
  255. package/src/ExpoImage.kepler.tsx +119 -0
  256. package/src/ExpoImage.tsx +119 -0
  257. package/src/ExpoImage.web.tsx +155 -0
  258. package/src/ExpoImageModules.kepler.ts +7 -0
  259. package/src/ExpoImageModules.ts +4 -0
  260. package/src/Image.tsx +82 -0
  261. package/src/Image.types.ts +450 -0
  262. package/src/ImageBackground.tsx +18 -0
  263. package/src/ImageViewNativeComponent.ts +59 -0
  264. package/src/NativeImageLoaderKepler.ts +22 -0
  265. package/src/fabric/registerNativeComponent.ts +70 -0
  266. package/src/index.ts +3 -0
  267. package/src/utils/AssetSourceResolver.web.ts +88 -0
  268. package/src/utils/blurhash/base83.ts +104 -0
  269. package/src/utils/blurhash/decode.ts +114 -0
  270. package/src/utils/blurhash/error.ts +7 -0
  271. package/src/utils/blurhash/useBlurhash.tsx +81 -0
  272. package/src/utils/blurhash/utils.ts +21 -0
  273. package/src/utils/resolveAssetSource.tsx +3 -0
  274. package/src/utils/resolveAssetSource.web.ts +40 -0
  275. package/src/utils/resolveHashString.tsx +36 -0
  276. package/src/utils/resolveHashString.web.tsx +33 -0
  277. package/src/utils/resolveSources.tsx +48 -0
  278. package/src/utils/thumbhash/thumbhash.ts +387 -0
  279. package/src/utils.ts +109 -0
  280. package/src/web/AnimationManager.tsx +224 -0
  281. package/src/web/ColorTintFilter.tsx +36 -0
  282. package/src/web/ImageWrapper.tsx +85 -0
  283. package/src/web/ImageWrapper.types.ts +36 -0
  284. package/src/web/getImageWrapperEventHandler.ts +29 -0
  285. package/src/web/hooks.ts +72 -0
  286. package/src/web/imageStyles.tsx +136 -0
  287. package/src/web/positioning.ts +48 -0
  288. package/src/web/useSourceSelection.ts +151 -0
  289. package/tsconfig.json +9 -0
@@ -0,0 +1,64 @@
1
+ package expo.modules.image.events
2
+
3
+ import android.graphics.drawable.Drawable
4
+ import android.util.Log
5
+ import com.bumptech.glide.load.DataSource
6
+ import com.bumptech.glide.load.engine.GlideException
7
+ import com.bumptech.glide.request.RequestListener
8
+ import com.bumptech.glide.request.target.Target
9
+ import expo.modules.image.ExpoImageViewWrapper
10
+ import expo.modules.image.enums.ImageCacheType
11
+ import expo.modules.image.records.ImageErrorEvent
12
+ import expo.modules.image.records.ImageLoadEvent
13
+ import expo.modules.image.records.ImageSource
14
+ import java.lang.ref.WeakReference
15
+ import java.util.*
16
+
17
+ class GlideRequestListener(
18
+ private val expoImageViewWrapper: WeakReference<ExpoImageViewWrapper>
19
+ ) : RequestListener<Drawable> {
20
+ override fun onLoadFailed(
21
+ e: GlideException?,
22
+ model: Any?,
23
+ target: Target<Drawable>,
24
+ isFirstResource: Boolean
25
+ ): Boolean {
26
+ val errorMessage = e
27
+ ?.message
28
+ // Glide always append that line to the end of the message.
29
+ // It's not possible to call `logRootCauses` from the JS, so we decided to remove it.
30
+ ?.removeSuffix("\n call GlideException#logRootCauses(String) for more detail")
31
+ ?: "Unknown error"
32
+
33
+ expoImageViewWrapper
34
+ .get()
35
+ ?.onError
36
+ ?.invoke(ImageErrorEvent(errorMessage))
37
+
38
+ Log.e("ExpoImage", errorMessage)
39
+ e?.logRootCauses("ExpoImage")
40
+ return false
41
+ }
42
+
43
+ override fun onResourceReady(
44
+ resource: Drawable,
45
+ model: Any,
46
+ target: Target<Drawable>,
47
+ dataSource: DataSource,
48
+ isFirstResource: Boolean
49
+ ): Boolean {
50
+ expoImageViewWrapper.get()?.onLoad?.invoke(
51
+ ImageLoadEvent(
52
+ cacheType = ImageCacheType.fromNativeValue(dataSource).name.lowercase(Locale.getDefault()),
53
+ source = ImageSource(
54
+ url = model.toString(),
55
+ width = resource.intrinsicWidth,
56
+ height = resource.intrinsicHeight,
57
+ mediaType = null // TODO(@lukmccall): add mediaType
58
+ )
59
+ )
60
+ )
61
+
62
+ return false
63
+ }
64
+ }
@@ -0,0 +1,26 @@
1
+ package expo.modules.image.events
2
+
3
+ import com.facebook.react.modules.network.ProgressListener
4
+ import expo.modules.image.ExpoImageViewWrapper
5
+ import expo.modules.image.records.ImageProgressEvent
6
+ import java.lang.ref.WeakReference
7
+
8
+ class OkHttpProgressListener(
9
+ private val expoImageViewWrapper: WeakReference<ExpoImageViewWrapper>
10
+ ) : ProgressListener {
11
+ override fun onProgress(bytesWritten: Long, contentLength: Long, done: Boolean) {
12
+ // OkHttp calls that function twice at the end - when the last byte was downloaded with done set to false,
13
+ // and also shortly after, with done set to true. In both cases, the bytesWritten and the contentLength are equal.
14
+ // We want to avoid sending two same events to JS, that's why we return when done is set to true.
15
+ if (contentLength <= 0 || done) {
16
+ return
17
+ }
18
+
19
+ expoImageViewWrapper.get()?.onProgress?.invoke(
20
+ ImageProgressEvent(
21
+ loaded = bytesWritten.toInt(),
22
+ total = contentLength.toInt()
23
+ )
24
+ )
25
+ }
26
+ }
@@ -0,0 +1,80 @@
1
+ package expo.modules.image.okhttp
2
+
3
+ import android.content.Context
4
+ import com.bumptech.glide.Glide
5
+ import com.bumptech.glide.Registry
6
+ import com.bumptech.glide.annotation.GlideModule
7
+ import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
8
+ import com.bumptech.glide.load.model.GlideUrl
9
+ import com.bumptech.glide.load.model.Headers
10
+ import com.bumptech.glide.module.LibraryGlideModule
11
+ import expo.modules.image.events.OkHttpProgressListener
12
+ import okhttp3.OkHttpClient
13
+ import java.io.InputStream
14
+
15
+ /**
16
+ * GlideUrl with custom cache key.
17
+ * It wraps the base implementation and overrides logic behind cache key and when two
18
+ * objects are equal. Typically, Glide uses the only cache key to compare objects.
19
+ * It won't suit our use case. We want to make custom cache key transparent and
20
+ * not use it to compare objects.
21
+ */
22
+ class GlideUrlWithCustomCacheKey(
23
+ uri: String?,
24
+ headers: Headers?,
25
+ private val cacheKey: String
26
+ ) : GlideUrl(uri, headers) {
27
+ /**
28
+ * Cached hash code value
29
+ */
30
+ private var hashCode = 0
31
+
32
+ /**
33
+ * @return a super cache key from [GlideUrl]
34
+ */
35
+ private fun getBaseCacheKey(): String = super.getCacheKey()
36
+
37
+ override fun getCacheKey(): String = cacheKey
38
+
39
+ // Mostly copied from GlideUrl::equal
40
+ override fun equals(other: Any?): Boolean {
41
+ if (other is GlideUrlWithCustomCacheKey) {
42
+ return getBaseCacheKey() == other.getBaseCacheKey() && headers.equals(other.headers)
43
+ } else if (other is GlideUrl) {
44
+ return getBaseCacheKey() == other.cacheKey && headers.equals(other.headers)
45
+ }
46
+ return false
47
+ }
48
+
49
+ // Mostly copied from GlideUrl::hashCode
50
+ override fun hashCode(): Int {
51
+ if (hashCode == 0) {
52
+ hashCode = getBaseCacheKey().hashCode()
53
+ hashCode = 31 * hashCode + headers.hashCode()
54
+ }
55
+ return hashCode
56
+ }
57
+ }
58
+
59
+ /**
60
+ * To connect listener with the request we have to create custom model.
61
+ * In that way, we're passing more information to the final data loader.
62
+ */
63
+ data class GlideUrlWrapper(val glideUrl: GlideUrl) {
64
+ var progressListener: OkHttpProgressListener? = null
65
+
66
+ override fun toString(): String {
67
+ return glideUrl.toString()
68
+ }
69
+ }
70
+
71
+ @GlideModule
72
+ class ExpoImageOkHttpClientGlideModule : LibraryGlideModule() {
73
+ override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
74
+ val client = OkHttpClient()
75
+ // We don't use the `GlideUrl` directly but we want to replace the default okhttp loader anyway
76
+ // to make sure that the app will use only one client.
77
+ registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(client))
78
+ registry.prepend(GlideUrlWrapper::class.java, InputStream::class.java, GlideUrlWrapperLoader.Factory(client))
79
+ }
80
+ }
@@ -0,0 +1,55 @@
1
+ package expo.modules.image.okhttp
2
+
3
+ import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
4
+ import com.bumptech.glide.load.Options
5
+ import com.bumptech.glide.load.model.ModelLoader
6
+ import com.bumptech.glide.load.model.ModelLoaderFactory
7
+ import com.bumptech.glide.load.model.MultiModelLoaderFactory
8
+ import com.facebook.react.modules.network.ProgressResponseBody
9
+ import okhttp3.Interceptor
10
+ import okhttp3.OkHttpClient
11
+ import java.io.InputStream
12
+
13
+ class GlideUrlWrapperLoader(
14
+ private val commonClient: OkHttpClient
15
+ ) : ModelLoader<GlideUrlWrapper, InputStream> {
16
+ override fun buildLoadData(
17
+ model: GlideUrlWrapper,
18
+ width: Int,
19
+ height: Int,
20
+ options: Options
21
+ ): ModelLoader.LoadData<InputStream>? {
22
+ val loader = OkHttpUrlLoader(
23
+ commonClient
24
+ .newBuilder()
25
+ .addInterceptor(
26
+ Interceptor { chain ->
27
+ val originalResponse = chain.proceed(chain.request())
28
+ originalResponse
29
+ .newBuilder()
30
+ .body(
31
+ ProgressResponseBody(originalResponse.body) { bytesWritten, contentLength, done ->
32
+ model.progressListener?.onProgress(bytesWritten, contentLength, done)
33
+ }
34
+ )
35
+ .build()
36
+ }
37
+ )
38
+ .build()
39
+ )
40
+
41
+ return loader.buildLoadData(model.glideUrl, width, height, options)
42
+ }
43
+
44
+ // The default http loader always returns true.
45
+ override fun handles(model: GlideUrlWrapper): Boolean = true
46
+
47
+ class Factory(
48
+ private val commonClient: OkHttpClient
49
+ ) : ModelLoaderFactory<GlideUrlWrapper, InputStream> {
50
+ override fun build(multiFactory: MultiModelLoaderFactory): ModelLoader<GlideUrlWrapper, InputStream> =
51
+ GlideUrlWrapperLoader(commonClient)
52
+
53
+ override fun teardown() = Unit
54
+ }
55
+ }
@@ -0,0 +1,10 @@
1
+ package expo.modules.image.records
2
+
3
+ import expo.modules.kotlin.types.Enumerable
4
+
5
+ enum class CachePolicy(val value: String) : Enumerable {
6
+ NONE("none"),
7
+ DISK("disk"),
8
+ MEMORY("memory"),
9
+ MEMORY_AND_DISK("memory-disk")
10
+ }
@@ -0,0 +1,89 @@
1
+ package expo.modules.image.records
2
+
3
+ import android.graphics.Matrix
4
+ import android.graphics.RectF
5
+ import expo.modules.image.calcXTranslation
6
+ import expo.modules.image.calcYTranslation
7
+ import expo.modules.kotlin.records.Field
8
+ import expo.modules.kotlin.records.Record
9
+
10
+ /**
11
+ * Represents a position value that might be either `Double` or `String`.
12
+ * TODO(@lukmccall): Use `Either` instead of `Any`
13
+ */
14
+ typealias ContentPositionValue = Any
15
+
16
+ private typealias CalcAxisOffset = (
17
+ value: Float,
18
+ imageRect: RectF,
19
+ viewRect: RectF,
20
+ isPercentage: Boolean,
21
+ isReverse: Boolean
22
+ ) -> Float
23
+
24
+ class ContentPosition : Record {
25
+ @Field
26
+ val top: ContentPositionValue? = null
27
+
28
+ @Field
29
+ val bottom: ContentPositionValue? = null
30
+
31
+ @Field
32
+ val right: ContentPositionValue? = null
33
+
34
+ @Field
35
+ val left: ContentPositionValue? = null
36
+
37
+ private fun ContentPositionValue?.calcOffset(
38
+ isReverse: Boolean,
39
+ imageRect: RectF,
40
+ viewRect: RectF,
41
+ calcAxisOffset: CalcAxisOffset
42
+ ): Float? {
43
+ if (this == null) {
44
+ return null
45
+ }
46
+
47
+ return if (this is Double) {
48
+ val value = this.toFloat()
49
+ calcAxisOffset(value, imageRect, viewRect, false, isReverse)
50
+ } else {
51
+ val value = this as String
52
+
53
+ if (value == "center") {
54
+ calcAxisOffset(50f, imageRect, viewRect, true, isReverse)
55
+ } else {
56
+ calcAxisOffset(value.removeSuffix("%").toFloat(), imageRect, viewRect, true, isReverse)
57
+ }
58
+ }
59
+ }
60
+
61
+ private fun offsetX(
62
+ imageRect: RectF,
63
+ viewRect: RectF
64
+ ): Float {
65
+ return left.calcOffset(false, imageRect, viewRect, ::calcXTranslation)
66
+ ?: right.calcOffset(true, imageRect, viewRect, ::calcXTranslation)
67
+ ?: calcXTranslation(50f, imageRect, viewRect, isPercentage = true) // default value
68
+ }
69
+
70
+ private fun offsetY(
71
+ imageRect: RectF,
72
+ viewRect: RectF
73
+ ): Float {
74
+ return top.calcOffset(false, imageRect, viewRect, ::calcYTranslation)
75
+ ?: bottom.calcOffset(true, imageRect, viewRect, ::calcYTranslation)
76
+ ?: calcYTranslation(50f, imageRect, viewRect, isPercentage = true) // default value
77
+ }
78
+
79
+ internal fun apply(to: Matrix, imageRect: RectF, viewRect: RectF) {
80
+ val xOffset = offsetX(imageRect, viewRect)
81
+ val yOffset = offsetY(imageRect, viewRect)
82
+
83
+ to.postTranslate(xOffset, yOffset)
84
+ }
85
+
86
+ companion object {
87
+ val center = ContentPosition()
88
+ }
89
+ }
@@ -0,0 +1,8 @@
1
+ package expo.modules.image.records
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+
6
+ data class ImageTransition(
7
+ @Field val duration: Int = 0
8
+ ) : Record
@@ -0,0 +1,160 @@
1
+ package expo.modules.image.records
2
+
3
+ import android.content.Context
4
+ import android.net.Uri
5
+ import com.bumptech.glide.load.model.GlideUrl
6
+ import com.bumptech.glide.load.model.Headers
7
+ import com.bumptech.glide.load.model.LazyHeaders
8
+ import com.bumptech.glide.request.RequestOptions
9
+ import com.bumptech.glide.signature.ApplicationVersionSignature
10
+ import expo.modules.image.GlideBlurhashModel
11
+ import expo.modules.image.GlideModel
12
+ import expo.modules.image.GlideRawModel
13
+ import expo.modules.image.GlideThumbhashModel
14
+ import expo.modules.image.GlideUriModel
15
+ import expo.modules.image.GlideUrlModel
16
+ import expo.modules.image.ResourceIdHelper
17
+ import expo.modules.image.okhttp.GlideUrlWithCustomCacheKey
18
+ import expo.modules.kotlin.records.Field
19
+ import expo.modules.kotlin.records.Record
20
+
21
+ data class SourceMap(
22
+ @Field val uri: String? = null,
23
+ @Field val width: Int = 0,
24
+ @Field val height: Int = 0,
25
+ @Field val scale: Double = 1.0,
26
+ @Field val headers: Map<String, String>? = null,
27
+ @Field val cacheKey: String? = null
28
+ ) : Record {
29
+ private var parsedUri: Uri? = null
30
+
31
+ private fun isDataUrl() = parsedUri?.scheme?.startsWith("data") ?: false
32
+
33
+ private fun isContentUrl() = parsedUri?.scheme?.startsWith("content") ?: false
34
+
35
+ private fun isResourceUri() = parsedUri?.scheme?.startsWith("android.resource") ?: false
36
+
37
+ private fun isLocalResourceUri() = parsedUri?.scheme?.startsWith("res") ?: false
38
+
39
+ private fun isLocalFileUri() = parsedUri?.scheme?.startsWith("file") ?: false
40
+
41
+ private fun isSvg() = parsedUri?.toString()?.substring(parsedUri.toString().lastIndexOf('.'))?.startsWith(".svg") ?: false
42
+
43
+ fun isBlurhash() = parsedUri?.scheme?.startsWith("blurhash") ?: false
44
+
45
+ fun isThumbhash() = parsedUri?.scheme?.startsWith("thumbhash") ?: false
46
+
47
+ internal fun createGlideModel(context: Context): GlideModel? {
48
+ if (uri.isNullOrBlank()) {
49
+ return null
50
+ }
51
+
52
+ if (parsedUri == null) {
53
+ parsedUri = computeUri(context)
54
+ }
55
+
56
+ if (isContentUrl() || isDataUrl()) {
57
+ return GlideRawModel(uri)
58
+ }
59
+
60
+ if (isBlurhash()) {
61
+ return GlideBlurhashModel(
62
+ parsedUri!!,
63
+ width,
64
+ height
65
+ )
66
+ }
67
+
68
+ if (isThumbhash()) {
69
+ return GlideThumbhashModel(
70
+ parsedUri!!
71
+ )
72
+ }
73
+
74
+ if (isResourceUri()) {
75
+ return GlideUriModel(parsedUri!!)
76
+ }
77
+
78
+ if (isLocalResourceUri()) {
79
+ return GlideUriModel(
80
+ // Convert `res:/` scheme to `android.resource://`.
81
+ // Otherwise, glide can't understand the Uri.
82
+ Uri.parse(parsedUri!!.toString().replace("res:/", "android.resource://" + context.packageName + "/"))
83
+ )
84
+ }
85
+
86
+ if (isLocalFileUri()) {
87
+ return GlideRawModel(parsedUri!!.toString())
88
+ }
89
+
90
+ val glideUrl = if (cacheKey == null) {
91
+ GlideUrl(uri, getCustomHeaders())
92
+ } else {
93
+ GlideUrlWithCustomCacheKey(uri, getCustomHeaders(), cacheKey)
94
+ }
95
+ return GlideUrlModel(glideUrl)
96
+ }
97
+
98
+ internal fun createOptions(context: Context): RequestOptions {
99
+ return RequestOptions()
100
+ .apply {
101
+ if (parsedUri == null) {
102
+ parsedUri = computeUri(context)
103
+ }
104
+
105
+ // Override the size for local assets (apart from SVGs). This ensures that
106
+ // resizeMode "center" displays the image in the correct size.
107
+ if (width != 0 && height != 0 && !isSvg()) {
108
+ override((width * scale).toInt(), (height * scale).toInt())
109
+ }
110
+
111
+ if (isResourceUri()) {
112
+ // Every local resource (drawable) in Android has its own unique numeric id, which are
113
+ // generated at build time. Although these ids are unique, they are not guaranteed unique
114
+ // across builds. The underlying glide implementation caches these resources. To make
115
+ // sure the cache does not return the wrong image, we should clear the cache when the
116
+ // application version changes.
117
+ apply(
118
+ RequestOptions.signatureOf(ApplicationVersionSignature.obtain(context))
119
+ )
120
+ }
121
+ }
122
+ }
123
+
124
+ private fun getCustomHeaders(): Headers {
125
+ if (headers == null) {
126
+ return LazyHeaders.DEFAULT
127
+ }
128
+
129
+ return LazyHeaders
130
+ .Builder()
131
+ .apply {
132
+ headers.forEach { (key, value) ->
133
+ addHeader(key, value)
134
+ }
135
+ }
136
+ .build()
137
+ }
138
+
139
+ private fun computeUri(context: Context): Uri? {
140
+ val stringUri = uri ?: return null
141
+ return try {
142
+ val uri: Uri = Uri.parse(stringUri)
143
+ // Verify scheme is set, so that relative uri (used by static resources) are not handled.
144
+ if (uri.scheme == null) {
145
+ computeLocalUri(stringUri, context)
146
+ } else {
147
+ uri
148
+ }
149
+ } catch (e: Exception) {
150
+ computeLocalUri(stringUri, context)
151
+ }
152
+ }
153
+
154
+ private fun computeLocalUri(stringUri: String, context: Context): Uri? {
155
+ return ResourceIdHelper.getResourceUri(context, stringUri)
156
+ }
157
+
158
+ internal val pixelCount: Double
159
+ get() = width * height * scale * scale
160
+ }
@@ -0,0 +1,25 @@
1
+ package expo.modules.image.records
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+
6
+ data class ImageSource(
7
+ @Field val url: String,
8
+ @Field val width: Int,
9
+ @Field val height: Int,
10
+ @Field val mediaType: String?
11
+ ) : Record
12
+
13
+ data class ImageLoadEvent(
14
+ @Field val cacheType: String,
15
+ @Field val source: ImageSource
16
+ ) : Record
17
+
18
+ data class ImageProgressEvent(
19
+ @Field val loaded: Int,
20
+ @Field val total: Int
21
+ ) : Record
22
+
23
+ data class ImageErrorEvent(
24
+ @Field val error: String
25
+ ) : Record
@@ -0,0 +1,33 @@
1
+ package expo.modules.image.svg
2
+
3
+ import com.bumptech.glide.load.Options
4
+ import com.bumptech.glide.load.ResourceDecoder
5
+ import com.bumptech.glide.load.engine.Resource
6
+ import com.bumptech.glide.load.resource.SimpleResource
7
+ import com.caverock.androidsvg.SVG
8
+ import com.caverock.androidsvg.SVGParseException
9
+ import java.io.IOException
10
+ import java.io.InputStream
11
+
12
+ /**
13
+ * Decodes an SVG internal representation from an [InputStream].
14
+ *
15
+ * Copied from https://github.com/bumptech/glide/blob/10acc31a16b4c1b5684f69e8de3117371dfa77a8/samples/svg/src/main/java/com/bumptech/glide/samples/svg/SvgDecoder.java
16
+ * and rewritten to Kotlin.
17
+ */
18
+ class SVGDecoder : ResourceDecoder<InputStream, SVG> {
19
+ // TODO: Can we tell?
20
+ override fun handles(source: InputStream, options: Options) = true
21
+
22
+ @Throws(IOException::class)
23
+ override fun decode(source: InputStream, width: Int, height: Int, options: Options): Resource<SVG>? {
24
+ return try {
25
+ val svg: SVG = SVG.getFromInputStream(source)
26
+ svg.documentWidth = width.toFloat()
27
+ svg.documentHeight = height.toFloat()
28
+ SimpleResource(svg)
29
+ } catch (ex: SVGParseException) {
30
+ throw IOException("Cannot load SVG from stream", ex)
31
+ }
32
+ }
33
+ }
@@ -0,0 +1,37 @@
1
+ package expo.modules.image.svg
2
+
3
+ import android.content.Context
4
+ import android.graphics.Bitmap
5
+ import android.graphics.Canvas
6
+ import android.graphics.Picture
7
+ import android.graphics.drawable.BitmapDrawable
8
+ import android.graphics.drawable.Drawable
9
+ import android.graphics.drawable.PictureDrawable
10
+ import com.bumptech.glide.load.Options
11
+ import com.bumptech.glide.load.engine.Resource
12
+ import com.bumptech.glide.load.resource.SimpleResource
13
+ import com.bumptech.glide.load.resource.transcode.ResourceTranscoder
14
+ import com.caverock.androidsvg.SVG
15
+
16
+ /**
17
+ * Convert the [SVG]'s internal representation to an Android-compatible one ([Picture]).
18
+ *
19
+ * Copied from https://github.com/bumptech/glide/blob/10acc31a16b4c1b5684f69e8de3117371dfa77a8/samples/svg/src/main/java/com/bumptech/glide/samples/svg/SvgDrawableTranscoder.java
20
+ * and rewritten to Kotlin.
21
+ */
22
+ class SVGDrawableTranscoder(val context: Context) : ResourceTranscoder<SVG?, Drawable> {
23
+ override fun transcode(toTranscode: Resource<SVG?>, options: Options): Resource<Drawable> {
24
+ val picture = toTranscode.get().renderToPicture()
25
+ val drawable = PictureDrawable(picture)
26
+ val width = drawable.intrinsicWidth
27
+ val height = drawable.intrinsicHeight
28
+
29
+ val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
30
+ val canvas = Canvas(bitmap)
31
+ drawable.setBounds(0, 0, canvas.width, canvas.height)
32
+ drawable.draw(canvas)
33
+
34
+ val newDrawable = BitmapDrawable(context.resources, bitmap)
35
+ return SimpleResource(newDrawable)
36
+ }
37
+ }
@@ -0,0 +1,26 @@
1
+ package expo.modules.image.svg
2
+
3
+ import android.content.Context
4
+ import android.graphics.drawable.Drawable
5
+ import com.bumptech.glide.Glide
6
+ import com.bumptech.glide.Registry
7
+ import com.bumptech.glide.annotation.GlideModule
8
+ import com.bumptech.glide.module.LibraryGlideModule
9
+ import com.caverock.androidsvg.SVG
10
+ import java.io.InputStream
11
+
12
+ /**
13
+ * [LibraryGlideModule] registering support for SVG to Glide.
14
+ *
15
+ * Copied from https://github.com/bumptech/glide/blob/10acc31a16b4c1b5684f69e8de3117371dfa77a8/samples/svg/src/main/java/com/bumptech/glide/samples/svg/SvgModule.java
16
+ * and rewritten to Kotlin.
17
+ */
18
+ @GlideModule
19
+ class SVGModule : LibraryGlideModule() {
20
+ override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
21
+ super.registerComponents(context, glide, registry)
22
+ registry
23
+ .append(InputStream::class.java, SVG::class.java, SVGDecoder())
24
+ .register(SVG::class.java, Drawable::class.java, SVGDrawableTranscoder(context))
25
+ }
26
+ }