react-native-ensys-camera-x 1.0.7

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 (506) hide show
  1. package/CustomCamera.podspec +20 -0
  2. package/LICENSE +20 -0
  3. package/README.md +833 -0
  4. package/android/build.gradle +135 -0
  5. package/android/consumer-rules.pro +47 -0
  6. package/android/src/main/AndroidManifest.xml +72 -0
  7. package/android/src/main/java/android/print/PdfPrint.java +51 -0
  8. package/android/src/main/java/com/customcamera/AnimatedImageViewManager.kt +199 -0
  9. package/android/src/main/java/com/customcamera/AudioPlayerView.kt +322 -0
  10. package/android/src/main/java/com/customcamera/AudioRecorder.kt +506 -0
  11. package/android/src/main/java/com/customcamera/BarcodeScannerActivity.kt +361 -0
  12. package/android/src/main/java/com/customcamera/BlurBackgroundView.kt +199 -0
  13. package/android/src/main/java/com/customcamera/CacheCleanupManager.kt +386 -0
  14. package/android/src/main/java/com/customcamera/CameraAspectRatioMode.kt +77 -0
  15. package/android/src/main/java/com/customcamera/CameraAsset.kt +211 -0
  16. package/android/src/main/java/com/customcamera/CameraTheme.kt +479 -0
  17. package/android/src/main/java/com/customcamera/CompressionCacheStore.kt +504 -0
  18. package/android/src/main/java/com/customcamera/CompressionFileNameUtils.kt +7 -0
  19. package/android/src/main/java/com/customcamera/CompressionProfile.kt +195 -0
  20. package/android/src/main/java/com/customcamera/CustomCameraActivity.kt +7291 -0
  21. package/android/src/main/java/com/customcamera/CustomCameraFileProvider.kt +9 -0
  22. package/android/src/main/java/com/customcamera/CustomCameraModule.kt +2479 -0
  23. package/android/src/main/java/com/customcamera/CustomCameraPackage.kt +53 -0
  24. package/android/src/main/java/com/customcamera/DisplayUtils.kt +9 -0
  25. package/android/src/main/java/com/customcamera/DocumentPreviewView.kt +846 -0
  26. package/android/src/main/java/com/customcamera/DrawingView.kt +214 -0
  27. package/android/src/main/java/com/customcamera/EmojiPickerData.kt +228 -0
  28. package/android/src/main/java/com/customcamera/FilterStripAdapter.kt +158 -0
  29. package/android/src/main/java/com/customcamera/GifSupport.kt +215 -0
  30. package/android/src/main/java/com/customcamera/GlobalAudioPlayer.kt +320 -0
  31. package/android/src/main/java/com/customcamera/ImageExporter.kt +1394 -0
  32. package/android/src/main/java/com/customcamera/ImageFormatSupport.kt +163 -0
  33. package/android/src/main/java/com/customcamera/Localization.kt +206 -0
  34. package/android/src/main/java/com/customcamera/MediaAdapters.kt +758 -0
  35. package/android/src/main/java/com/customcamera/MediaCacheManager.kt +782 -0
  36. package/android/src/main/java/com/customcamera/MediaExportFormats.kt +201 -0
  37. package/android/src/main/java/com/customcamera/MediaFileIo.kt +310 -0
  38. package/android/src/main/java/com/customcamera/MediaMetadataStore.kt +494 -0
  39. package/android/src/main/java/com/customcamera/MediaPreviewEditor.kt +14865 -0
  40. package/android/src/main/java/com/customcamera/MediaPublisher.kt +423 -0
  41. package/android/src/main/java/com/customcamera/MediaStorageWorkflow.kt +722 -0
  42. package/android/src/main/java/com/customcamera/MediaStoreRepository.kt +378 -0
  43. package/android/src/main/java/com/customcamera/MediaThumbnailView.kt +286 -0
  44. package/android/src/main/java/com/customcamera/MediaUtils.kt +37 -0
  45. package/android/src/main/java/com/customcamera/NativeVideoViewManager.kt +845 -0
  46. package/android/src/main/java/com/customcamera/OfficeDocumentConverter.kt +2791 -0
  47. package/android/src/main/java/com/customcamera/PendingCaptureCrops.kt +88 -0
  48. package/android/src/main/java/com/customcamera/PowerPointPreview.kt +4077 -0
  49. package/android/src/main/java/com/customcamera/RecentPickerStore.kt +74 -0
  50. package/android/src/main/java/com/customcamera/SecureFileEraser.kt +411 -0
  51. package/android/src/main/java/com/customcamera/SecureKeyModule.kt +88 -0
  52. package/android/src/main/java/com/customcamera/SecureMediaDeleter.kt +493 -0
  53. package/android/src/main/java/com/customcamera/StickerPickerData.kt +107 -0
  54. package/android/src/main/java/com/customcamera/SvgDrawable.kt +175 -0
  55. package/android/src/main/java/com/customcamera/SvgIconViewManager.kt +77 -0
  56. package/android/src/main/java/com/customcamera/TextEntryController.kt +553 -0
  57. package/android/src/main/java/com/customcamera/ThumbnailLoader.kt +455 -0
  58. package/android/src/main/java/com/customcamera/UploadService.kt +909 -0
  59. package/android/src/main/java/com/customcamera/VideoEditUtils.kt +210 -0
  60. package/android/src/main/java/com/customcamera/VideoRotationTranscoder.kt +1992 -0
  61. package/android/src/main/java/com/customcamera/gif/AnimatedGifEncoder.kt +473 -0
  62. package/android/src/main/java/com/customcamera/gif/GifDecoder.kt +690 -0
  63. package/android/src/main/java/com/customcamera/gif/GifFramesDrawable.kt +160 -0
  64. package/android/src/main/java/com/customcamera/gif/GifHttp.kt +112 -0
  65. package/android/src/main/java/com/customcamera/gif/GifImageLoader.kt +186 -0
  66. package/android/src/main/java/com/customcamera/gif/GifModels.kt +74 -0
  67. package/android/src/main/java/com/customcamera/gif/GifProvider.kt +49 -0
  68. package/android/src/main/java/com/customcamera/gif/GifProviders.kt +27 -0
  69. package/android/src/main/java/com/customcamera/gif/GifRepository.kt +124 -0
  70. package/android/src/main/java/com/customcamera/gif/GifSafety.kt +33 -0
  71. package/android/src/main/java/com/customcamera/gif/GiphyGifProvider.kt +146 -0
  72. package/android/src/main/java/com/customcamera/gif/LZWEncoder.kt +258 -0
  73. package/android/src/main/java/com/customcamera/gif/NeuQuant.kt +435 -0
  74. package/android/src/main/java/com/customcamera/gif/TenorGifProvider.kt +147 -0
  75. package/android/src/main/res/drawable/bg_brush_selected.xml +5 -0
  76. package/android/src/main/res/drawable/bg_color_swatch.xml +5 -0
  77. package/android/src/main/res/drawable/bg_editor_toolbar.xml +6 -0
  78. package/android/src/main/res/drawable/bg_icon_circle.xml +5 -0
  79. package/android/src/main/res/drawable/bg_icon_circle_light.xml +5 -0
  80. package/android/src/main/res/drawable/bg_mode_tab_active.xml +6 -0
  81. package/android/src/main/res/drawable/bg_pill.xml +6 -0
  82. package/android/src/main/res/drawable/bg_recording_dot.xml +6 -0
  83. package/android/src/main/res/drawable/bg_selection_badge.xml +8 -0
  84. package/android/src/main/res/drawable/bg_send_button.xml +5 -0
  85. package/android/src/main/res/drawable/bg_shutter_inner.xml +5 -0
  86. package/android/src/main/res/drawable/bg_shutter_recording.xml +6 -0
  87. package/android/src/main/res/drawable/bg_shutter_ring.xml +8 -0
  88. package/android/src/main/res/drawable/bg_shutter_video_idle.xml +5 -0
  89. package/android/src/main/res/drawable/bg_thumbnail.xml +9 -0
  90. package/android/src/main/res/drawable/ic_aspect_ratio.xml +17 -0
  91. package/android/src/main/res/drawable/ic_audio.xml +10 -0
  92. package/android/src/main/res/drawable/ic_back.xml +10 -0
  93. package/android/src/main/res/drawable/ic_brush_medium.xml +12 -0
  94. package/android/src/main/res/drawable/ic_brush_thick.xml +12 -0
  95. package/android/src/main/res/drawable/ic_brush_thin.xml +12 -0
  96. package/android/src/main/res/drawable/ic_check.xml +17 -0
  97. package/android/src/main/res/drawable/ic_chevron_down.xml +10 -0
  98. package/android/src/main/res/drawable/ic_chevron_up.xml +13 -0
  99. package/android/src/main/res/drawable/ic_clear_drawing.xml +10 -0
  100. package/android/src/main/res/drawable/ic_close.xml +10 -0
  101. package/android/src/main/res/drawable/ic_cover.xml +9 -0
  102. package/android/src/main/res/drawable/ic_crop.xml +10 -0
  103. package/android/src/main/res/drawable/ic_delete.xml +9 -0
  104. package/android/src/main/res/drawable/ic_document.xml +10 -0
  105. package/android/src/main/res/drawable/ic_done.xml +10 -0
  106. package/android/src/main/res/drawable/ic_download.xml +10 -0
  107. package/android/src/main/res/drawable/ic_emoji.xml +10 -0
  108. package/android/src/main/res/drawable/ic_filter.xml +10 -0
  109. package/android/src/main/res/drawable/ic_flash_auto.xml +7 -0
  110. package/android/src/main/res/drawable/ic_flash_off.xml +23 -0
  111. package/android/src/main/res/drawable/ic_flash_on.xml +16 -0
  112. package/android/src/main/res/drawable/ic_flip.xml +15 -0
  113. package/android/src/main/res/drawable/ic_gallery.xml +10 -0
  114. package/android/src/main/res/drawable/ic_gif.xml +24 -0
  115. package/android/src/main/res/drawable/ic_lock.xml +19 -0
  116. package/android/src/main/res/drawable/ic_lock_open.xml +22 -0
  117. package/android/src/main/res/drawable/ic_marker.xml +10 -0
  118. package/android/src/main/res/drawable/ic_more_vert.xml +18 -0
  119. package/android/src/main/res/drawable/ic_overlay_close.xml +19 -0
  120. package/android/src/main/res/drawable/ic_overlay_resize.xml +18 -0
  121. package/android/src/main/res/drawable/ic_pause.xml +10 -0
  122. package/android/src/main/res/drawable/ic_photo_mode.xml +10 -0
  123. package/android/src/main/res/drawable/ic_play.xml +10 -0
  124. package/android/src/main/res/drawable/ic_redo.xml +10 -0
  125. package/android/src/main/res/drawable/ic_rotate.xml +9 -0
  126. package/android/src/main/res/drawable/ic_send.xml +10 -0
  127. package/android/src/main/res/drawable/ic_share.xml +10 -0
  128. package/android/src/main/res/drawable/ic_stat_upload.xml +10 -0
  129. package/android/src/main/res/drawable/ic_sticker.xml +24 -0
  130. package/android/src/main/res/drawable/ic_text.xml +10 -0
  131. package/android/src/main/res/drawable/ic_text_align_center.xml +11 -0
  132. package/android/src/main/res/drawable/ic_text_align_left.xml +11 -0
  133. package/android/src/main/res/drawable/ic_text_align_right.xml +11 -0
  134. package/android/src/main/res/drawable/ic_text_bg.xml +11 -0
  135. package/android/src/main/res/drawable/ic_trim.xml +23 -0
  136. package/android/src/main/res/drawable/ic_undo.xml +10 -0
  137. package/android/src/main/res/drawable/ic_video_mode.xml +10 -0
  138. package/android/src/main/res/drawable/ic_video_note.xml +13 -0
  139. package/android/src/main/res/drawable/ic_volume_off.xml +9 -0
  140. package/android/src/main/res/drawable/ic_volume_on.xml +9 -0
  141. package/android/src/main/res/drawable/sticker_heart.xml +9 -0
  142. package/android/src/main/res/drawable/sticker_smile.xml +9 -0
  143. package/android/src/main/res/drawable/sticker_star.xml +9 -0
  144. package/android/src/main/res/drawable/sticker_thumbsup.xml +9 -0
  145. package/android/src/main/res/values/colors.xml +34 -0
  146. package/android/src/main/res/values/ids.xml +15 -0
  147. package/android/src/main/res/values/strings.xml +15 -0
  148. package/android/src/main/res/values/styles.xml +20 -0
  149. package/android/src/main/res/xml/file_paths.xml +34 -0
  150. package/ios/CustomCamera.h +5 -0
  151. package/ios/CustomCamera.mm +214 -0
  152. package/lib/module/AudioPlayerContext.js +233 -0
  153. package/lib/module/AudioPlayerContext.js.map +1 -0
  154. package/lib/module/CameraPreview.js +304 -0
  155. package/lib/module/CameraPreview.js.map +1 -0
  156. package/lib/module/NativeAnimatedImageView.js +41 -0
  157. package/lib/module/NativeAnimatedImageView.js.map +1 -0
  158. package/lib/module/NativeAudioPlayerView.js +33 -0
  159. package/lib/module/NativeAudioPlayerView.js.map +1 -0
  160. package/lib/module/NativeBlurView.js +35 -0
  161. package/lib/module/NativeBlurView.js.map +1 -0
  162. package/lib/module/NativeCustomCamera.js +128 -0
  163. package/lib/module/NativeCustomCamera.js.map +1 -0
  164. package/lib/module/NativeCustomCamera.web.js +143 -0
  165. package/lib/module/NativeCustomCamera.web.js.map +1 -0
  166. package/lib/module/NativeCustomVideoView.js +5 -0
  167. package/lib/module/NativeCustomVideoView.js.map +1 -0
  168. package/lib/module/NativeDocumentPreviewView.js +156 -0
  169. package/lib/module/NativeDocumentPreviewView.js.map +1 -0
  170. package/lib/module/NativeMediaThumbnailView.js +110 -0
  171. package/lib/module/NativeMediaThumbnailView.js.map +1 -0
  172. package/lib/module/NativeSvgIcon.js +32 -0
  173. package/lib/module/NativeSvgIcon.js.map +1 -0
  174. package/lib/module/ResponsivePreviewContainer.js +171 -0
  175. package/lib/module/ResponsivePreviewContainer.js.map +1 -0
  176. package/lib/module/accessibility.js +61 -0
  177. package/lib/module/accessibility.js.map +1 -0
  178. package/lib/module/config.js +352 -0
  179. package/lib/module/config.js.map +1 -0
  180. package/lib/module/cpuOptimization.js +296 -0
  181. package/lib/module/cpuOptimization.js.map +1 -0
  182. package/lib/module/db/DatabaseManager.js +191 -0
  183. package/lib/module/db/DatabaseManager.js.map +1 -0
  184. package/lib/module/db/MediaMapper.js +284 -0
  185. package/lib/module/db/MediaMapper.js.map +1 -0
  186. package/lib/module/db/MediaRepository.js +242 -0
  187. package/lib/module/db/MediaRepository.js.map +1 -0
  188. package/lib/module/db/index.js +41 -0
  189. package/lib/module/db/index.js.map +1 -0
  190. package/lib/module/db/migrations.js +180 -0
  191. package/lib/module/db/migrations.js.map +1 -0
  192. package/lib/module/db/react-native-sqlcipher.d.js +2 -0
  193. package/lib/module/db/react-native-sqlcipher.d.js.map +1 -0
  194. package/lib/module/db/secureKey.js +87 -0
  195. package/lib/module/db/secureKey.js.map +1 -0
  196. package/lib/module/db/types.js +57 -0
  197. package/lib/module/db/types.js.map +1 -0
  198. package/lib/module/db/useMediaDatabase.js +123 -0
  199. package/lib/module/db/useMediaDatabase.js.map +1 -0
  200. package/lib/module/defaultIcons.js +2259 -0
  201. package/lib/module/defaultIcons.js.map +1 -0
  202. package/lib/module/deviceCapabilities.js +408 -0
  203. package/lib/module/deviceCapabilities.js.map +1 -0
  204. package/lib/module/errors.js +267 -0
  205. package/lib/module/errors.js.map +1 -0
  206. package/lib/module/iconConfig.js +796 -0
  207. package/lib/module/iconConfig.js.map +1 -0
  208. package/lib/module/icons.js +514 -0
  209. package/lib/module/icons.js.map +1 -0
  210. package/lib/module/index.js +251 -0
  211. package/lib/module/index.js.map +1 -0
  212. package/lib/module/jest-globals.d.js +2 -0
  213. package/lib/module/jest-globals.d.js.map +1 -0
  214. package/lib/module/library/MediaLibrary.js +107 -0
  215. package/lib/module/library/MediaLibrary.js.map +1 -0
  216. package/lib/module/library/MediaLibraryScreen.js +2597 -0
  217. package/lib/module/library/MediaLibraryScreen.js.map +1 -0
  218. package/lib/module/library/components/AudioPreview.js +74 -0
  219. package/lib/module/library/components/AudioPreview.js.map +1 -0
  220. package/lib/module/library/components/DocumentPreview.js +331 -0
  221. package/lib/module/library/components/DocumentPreview.js.map +1 -0
  222. package/lib/module/library/components/ExpandableFab.js +343 -0
  223. package/lib/module/library/components/ExpandableFab.js.map +1 -0
  224. package/lib/module/library/components/FileTiles.js +251 -0
  225. package/lib/module/library/components/FileTiles.js.map +1 -0
  226. package/lib/module/library/components/ImagePreview.js +70 -0
  227. package/lib/module/library/components/ImagePreview.js.map +1 -0
  228. package/lib/module/library/components/MediaMetadataHeader.js +175 -0
  229. package/lib/module/library/components/MediaMetadataHeader.js.map +1 -0
  230. package/lib/module/library/components/RecordAudioScreen.js +816 -0
  231. package/lib/module/library/components/RecordAudioScreen.js.map +1 -0
  232. package/lib/module/library/components/VideoPreview.js +453 -0
  233. package/lib/module/library/components/VideoPreview.js.map +1 -0
  234. package/lib/module/library/components/ViewerCounter.js +55 -0
  235. package/lib/module/library/components/ViewerCounter.js.map +1 -0
  236. package/lib/module/library/compressionEstimate.js +507 -0
  237. package/lib/module/library/compressionEstimate.js.map +1 -0
  238. package/lib/module/library/fileMetadata.js +196 -0
  239. package/lib/module/library/fileMetadata.js.map +1 -0
  240. package/lib/module/library/index.js +42 -0
  241. package/lib/module/library/index.js.map +1 -0
  242. package/lib/module/library/libraryIconRegistry.js +69 -0
  243. package/lib/module/library/libraryIconRegistry.js.map +1 -0
  244. package/lib/module/library/libraryIcons.js +858 -0
  245. package/lib/module/library/libraryIcons.js.map +1 -0
  246. package/lib/module/library/localizedFormat.js +181 -0
  247. package/lib/module/library/localizedFormat.js.map +1 -0
  248. package/lib/module/library/palette.js +225 -0
  249. package/lib/module/library/palette.js.map +1 -0
  250. package/lib/module/library/responsive.js +53 -0
  251. package/lib/module/library/responsive.js.map +1 -0
  252. package/lib/module/locales.js +416 -0
  253. package/lib/module/locales.js.map +1 -0
  254. package/lib/module/localization.js +1064 -0
  255. package/lib/module/localization.js.map +1 -0
  256. package/lib/module/logger.js +20 -0
  257. package/lib/module/logger.js.map +1 -0
  258. package/lib/module/lowMemoryCache.js +206 -0
  259. package/lib/module/lowMemoryCache.js.map +1 -0
  260. package/lib/module/mediaHistory.js +33 -0
  261. package/lib/module/mediaHistory.js.map +1 -0
  262. package/lib/module/mediaStorageService.js +402 -0
  263. package/lib/module/mediaStorageService.js.map +1 -0
  264. package/lib/module/memory.js +79 -0
  265. package/lib/module/memory.js.map +1 -0
  266. package/lib/module/nativeActions.js +307 -0
  267. package/lib/module/nativeActions.js.map +1 -0
  268. package/lib/module/nativeComponentSupport.js +61 -0
  269. package/lib/module/nativeComponentSupport.js.map +1 -0
  270. package/lib/module/networkPerformance.js +24 -0
  271. package/lib/module/networkPerformance.js.map +1 -0
  272. package/lib/module/package.json +1 -0
  273. package/lib/module/performance.js +104 -0
  274. package/lib/module/performance.js.map +1 -0
  275. package/lib/module/performanceMonitor.js +155 -0
  276. package/lib/module/performanceMonitor.js.map +1 -0
  277. package/lib/module/permissions.js +370 -0
  278. package/lib/module/permissions.js.map +1 -0
  279. package/lib/module/rendering.js +40 -0
  280. package/lib/module/rendering.js.map +1 -0
  281. package/lib/module/security.js +18 -0
  282. package/lib/module/security.js.map +1 -0
  283. package/lib/module/state.js +266 -0
  284. package/lib/module/state.js.map +1 -0
  285. package/lib/module/storage.js +400 -0
  286. package/lib/module/storage.js.map +1 -0
  287. package/lib/module/theme.js +390 -0
  288. package/lib/module/theme.js.map +1 -0
  289. package/lib/module/types.js +4 -0
  290. package/lib/module/types.js.map +1 -0
  291. package/lib/module/utils.js +115 -0
  292. package/lib/module/utils.js.map +1 -0
  293. package/lib/module/validation.js +288 -0
  294. package/lib/module/validation.js.map +1 -0
  295. package/lib/typescript/package.json +1 -0
  296. package/lib/typescript/src/AudioPlayerContext.d.ts +27 -0
  297. package/lib/typescript/src/AudioPlayerContext.d.ts.map +1 -0
  298. package/lib/typescript/src/CameraPreview.d.ts +13 -0
  299. package/lib/typescript/src/CameraPreview.d.ts.map +1 -0
  300. package/lib/typescript/src/NativeAnimatedImageView.d.ts +33 -0
  301. package/lib/typescript/src/NativeAnimatedImageView.d.ts.map +1 -0
  302. package/lib/typescript/src/NativeAudioPlayerView.d.ts +12 -0
  303. package/lib/typescript/src/NativeAudioPlayerView.d.ts.map +1 -0
  304. package/lib/typescript/src/NativeBlurView.d.ts +12 -0
  305. package/lib/typescript/src/NativeBlurView.d.ts.map +1 -0
  306. package/lib/typescript/src/NativeCustomCamera.d.ts +167 -0
  307. package/lib/typescript/src/NativeCustomCamera.d.ts.map +1 -0
  308. package/lib/typescript/src/NativeCustomCamera.web.d.ts +47 -0
  309. package/lib/typescript/src/NativeCustomCamera.web.d.ts.map +1 -0
  310. package/lib/typescript/src/NativeCustomVideoView.d.ts +30 -0
  311. package/lib/typescript/src/NativeCustomVideoView.d.ts.map +1 -0
  312. package/lib/typescript/src/NativeDocumentPreviewView.d.ts +48 -0
  313. package/lib/typescript/src/NativeDocumentPreviewView.d.ts.map +1 -0
  314. package/lib/typescript/src/NativeMediaThumbnailView.d.ts +54 -0
  315. package/lib/typescript/src/NativeMediaThumbnailView.d.ts.map +1 -0
  316. package/lib/typescript/src/NativeSvgIcon.d.ts +25 -0
  317. package/lib/typescript/src/NativeSvgIcon.d.ts.map +1 -0
  318. package/lib/typescript/src/ResponsivePreviewContainer.d.ts +92 -0
  319. package/lib/typescript/src/ResponsivePreviewContainer.d.ts.map +1 -0
  320. package/lib/typescript/src/accessibility.d.ts +20 -0
  321. package/lib/typescript/src/accessibility.d.ts.map +1 -0
  322. package/lib/typescript/src/config.d.ts +145 -0
  323. package/lib/typescript/src/config.d.ts.map +1 -0
  324. package/lib/typescript/src/cpuOptimization.d.ts +63 -0
  325. package/lib/typescript/src/cpuOptimization.d.ts.map +1 -0
  326. package/lib/typescript/src/db/DatabaseManager.d.ts +48 -0
  327. package/lib/typescript/src/db/DatabaseManager.d.ts.map +1 -0
  328. package/lib/typescript/src/db/MediaMapper.d.ts +77 -0
  329. package/lib/typescript/src/db/MediaMapper.d.ts.map +1 -0
  330. package/lib/typescript/src/db/MediaRepository.d.ts +66 -0
  331. package/lib/typescript/src/db/MediaRepository.d.ts.map +1 -0
  332. package/lib/typescript/src/db/index.d.ts +23 -0
  333. package/lib/typescript/src/db/index.d.ts.map +1 -0
  334. package/lib/typescript/src/db/migrations.d.ts +31 -0
  335. package/lib/typescript/src/db/migrations.d.ts.map +1 -0
  336. package/lib/typescript/src/db/secureKey.d.ts +10 -0
  337. package/lib/typescript/src/db/secureKey.d.ts.map +1 -0
  338. package/lib/typescript/src/db/types.d.ts +115 -0
  339. package/lib/typescript/src/db/types.d.ts.map +1 -0
  340. package/lib/typescript/src/db/useMediaDatabase.d.ts +41 -0
  341. package/lib/typescript/src/db/useMediaDatabase.d.ts.map +1 -0
  342. package/lib/typescript/src/defaultIcons.d.ts +139 -0
  343. package/lib/typescript/src/defaultIcons.d.ts.map +1 -0
  344. package/lib/typescript/src/deviceCapabilities.d.ts +92 -0
  345. package/lib/typescript/src/deviceCapabilities.d.ts.map +1 -0
  346. package/lib/typescript/src/errors.d.ts +95 -0
  347. package/lib/typescript/src/errors.d.ts.map +1 -0
  348. package/lib/typescript/src/iconConfig.d.ts +176 -0
  349. package/lib/typescript/src/iconConfig.d.ts.map +1 -0
  350. package/lib/typescript/src/icons.d.ts +217 -0
  351. package/lib/typescript/src/icons.d.ts.map +1 -0
  352. package/lib/typescript/src/index.d.ts +104 -0
  353. package/lib/typescript/src/index.d.ts.map +1 -0
  354. package/lib/typescript/src/library/MediaLibrary.d.ts +57 -0
  355. package/lib/typescript/src/library/MediaLibrary.d.ts.map +1 -0
  356. package/lib/typescript/src/library/MediaLibraryScreen.d.ts +51 -0
  357. package/lib/typescript/src/library/MediaLibraryScreen.d.ts.map +1 -0
  358. package/lib/typescript/src/library/components/AudioPreview.d.ts +6 -0
  359. package/lib/typescript/src/library/components/AudioPreview.d.ts.map +1 -0
  360. package/lib/typescript/src/library/components/DocumentPreview.d.ts +10 -0
  361. package/lib/typescript/src/library/components/DocumentPreview.d.ts.map +1 -0
  362. package/lib/typescript/src/library/components/ExpandableFab.d.ts +39 -0
  363. package/lib/typescript/src/library/components/ExpandableFab.d.ts.map +1 -0
  364. package/lib/typescript/src/library/components/FileTiles.d.ts +8 -0
  365. package/lib/typescript/src/library/components/FileTiles.d.ts.map +1 -0
  366. package/lib/typescript/src/library/components/ImagePreview.d.ts +5 -0
  367. package/lib/typescript/src/library/components/ImagePreview.d.ts.map +1 -0
  368. package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts +16 -0
  369. package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts.map +1 -0
  370. package/lib/typescript/src/library/components/RecordAudioScreen.d.ts +8 -0
  371. package/lib/typescript/src/library/components/RecordAudioScreen.d.ts.map +1 -0
  372. package/lib/typescript/src/library/components/VideoPreview.d.ts +20 -0
  373. package/lib/typescript/src/library/components/VideoPreview.d.ts.map +1 -0
  374. package/lib/typescript/src/library/components/ViewerCounter.d.ts +13 -0
  375. package/lib/typescript/src/library/components/ViewerCounter.d.ts.map +1 -0
  376. package/lib/typescript/src/library/compressionEstimate.d.ts +313 -0
  377. package/lib/typescript/src/library/compressionEstimate.d.ts.map +1 -0
  378. package/lib/typescript/src/library/fileMetadata.d.ts +33 -0
  379. package/lib/typescript/src/library/fileMetadata.d.ts.map +1 -0
  380. package/lib/typescript/src/library/index.d.ts +40 -0
  381. package/lib/typescript/src/library/index.d.ts.map +1 -0
  382. package/lib/typescript/src/library/libraryIconRegistry.d.ts +35 -0
  383. package/lib/typescript/src/library/libraryIconRegistry.d.ts.map +1 -0
  384. package/lib/typescript/src/library/libraryIcons.d.ts +85 -0
  385. package/lib/typescript/src/library/libraryIcons.d.ts.map +1 -0
  386. package/lib/typescript/src/library/localizedFormat.d.ts +49 -0
  387. package/lib/typescript/src/library/localizedFormat.d.ts.map +1 -0
  388. package/lib/typescript/src/library/palette.d.ts +104 -0
  389. package/lib/typescript/src/library/palette.d.ts.map +1 -0
  390. package/lib/typescript/src/library/responsive.d.ts +15 -0
  391. package/lib/typescript/src/library/responsive.d.ts.map +1 -0
  392. package/lib/typescript/src/locales.d.ts +103 -0
  393. package/lib/typescript/src/locales.d.ts.map +1 -0
  394. package/lib/typescript/src/localization.d.ts +1051 -0
  395. package/lib/typescript/src/localization.d.ts.map +1 -0
  396. package/lib/typescript/src/logger.d.ts +4 -0
  397. package/lib/typescript/src/logger.d.ts.map +1 -0
  398. package/lib/typescript/src/lowMemoryCache.d.ts +64 -0
  399. package/lib/typescript/src/lowMemoryCache.d.ts.map +1 -0
  400. package/lib/typescript/src/mediaHistory.d.ts +3 -0
  401. package/lib/typescript/src/mediaHistory.d.ts.map +1 -0
  402. package/lib/typescript/src/mediaStorageService.d.ts +155 -0
  403. package/lib/typescript/src/mediaStorageService.d.ts.map +1 -0
  404. package/lib/typescript/src/memory.d.ts +21 -0
  405. package/lib/typescript/src/memory.d.ts.map +1 -0
  406. package/lib/typescript/src/nativeActions.d.ts +174 -0
  407. package/lib/typescript/src/nativeActions.d.ts.map +1 -0
  408. package/lib/typescript/src/nativeComponentSupport.d.ts +30 -0
  409. package/lib/typescript/src/nativeComponentSupport.d.ts.map +1 -0
  410. package/lib/typescript/src/networkPerformance.d.ts +12 -0
  411. package/lib/typescript/src/networkPerformance.d.ts.map +1 -0
  412. package/lib/typescript/src/performance.d.ts +32 -0
  413. package/lib/typescript/src/performance.d.ts.map +1 -0
  414. package/lib/typescript/src/performanceMonitor.d.ts +45 -0
  415. package/lib/typescript/src/performanceMonitor.d.ts.map +1 -0
  416. package/lib/typescript/src/permissions.d.ts +132 -0
  417. package/lib/typescript/src/permissions.d.ts.map +1 -0
  418. package/lib/typescript/src/rendering.d.ts +9 -0
  419. package/lib/typescript/src/rendering.d.ts.map +1 -0
  420. package/lib/typescript/src/security.d.ts +6 -0
  421. package/lib/typescript/src/security.d.ts.map +1 -0
  422. package/lib/typescript/src/state.d.ts +102 -0
  423. package/lib/typescript/src/state.d.ts.map +1 -0
  424. package/lib/typescript/src/storage.d.ts +117 -0
  425. package/lib/typescript/src/storage.d.ts.map +1 -0
  426. package/lib/typescript/src/theme.d.ts +15 -0
  427. package/lib/typescript/src/theme.d.ts.map +1 -0
  428. package/lib/typescript/src/types.d.ts +802 -0
  429. package/lib/typescript/src/types.d.ts.map +1 -0
  430. package/lib/typescript/src/utils.d.ts +50 -0
  431. package/lib/typescript/src/utils.d.ts.map +1 -0
  432. package/lib/typescript/src/validation.d.ts +52 -0
  433. package/lib/typescript/src/validation.d.ts.map +1 -0
  434. package/package.json +204 -0
  435. package/react-native.config.js +17 -0
  436. package/src/AudioPlayerContext.tsx +220 -0
  437. package/src/CameraPreview.tsx +352 -0
  438. package/src/NativeAnimatedImageView.tsx +71 -0
  439. package/src/NativeAudioPlayerView.tsx +39 -0
  440. package/src/NativeBlurView.tsx +40 -0
  441. package/src/NativeCustomCamera.ts +305 -0
  442. package/src/NativeCustomCamera.web.ts +192 -0
  443. package/src/NativeCustomVideoView.ts +30 -0
  444. package/src/NativeDocumentPreviewView.tsx +196 -0
  445. package/src/NativeMediaThumbnailView.tsx +167 -0
  446. package/src/NativeSvgIcon.tsx +46 -0
  447. package/src/ResponsivePreviewContainer.tsx +227 -0
  448. package/src/accessibility.ts +80 -0
  449. package/src/config.ts +406 -0
  450. package/src/cpuOptimization.ts +365 -0
  451. package/src/db/DatabaseManager.ts +212 -0
  452. package/src/db/MediaMapper.ts +377 -0
  453. package/src/db/MediaRepository.ts +313 -0
  454. package/src/db/index.ts +73 -0
  455. package/src/db/migrations.ts +222 -0
  456. package/src/db/react-native-sqlcipher.d.ts +76 -0
  457. package/src/db/secureKey.ts +100 -0
  458. package/src/db/types.ts +123 -0
  459. package/src/db/useMediaDatabase.ts +191 -0
  460. package/src/defaultIcons.tsx +2390 -0
  461. package/src/deviceCapabilities.ts +438 -0
  462. package/src/errors.ts +350 -0
  463. package/src/iconConfig.tsx +962 -0
  464. package/src/icons.tsx +788 -0
  465. package/src/index.tsx +763 -0
  466. package/src/jest-globals.d.ts +9 -0
  467. package/src/library/MediaLibrary.tsx +141 -0
  468. package/src/library/MediaLibraryScreen.tsx +3003 -0
  469. package/src/library/components/AudioPreview.tsx +73 -0
  470. package/src/library/components/DocumentPreview.tsx +361 -0
  471. package/src/library/components/ExpandableFab.tsx +342 -0
  472. package/src/library/components/FileTiles.tsx +252 -0
  473. package/src/library/components/ImagePreview.tsx +72 -0
  474. package/src/library/components/MediaMetadataHeader.tsx +208 -0
  475. package/src/library/components/RecordAudioScreen.tsx +845 -0
  476. package/src/library/components/VideoPreview.tsx +478 -0
  477. package/src/library/components/ViewerCounter.tsx +71 -0
  478. package/src/library/compressionEstimate.ts +662 -0
  479. package/src/library/fileMetadata.ts +212 -0
  480. package/src/library/index.ts +66 -0
  481. package/src/library/libraryIconRegistry.tsx +86 -0
  482. package/src/library/libraryIcons.tsx +928 -0
  483. package/src/library/localizedFormat.ts +207 -0
  484. package/src/library/palette.ts +281 -0
  485. package/src/library/responsive.ts +49 -0
  486. package/src/locales.ts +381 -0
  487. package/src/localization.ts +1947 -0
  488. package/src/logger.ts +20 -0
  489. package/src/lowMemoryCache.ts +218 -0
  490. package/src/mediaHistory.ts +42 -0
  491. package/src/mediaStorageService.ts +546 -0
  492. package/src/memory.ts +102 -0
  493. package/src/nativeActions.ts +422 -0
  494. package/src/nativeComponentSupport.ts +60 -0
  495. package/src/networkPerformance.ts +30 -0
  496. package/src/performance.ts +137 -0
  497. package/src/performanceMonitor.ts +219 -0
  498. package/src/permissions.ts +403 -0
  499. package/src/rendering.ts +47 -0
  500. package/src/security.ts +19 -0
  501. package/src/state.ts +333 -0
  502. package/src/storage.ts +453 -0
  503. package/src/theme.tsx +467 -0
  504. package/src/types.ts +946 -0
  505. package/src/utils.ts +177 -0
  506. package/src/validation.ts +372 -0
@@ -0,0 +1,160 @@
1
+ package com.customcamera.gif
2
+
3
+ import android.graphics.Bitmap
4
+ import android.graphics.Canvas
5
+ import android.graphics.ColorFilter
6
+ import android.graphics.Paint
7
+ import android.graphics.PixelFormat
8
+ import android.graphics.Rect
9
+ import android.graphics.drawable.Animatable
10
+ import android.graphics.drawable.Drawable
11
+ import android.os.Handler
12
+ import android.os.Looper
13
+
14
+ /**
15
+ * An [Animatable] [Drawable] that cycles through a list of pre-decoded GIF frames.
16
+ *
17
+ * This is the all-API animation path for placed GIF overlays: on API < 28 there is no
18
+ * [android.graphics.drawable.AnimatedImageDrawable], and even on API 28+ we prefer feeding the
19
+ * exporter the exact same frames it will re-encode. Frames are decoded once (see the GIF decoder)
20
+ * and owned by this drawable; call [recycle] when the overlay is removed.
21
+ *
22
+ * Timing is driven by a main-thread [Handler]: each frame is shown for its own delay, then the
23
+ * next frame is scheduled. Looping honours the source GIF's loop count (0 == loop forever, the
24
+ * common case). Drawing scales the current frame to the drawable [bounds], so the same instance
25
+ * works for a small grid thumbnail and a large placed overlay.
26
+ */
27
+ class GifFramesDrawable(
28
+ private val frames: List<Bitmap>,
29
+ delaysMs: List<Int>,
30
+ private val loopCount: Int
31
+ ) : Drawable(), Animatable, Runnable {
32
+
33
+ /** Per-frame display duration, sanitised so a zero/negative delay never busy-loops. */
34
+ private val delays: IntArray = IntArray(frames.size) { i ->
35
+ val d = delaysMs.getOrElse(i) { DEFAULT_DELAY_MS }
36
+ if (d <= 0) DEFAULT_DELAY_MS else d
37
+ }
38
+
39
+ private val paint = Paint(Paint.FILTER_BITMAP_FLAG)
40
+ private val handler = Handler(Looper.getMainLooper())
41
+ private val srcRect = Rect()
42
+
43
+ private var currentFrame = 0
44
+ private var loopsDone = 0
45
+ private var running = false
46
+
47
+ /** Number of decoded frames; 1 (or 0) means this is effectively a static image. */
48
+ val frameCount: Int get() = frames.size
49
+
50
+ /** Total time for one full pass through every frame, in milliseconds. */
51
+ val totalDurationMs: Int get() = delays.sum()
52
+
53
+ override fun draw(canvas: Canvas) {
54
+ if (frames.isEmpty()) return
55
+ val bmp = frames[currentFrame.coerceIn(0, frames.size - 1)]
56
+ if (bmp.isRecycled) return
57
+ srcRect.set(0, 0, bmp.width, bmp.height)
58
+ canvas.drawBitmap(bmp, srcRect, bounds, paint)
59
+ }
60
+
61
+ override fun run() {
62
+ if (!running || frames.size <= 1) return
63
+ val next = currentFrame + 1
64
+ if (next >= frames.size) {
65
+ // Completed one full pass through the frames.
66
+ if (loopCount != 0) {
67
+ loopsDone++
68
+ if (loopsDone >= loopCount) {
69
+ currentFrame = frames.size - 1
70
+ running = false
71
+ invalidateSelf()
72
+ return
73
+ }
74
+ }
75
+ currentFrame = 0
76
+ } else {
77
+ currentFrame = next
78
+ }
79
+ invalidateSelf()
80
+ scheduleNext()
81
+ }
82
+
83
+ private fun scheduleNext() {
84
+ if (!running || frames.size <= 1) return
85
+ val delay = delays[currentFrame.coerceIn(0, delays.size - 1)].toLong()
86
+ handler.postDelayed(this, delay)
87
+ }
88
+
89
+ override fun start() {
90
+ if (frames.size <= 1) { invalidateSelf(); return }
91
+ if (running) return
92
+ running = true
93
+ loopsDone = 0
94
+ scheduleNext()
95
+ invalidateSelf()
96
+ }
97
+
98
+ override fun stop() {
99
+ running = false
100
+ handler.removeCallbacks(this)
101
+ }
102
+
103
+ override fun isRunning(): Boolean = running
104
+
105
+ override fun setAlpha(alpha: Int) { paint.alpha = alpha; invalidateSelf() }
106
+
107
+ override fun setColorFilter(colorFilter: ColorFilter?) {
108
+ paint.colorFilter = colorFilter
109
+ invalidateSelf()
110
+ }
111
+
112
+ @Deprecated("Deprecated in Java", ReplaceWith("PixelFormat.TRANSLUCENT"))
113
+ override fun getOpacity(): Int = PixelFormat.TRANSLUCENT
114
+
115
+ override fun getIntrinsicWidth(): Int = frames.firstOrNull()?.width ?: -1
116
+
117
+ override fun getIntrinsicHeight(): Int = frames.firstOrNull()?.height ?: -1
118
+
119
+ /** Reset to the first frame without freeing bitmaps; safe to call before re-[start]. */
120
+ fun reset() {
121
+ stop()
122
+ currentFrame = 0
123
+ loopsDone = 0
124
+ invalidateSelf()
125
+ }
126
+
127
+ /**
128
+ * Deterministically position the drawable at the frame visible [timeMs] into a forever-looping
129
+ * playback (independent of [loopCount]). Used by the exporter to step frames by hand; the caller
130
+ * must [stop] the handler-driven animation first so the two don't fight over [currentFrame].
131
+ */
132
+ fun seekToTime(timeMs: Long) {
133
+ if (frames.size <= 1) { currentFrame = 0; invalidateSelf(); return }
134
+ val total = totalDurationMs
135
+ if (total <= 0) { currentFrame = 0; invalidateSelf(); return }
136
+ var t = (timeMs % total).toInt()
137
+ if (t < 0) t += total
138
+ var acc = 0
139
+ var idx = 0
140
+ for (i in delays.indices) {
141
+ acc += delays[i]
142
+ if (t < acc) { idx = i; break }
143
+ idx = i
144
+ }
145
+ currentFrame = idx.coerceIn(0, frames.size - 1)
146
+ invalidateSelf()
147
+ }
148
+
149
+ /** Stop animating and free every frame bitmap. The drawable is unusable afterwards. */
150
+ fun recycle() {
151
+ stop()
152
+ for (f in frames) {
153
+ if (!f.isRecycled) f.recycle()
154
+ }
155
+ }
156
+
157
+ companion object {
158
+ private const val DEFAULT_DELAY_MS = 100
159
+ }
160
+ }
@@ -0,0 +1,112 @@
1
+ package com.customcamera.gif
2
+
3
+ import java.io.IOException
4
+ import java.net.SocketTimeoutException
5
+ import java.net.UnknownHostException
6
+ import java.util.concurrent.TimeUnit
7
+ import okhttp3.OkHttpClient
8
+ import okhttp3.Request
9
+ import org.json.JSONException
10
+ import org.json.JSONObject
11
+
12
+ /**
13
+ * Minimal blocking HTTP helper shared by every [GifProvider].
14
+ *
15
+ * Providers run on a background thread and must never throw, so [getJson] performs a synchronous
16
+ * GET and folds every failure mode -- transport, HTTP status, and JSON parsing -- into a typed
17
+ * [GifResult.Failure]. A single [OkHttpClient] is shared across all providers so its connection
18
+ * pool and dispatcher threads are reused.
19
+ */
20
+ class GifHttp {
21
+
22
+ companion object {
23
+ /** Shared, lazily-created client: 15s to connect, 20s waiting for the response body. */
24
+ private val client: OkHttpClient by lazy {
25
+ OkHttpClient.Builder()
26
+ .connectTimeout(15, TimeUnit.SECONDS)
27
+ .readTimeout(20, TimeUnit.SECONDS)
28
+ .build()
29
+ }
30
+
31
+ /**
32
+ * Perform a blocking GET and parse the response body as a JSON object.
33
+ *
34
+ * Never throws. Failures map to: [GifError.TIMEOUT] (socket timeout), [GifError.NETWORK]
35
+ * (DNS / connectivity / other IO), [GifError.RATE_LIMITED] (HTTP 429), [GifError.SERVER]
36
+ * (HTTP 5xx), [GifError.PARSE] (empty or malformed JSON), and [GifError.UNKNOWN] (any other
37
+ * non-2xx status or unexpected error).
38
+ */
39
+ fun getJson(url: String): GifResult<JSONObject> {
40
+ return try {
41
+ val request = Request.Builder()
42
+ .url(url)
43
+ .header("Accept", "application/json")
44
+ .get()
45
+ .build()
46
+ client.newCall(request).execute().use { response ->
47
+ val code = response.code
48
+ when {
49
+ response.isSuccessful -> {
50
+ val bodyString = response.body?.string()
51
+ if (bodyString.isNullOrBlank()) {
52
+ GifResult.Failure(GifError.PARSE)
53
+ } else {
54
+ try {
55
+ GifResult.Success(JSONObject(bodyString))
56
+ } catch (e: JSONException) {
57
+ GifResult.Failure(GifError.PARSE, e)
58
+ }
59
+ }
60
+ }
61
+ code == 429 -> GifResult.Failure(GifError.RATE_LIMITED)
62
+ code in 500..599 -> GifResult.Failure(GifError.SERVER)
63
+ else -> GifResult.Failure(GifError.UNKNOWN)
64
+ }
65
+ }
66
+ } catch (e: SocketTimeoutException) {
67
+ GifResult.Failure(GifError.TIMEOUT, e)
68
+ } catch (e: UnknownHostException) {
69
+ GifResult.Failure(GifError.NETWORK, e)
70
+ } catch (e: IOException) {
71
+ GifResult.Failure(GifError.NETWORK, e)
72
+ } catch (e: Exception) {
73
+ GifResult.Failure(GifError.UNKNOWN, e)
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Blocking GET that returns the raw response body bytes — used to download a GIF's bytes
79
+ * (grid thumbnail or the full content GIF) for decoding. Same failure folding as [getJson],
80
+ * except the body is returned verbatim rather than parsed. Never throws.
81
+ */
82
+ fun getBytes(url: String): GifResult<ByteArray> {
83
+ return try {
84
+ val request = Request.Builder().url(url).get().build()
85
+ client.newCall(request).execute().use { response ->
86
+ val code = response.code
87
+ when {
88
+ response.isSuccessful -> {
89
+ val bytes = response.body?.bytes()
90
+ if (bytes == null || bytes.isEmpty()) {
91
+ GifResult.Failure(GifError.PARSE)
92
+ } else {
93
+ GifResult.Success(bytes)
94
+ }
95
+ }
96
+ code == 429 -> GifResult.Failure(GifError.RATE_LIMITED)
97
+ code in 500..599 -> GifResult.Failure(GifError.SERVER)
98
+ else -> GifResult.Failure(GifError.UNKNOWN)
99
+ }
100
+ }
101
+ } catch (e: SocketTimeoutException) {
102
+ GifResult.Failure(GifError.TIMEOUT, e)
103
+ } catch (e: UnknownHostException) {
104
+ GifResult.Failure(GifError.NETWORK, e)
105
+ } catch (e: IOException) {
106
+ GifResult.Failure(GifError.NETWORK, e)
107
+ } catch (e: Exception) {
108
+ GifResult.Failure(GifError.UNKNOWN, e)
109
+ }
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,186 @@
1
+ package com.customcamera.gif
2
+
3
+ import android.graphics.BitmapFactory
4
+ import android.graphics.drawable.AnimatedImageDrawable
5
+ import android.graphics.drawable.BitmapDrawable
6
+ import android.graphics.drawable.Drawable
7
+ import android.os.Build
8
+ import android.os.Handler
9
+ import android.os.Looper
10
+ import android.util.LruCache
11
+ import android.widget.ImageView
12
+ import java.nio.ByteBuffer
13
+ import java.util.WeakHashMap
14
+ import java.util.concurrent.Executors
15
+
16
+ /**
17
+ * Loads GIF thumbnails into the picker grid's [ImageView]s, and downloads raw GIF bytes for the
18
+ * placed overlay. Deliberately dependency-free (uses only [GifHttp] + the platform decoders) so
19
+ * the grid works without the frame decoder/encoder.
20
+ *
21
+ * Thumbnails animate on API 28+ via [AnimatedImageDrawable] (native, lazy, memory-friendly) and
22
+ * fall back to a downsampled static first frame on API 24–27 — a deliberate low-RAM degradation
23
+ * for old devices; the *placed* overlay still animates on every API via the frame decoder.
24
+ *
25
+ * Downloaded bytes are held in a bounded LRU cache keyed by URL so re-scrolling the grid and then
26
+ * picking a GIF never re-downloads. Decoded thumbnails are additionally held in a small drawable
27
+ * LRU so a GIF that scrolls back into view is re-shown instantly (no re-decode, no blank flash). A
28
+ * per-[ImageView] "pending url" guard (a [WeakHashMap]) makes the loader safe against RecyclerView
29
+ * cell recycling: a slow response for a recycled cell is dropped instead of painting the wrong GIF.
30
+ */
31
+ class GifImageLoader(cacheSizeBytes: Int = DEFAULT_CACHE_BYTES) {
32
+
33
+ private val executor = Executors.newFixedThreadPool(3) { r ->
34
+ Thread(r, "gif-img").apply { isDaemon = true }
35
+ }
36
+ private val main = Handler(Looper.getMainLooper())
37
+
38
+ private val byteCache = object : LruCache<String, ByteArray>(cacheSizeBytes) {
39
+ override fun sizeOf(key: String, value: ByteArray): Int = value.size
40
+ }
41
+
42
+ /** The URL each live ImageView currently wants; guards against recycled-cell races. */
43
+ private val pending = WeakHashMap<ImageView, String>()
44
+
45
+ /**
46
+ * Decoded thumbnails, keyed by URL+size. Lets a previously-viewed GIF that scrolls back into the
47
+ * grid be re-shown synchronously on the main thread — no re-decode, no background hop, and no
48
+ * blanking of the cell — which is what removes the 1–2s blank/flicker on scroll-back. Bounded by
49
+ * entry count; an evicted animation is stopped (unless it is still attached to a live cell).
50
+ */
51
+ private val drawableCache = object : LruCache<String, Drawable>(DRAWABLE_CACHE_ENTRIES) {
52
+ override fun entryRemoved(evicted: Boolean, key: String, oldValue: Drawable, newValue: Drawable?) {
53
+ if (oldValue !== newValue && oldValue.callback == null) {
54
+ (oldValue as? AnimatedImageDrawable)?.stop()
55
+ }
56
+ }
57
+ }
58
+
59
+ @Volatile private var shutdown = false
60
+
61
+ /**
62
+ * Download (or reuse cached bytes for) [url], decode, and set the result on [target]. Any
63
+ * previous request for the same view is superseded. [maxDim] caps the decoded thumbnail size.
64
+ */
65
+ fun loadThumbnail(url: String, target: ImageView, maxDim: Int = DEFAULT_THUMB_MAX_DIM) {
66
+ if (url.isBlank()) return
67
+ pending[target] = url
68
+ val key = cacheKey(url, maxDim)
69
+
70
+ // Fast path: this GIF was decoded earlier (e.g. it was scrolled off and is now coming back).
71
+ // Re-show the cached drawable straight away on the main thread — no blanking, no re-decode,
72
+ // no background hop — so a returning cell never flashes its blank placeholder. Only reuse a
73
+ // drawable that isn't currently attached to a different live cell (the rare same-URL-twice
74
+ // case), so we never steal an animation from another visible view.
75
+ val cached = drawableCache.get(key)
76
+ if (cached != null && (cached.callback == null || cached.callback === target)) {
77
+ if (target.drawable !== cached) {
78
+ (target.drawable as? AnimatedImageDrawable)?.stop()
79
+ target.setImageDrawable(cached)
80
+ }
81
+ (cached as? AnimatedImageDrawable)?.start()
82
+ return
83
+ }
84
+
85
+ // Slow path: first time this GIF is shown. Blank the recycled cell and decode off-main, then
86
+ // cache the result so any later scroll-back takes the flicker-free fast path above.
87
+ (target.drawable as? AnimatedImageDrawable)?.stop()
88
+ target.setImageDrawable(null)
89
+ execute {
90
+ val bytes = bytesFor(url) ?: return@execute
91
+ val drawable = runCatching { decodeThumbnail(bytes, maxDim) }.getOrNull() ?: return@execute
92
+ main.post {
93
+ // Cache even if this cell was recycled meanwhile: the decode is done, so a future
94
+ // appearance of the same GIF should reuse it rather than decode again.
95
+ drawableCache.put(key, drawable)
96
+ if (shutdown || pending[target] != url) {
97
+ (drawable as? AnimatedImageDrawable)?.stop()
98
+ return@post
99
+ }
100
+ target.setImageDrawable(drawable)
101
+ (drawable as? AnimatedImageDrawable)?.start()
102
+ }
103
+ }
104
+ }
105
+
106
+ private fun cacheKey(url: String, maxDim: Int): String = "$url#$maxDim"
107
+
108
+ /** Blocking byte fetch for the overlay content GIF; caches on success. Off-main callers only. */
109
+ fun fetchBytes(url: String): GifResult<ByteArray> {
110
+ byteCache.get(url)?.let { return GifResult.Success(it) }
111
+ val res = GifHttp.getBytes(url)
112
+ if (res is GifResult.Success) byteCache.put(url, res.value)
113
+ return res
114
+ }
115
+
116
+ /** Forget the pending request for [target] (e.g. when the sheet closes). */
117
+ fun cancel(target: ImageView) {
118
+ pending.remove(target)
119
+ (target.drawable as? AnimatedImageDrawable)?.stop()
120
+ }
121
+
122
+ private fun bytesFor(url: String): ByteArray? {
123
+ byteCache.get(url)?.let { return it }
124
+ val res = GifHttp.getBytes(url)
125
+ return if (res is GifResult.Success) {
126
+ byteCache.put(url, res.value)
127
+ res.value
128
+ } else {
129
+ null
130
+ }
131
+ }
132
+
133
+ private fun decodeThumbnail(bytes: ByteArray, maxDim: Int): Drawable? {
134
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
135
+ val source = android.graphics.ImageDecoder.createSource(ByteBuffer.wrap(bytes))
136
+ return android.graphics.ImageDecoder.decodeDrawable(source) { decoder, info, _ ->
137
+ val longest = maxOf(info.size.width, info.size.height)
138
+ if (maxDim > 0 && longest > maxDim) {
139
+ decoder.setTargetSampleSize(sampleSizeFor(longest, maxDim))
140
+ }
141
+ }
142
+ }
143
+ // API 24–27: static first frame, downsampled.
144
+ val opts = BitmapFactory.Options().apply { inJustDecodeBounds = true }
145
+ BitmapFactory.decodeByteArray(bytes, 0, bytes.size, opts)
146
+ val longest = maxOf(opts.outWidth, opts.outHeight)
147
+ val decodeOpts = BitmapFactory.Options().apply {
148
+ inSampleSize = if (maxDim > 0 && longest > maxDim) sampleSizeFor(longest, maxDim) else 1
149
+ }
150
+ val bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.size, decodeOpts) ?: return null
151
+ return BitmapDrawable(null, bmp)
152
+ }
153
+
154
+ /** Largest power-of-two sample size that keeps the longest edge >= [maxDim]. */
155
+ private fun sampleSizeFor(longest: Int, maxDim: Int): Int {
156
+ var sample = 1
157
+ while (longest / (sample * 2) >= maxDim) sample *= 2
158
+ return sample
159
+ }
160
+
161
+ private fun execute(block: () -> Unit) {
162
+ try {
163
+ executor.execute { if (!shutdown) block() }
164
+ } catch (e: Exception) {
165
+ // Executor already shut down — nothing to do.
166
+ }
167
+ }
168
+
169
+ /** Release worker threads and drop the caches. Safe to call more than once. */
170
+ fun shutdown() {
171
+ shutdown = true
172
+ executor.shutdownNow()
173
+ byteCache.evictAll()
174
+ drawableCache.evictAll()
175
+ pending.clear()
176
+ }
177
+
178
+ companion object {
179
+ private const val DEFAULT_CACHE_BYTES = 16 * 1024 * 1024
180
+ private const val DEFAULT_THUMB_MAX_DIM = 256
181
+ // How many decoded thumbnails to keep for instant, flicker-free scroll-back. Sized to a few
182
+ // screens of the ~104dp grid cells; thumbnails are downsampled to DEFAULT_THUMB_MAX_DIM and
183
+ // AnimatedImageDrawable is lazy, so this stays modest on RAM. Evicted animations are stopped.
184
+ private const val DRAWABLE_CACHE_ENTRIES = 48
185
+ }
186
+ }
@@ -0,0 +1,74 @@
1
+ package com.customcamera.gif
2
+
3
+ /**
4
+ * Provider-agnostic data models for the GIF picker feature.
5
+ *
6
+ * Everything the UI and the editor overlay consume is expressed in these types so that the
7
+ * concrete provider (GIPHY, Tenor, or anything added later) stays fully behind [GifProvider].
8
+ * No GIPHY/Tenor-specific field ever leaks past this file.
9
+ */
10
+
11
+ /** A single GIF result. URLs point at remote animated GIF assets. */
12
+ data class GifItem(
13
+ /** Stable provider-scoped id, used for de-duplication and "recent" tracking. */
14
+ val id: String,
15
+ /** Human-readable description for accessibility / content-description. May be blank. */
16
+ val description: String,
17
+ /** Small animated GIF used for the grid thumbnail (roughly ~200 px wide). */
18
+ val previewUrl: String,
19
+ val previewWidth: Int,
20
+ val previewHeight: Int,
21
+ /** Larger animated GIF placed on the photo when the item is picked. */
22
+ val contentUrl: String,
23
+ val contentWidth: Int,
24
+ val contentHeight: Int
25
+ ) {
26
+ /** Aspect ratio (w/h) of the preview, defaulting to 1 when dimensions are unknown. */
27
+ val previewAspect: Float
28
+ get() = if (previewWidth > 0 && previewHeight > 0) previewWidth.toFloat() / previewHeight else 1f
29
+ }
30
+
31
+ /** A browsable category / trending term shown as a chip or tile before the user searches. */
32
+ data class GifCategory(
33
+ /** Display label (already localized/normalized by the provider). */
34
+ val name: String,
35
+ /** The query to run when the category is tapped. */
36
+ val searchTerm: String,
37
+ /** Optional still/animated preview image for the category tile. */
38
+ val imageUrl: String?
39
+ )
40
+
41
+ /** One page of results plus an opaque cursor for the next page (null/blank = no more). */
42
+ data class GifPage(
43
+ val items: List<GifItem>,
44
+ val nextCursor: String?
45
+ )
46
+
47
+ /** Why a provider call could not return data. Drives the UI's empty/error/retry states. */
48
+ enum class GifError {
49
+ /** No connectivity, DNS failure, socket error, etc. */
50
+ NETWORK,
51
+ /** The request timed out. */
52
+ TIMEOUT,
53
+ /** The provider has no API key configured, so it cannot be used at all. */
54
+ NO_API_KEY,
55
+ /** HTTP 429 / provider quota exceeded. */
56
+ RATE_LIMITED,
57
+ /** HTTP 5xx from the provider. */
58
+ SERVER,
59
+ /** The response could not be parsed into the expected shape. */
60
+ PARSE,
61
+ /** Anything else. */
62
+ UNKNOWN
63
+ }
64
+
65
+ /** Result wrapper. [Success] carries the value; [Failure] carries a typed [GifError]. */
66
+ sealed class GifResult<out T> {
67
+ data class Success<T>(val value: T) : GifResult<T>()
68
+ data class Failure(val error: GifError, val cause: Throwable? = null) : GifResult<Nothing>()
69
+
70
+ inline fun <R> map(transform: (T) -> R): GifResult<R> = when (this) {
71
+ is Success -> Success(transform(value))
72
+ is Failure -> this
73
+ }
74
+ }
@@ -0,0 +1,49 @@
1
+ package com.customcamera.gif
2
+
3
+ /**
4
+ * The single seam behind which every GIF service lives.
5
+ *
6
+ * The editor and picker UI depend ONLY on this interface, never on GIPHY/Tenor directly, so a
7
+ * provider can be swapped (or a new one added) by implementing this and registering it in
8
+ * [GifProviders]. Implementations are called on a background thread by [GifRepository] and must
9
+ * be blocking (no callbacks of their own); they must never touch the UI thread.
10
+ *
11
+ * Implementations must be resilient: any network/parse failure is returned as a
12
+ * [GifResult.Failure] with a typed [GifError] rather than thrown, so the UI can render a precise
13
+ * empty/error/retry state.
14
+ */
15
+ interface GifProvider {
16
+ /** Stable identifier, e.g. "giphy" or "tenor". Used to select the provider from config. */
17
+ val id: String
18
+
19
+ /**
20
+ * Attribution string the picker must display when this provider is active (both GIPHY and
21
+ * Tenor require visible attribution). Null when none is required.
22
+ */
23
+ val attributionLabel: String?
24
+
25
+ /**
26
+ * False when the provider cannot operate at all — almost always a missing API key. The UI
27
+ * shows a dedicated "not configured" message instead of a generic network error, and the
28
+ * repository short-circuits with [GifError.NO_API_KEY].
29
+ */
30
+ fun isConfigured(): Boolean
31
+
32
+ /**
33
+ * Trending / featured GIFs for the initial, no-query state.
34
+ * @param cursor opaque page cursor from a previous [GifPage.nextCursor]; null for the first page.
35
+ */
36
+ fun trending(limit: Int, cursor: String?): GifResult<GifPage>
37
+
38
+ /**
39
+ * Search results for [query].
40
+ * @param cursor opaque page cursor from a previous [GifPage.nextCursor]; null for the first page.
41
+ */
42
+ fun search(query: String, limit: Int, cursor: String?): GifResult<GifPage>
43
+
44
+ /**
45
+ * Browsable categories shown before the user types. Providers without a categories endpoint
46
+ * may return an empty list; the UI then simply shows trending only.
47
+ */
48
+ fun categories(): GifResult<List<GifCategory>>
49
+ }
@@ -0,0 +1,27 @@
1
+ package com.customcamera.gif
2
+
3
+ /**
4
+ * Factory mapping a configured provider id to a concrete [GifProvider] implementation.
5
+ *
6
+ * GIPHY is the default -- selected for a null, blank, or unrecognised id -- because Google has
7
+ * announced the discontinuation of the Tenor API; Tenor is therefore only used when explicitly
8
+ * requested by [providerId] == "tenor".
9
+ */
10
+ object GifProviders {
11
+
12
+ /**
13
+ * Create the provider selected by [providerId].
14
+ *
15
+ * @param providerId case-insensitive provider key. "tenor" -> [TenorGifProvider]; anything else
16
+ * (including null / blank / unknown) -> [GiphyGifProvider].
17
+ * @param apiKey API key handed to the selected provider; may be null (the provider then reports
18
+ * [GifProvider.isConfigured] == false).
19
+ * @return a ready-to-use [GifProvider]; never null.
20
+ */
21
+ fun create(providerId: String?, apiKey: String?): GifProvider {
22
+ return when (providerId?.trim()?.lowercase()) {
23
+ "tenor" -> TenorGifProvider(apiKey)
24
+ else -> GiphyGifProvider(apiKey)
25
+ }
26
+ }
27
+ }