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,455 @@
1
+ package com.customcamera
2
+
3
+ import android.content.ContentResolver
4
+ import android.graphics.Bitmap
5
+ import android.graphics.BitmapFactory
6
+ import android.graphics.Color
7
+ import android.graphics.ImageDecoder
8
+ import android.graphics.drawable.ColorDrawable
9
+ import android.media.MediaMetadataRetriever
10
+ import android.os.Build
11
+ import android.util.LruCache
12
+ import android.util.Size
13
+ import android.widget.ImageView
14
+ import java.io.File
15
+ import kotlinx.coroutines.CoroutineScope
16
+ import kotlinx.coroutines.Dispatchers
17
+ import kotlinx.coroutines.SupervisorJob
18
+ import kotlinx.coroutines.cancel
19
+ import kotlinx.coroutines.launch
20
+ import kotlinx.coroutines.withContext
21
+
22
+ class ThumbnailLoader(
23
+ private val contentResolver: ContentResolver
24
+ ) {
25
+ var lowMemoryMode: Boolean = false
26
+
27
+ private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob())
28
+ private val cache = object : LruCache<String, Bitmap>(cacheSizeKb()) {
29
+ override fun sizeOf(key: String, value: Bitmap): Int = value.byteCount / 1024
30
+ }
31
+
32
+ fun load(asset: CameraAsset, imageView: ImageView, sizePx: Int) {
33
+ val actualSizePx = if (lowMemoryMode) sizePx / 2 else sizePx
34
+ val key = "${asset.coverUri ?: asset.uri}@$actualSizePx"
35
+ imageView.tag = key
36
+ val placeholderColor = CameraTheme.get(imageView.context)
37
+ .componentColor("mediaGrid", "thumbnailPlaceholder", 0xFF111111.toInt())
38
+ imageView.setImageDrawable(ColorDrawable(placeholderColor))
39
+
40
+ cache.get(key)?.let {
41
+ imageView.setImageBitmap(it)
42
+ return
43
+ }
44
+
45
+ scope.launch {
46
+ val bitmap = loadBitmap(asset, actualSizePx)
47
+ if (bitmap != null) {
48
+ cache.put(key, bitmap)
49
+ withContext(Dispatchers.Main) {
50
+ if (imageView.tag == key) {
51
+ imageView.setImageBitmap(bitmap)
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Callback variant of [load] for hosts that own their view's tag.
60
+ *
61
+ * [load] stores its cache key in `imageView.tag`, which is exactly where ReactNative's
62
+ * BaseViewManager writes `testID` — a RN-hosted thumbnail must therefore not go through it.
63
+ * Same cache, same decode path (cover frame → video frame → resolver thumbnail → decoder);
64
+ * [onReady] is invoked on the main thread and only when a bitmap was produced, so a format
65
+ * this device cannot decode simply leaves the caller's placeholder in place.
66
+ */
67
+ fun requestBitmap(asset: CameraAsset, sizePx: Int, onReady: (Bitmap) -> Unit) {
68
+ val actualSizePx = if (lowMemoryMode) sizePx / 2 else sizePx
69
+ val key = "${asset.coverUri ?: asset.uri}@$actualSizePx"
70
+
71
+ cache.get(key)?.let {
72
+ onReady(it)
73
+ return
74
+ }
75
+
76
+ scope.launch {
77
+ val bitmap = loadBitmap(asset, actualSizePx) ?: return@launch
78
+ cache.put(key, bitmap)
79
+ withContext(Dispatchers.Main) { onReady(bitmap) }
80
+ }
81
+ }
82
+
83
+ fun preload(asset: CameraAsset, sizePx: Int) {
84
+ if (lowMemoryMode) return
85
+ val key = "${asset.coverUri ?: asset.uri}@$sizePx"
86
+ if (cache.get(key) != null) return
87
+
88
+ scope.launch {
89
+ val bitmap = loadBitmap(asset, sizePx) ?: return@launch
90
+ cache.put(key, bitmap)
91
+ }
92
+ }
93
+
94
+ fun clearCache() {
95
+ cache.evictAll()
96
+ }
97
+
98
+ /**
99
+ * Drop every cached bitmap decoded from [source] — one media file, at any decode size.
100
+ *
101
+ * Cache keys are `"<coverUri ?: uri>@<sizePx>"`, so one file can hold several entries (the grid
102
+ * tile, the strip, a full-width poster) and each must go. Called when media is DELETED: without
103
+ * it the LruCache keeps serving a decoded copy of the file, so a securely erased photo carries
104
+ * on rendering in the list — and that bitmap is itself a recoverable, if downscaled, copy of the
105
+ * media the user asked to have destroyed.
106
+ *
107
+ * `snapshot()` is copied before iterating because [LruCache.remove] mutates the map.
108
+ */
109
+ fun evictSource(source: String) {
110
+ if (source.isEmpty()) return
111
+ val prefix = "$source@"
112
+ cache.snapshot().keys
113
+ .filter { it.startsWith(prefix) }
114
+ .forEach { cache.remove(it) }
115
+ }
116
+
117
+ fun putOverride(key: String, bitmap: Bitmap) {
118
+ cache.put(key, bitmap)
119
+ }
120
+
121
+ fun clear() {
122
+ cache.evictAll()
123
+ scope.cancel()
124
+ }
125
+
126
+ private fun loadBitmap(asset: CameraAsset, sizePx: Int): Bitmap? {
127
+ // A user-selected cover overrides frame extraction — decode that JPEG directly.
128
+ asset.coverUri?.let { cover ->
129
+ loadCoverBitmap(cover, sizePx)?.let { return it }
130
+ }
131
+
132
+ if (asset.type == "video") {
133
+ loadVideoFrame(asset, sizePx)?.let { return it }
134
+ }
135
+
136
+ // Try ContentResolver thumbnail API for content:// URIs on Q+
137
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && asset.uri.scheme == "content") {
138
+ val thumb = runCatching {
139
+ contentResolver.loadThumbnail(asset.uri, Size(sizePx, sizePx), null)
140
+ }.getOrNull()
141
+ if (thumb != null && !thumb.isMostlyBlack()) return thumb
142
+ thumb?.recycle()
143
+ }
144
+
145
+ return if (asset.type == "video") {
146
+ loadVideoFrame(asset, sizePx)
147
+ } else {
148
+ loadImageBitmap(asset, sizePx)
149
+ }
150
+ }
151
+
152
+ /** Decode a user-selected cover frame (content:// or file:// URI string) at [sizePx]. */
153
+ private fun loadCoverBitmap(coverUri: String, sizePx: Int): Bitmap? =
154
+ runCatching {
155
+ val uri = android.net.Uri.parse(coverUri)
156
+ val filePath = if (uri.scheme == "file") uri.path else null
157
+ if (filePath != null && File(filePath).exists()) {
158
+ val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
159
+ BitmapFactory.decodeFile(filePath, bounds)
160
+ val options = BitmapFactory.Options().apply {
161
+ inSampleSize = bounds.calculateSampleSize(sizePx)
162
+ // Halves thumbnail memory on small-heap devices — see decodeConfig().
163
+ inPreferredConfig = decodeConfig()
164
+ }
165
+ return@runCatching BitmapFactory.decodeFile(filePath, options)
166
+ }
167
+ val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
168
+ contentResolver.openInputStream(uri)?.use { stream ->
169
+ BitmapFactory.decodeStream(stream, null, bounds)
170
+ }
171
+ contentResolver.openInputStream(uri)?.use { stream ->
172
+ val options = BitmapFactory.Options().apply {
173
+ inSampleSize = bounds.calculateSampleSize(sizePx)
174
+ // Halves thumbnail memory on small-heap devices — see decodeConfig().
175
+ inPreferredConfig = decodeConfig()
176
+ }
177
+ BitmapFactory.decodeStream(stream, null, options)
178
+ }
179
+ }.getOrNull()
180
+
181
+ /**
182
+ * Decode a still thumbnail for an image asset.
183
+ *
184
+ * BitmapFactory decodes the FIRST frame of an animated GIF, which is exactly what a grid or
185
+ * strip tile wants: one static, cheap frame, no decoder kept alive, no per-tile animation
186
+ * competing with the scroll. The same call therefore covers GIF, PNG, WebP and JPEG — and, on
187
+ * devices whose platform decoders support them, HEIF (API 28+) and AVIF (API 31+). The only
188
+ * format-specific decision is the bitmap config (see [decodeConfig]).
189
+ *
190
+ * Where BitmapFactory returns nothing, [decodeViaImageDecoder] retries through ImageDecoder,
191
+ * which handles some HEIF/AVIF variants BitmapFactory declines. A device too old to decode the
192
+ * format at all yields null, and the caller shows the placeholder rather than a black tile.
193
+ */
194
+ private fun loadImageBitmap(asset: CameraAsset, sizePx: Int): Bitmap? {
195
+ // Formats the platform cannot decode on this API level (HEIF < 28, AVIF < 31) would waste a
196
+ // full decode attempt per tile on every bind. The asset is still listed and still sendable —
197
+ // only its pixels are unavailable here.
198
+ if (!ImageFormatSupport.isDecodable(asset)) return null
199
+
200
+ return runCatching {
201
+ val config = decodeConfig(mayHaveAlpha = asset.mayHaveAlphaChannel())
202
+ // For file:// URIs or assets with cachePath, read directly from the file
203
+ val filePath = asset.cachePath ?: asset.uri.path
204
+ if (filePath != null && File(filePath).exists()) {
205
+ val bounds = BitmapFactory.Options().apply { inJustDecodeBounds = true }
206
+ BitmapFactory.decodeFile(filePath, bounds)
207
+ val options = BitmapFactory.Options().apply {
208
+ inSampleSize = bounds.calculateSampleSize(sizePx)
209
+ // Halves thumbnail memory on small-heap devices — see decodeConfig().
210
+ inPreferredConfig = config
211
+ }
212
+ return@runCatching BitmapFactory.decodeFile(filePath, options)
213
+ }
214
+
215
+ // Fallback to ContentResolver for content:// URIs
216
+ val bounds = BitmapFactory.Options().apply {
217
+ inJustDecodeBounds = true
218
+ }
219
+ contentResolver.openInputStream(asset.uri)?.use { stream ->
220
+ BitmapFactory.decodeStream(stream, null, bounds)
221
+ }
222
+
223
+ contentResolver.openInputStream(asset.uri)?.use { stream ->
224
+ val options = BitmapFactory.Options().apply {
225
+ inSampleSize = bounds.calculateSampleSize(sizePx)
226
+ // Halves thumbnail memory on small-heap devices — see decodeConfig().
227
+ inPreferredConfig = config
228
+ }
229
+ BitmapFactory.decodeStream(stream, null, options)
230
+ }
231
+ }.getOrNull() ?: decodeViaImageDecoder(asset, sizePx)
232
+ }
233
+
234
+ /**
235
+ * Last-resort decode through ImageDecoder (API 28+), which reads some HEIF/AVIF files that
236
+ * BitmapFactory refuses. Downsamples during decode so a full-resolution frame is never
237
+ * materialised just to be scaled down for a tile.
238
+ */
239
+ private fun decodeViaImageDecoder(asset: CameraAsset, sizePx: Int): Bitmap? {
240
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return null
241
+ return runCatching {
242
+ val filePath = asset.cachePath ?: asset.uri.path
243
+ val source = if (filePath != null && File(filePath).exists()) {
244
+ ImageDecoder.createSource(File(filePath))
245
+ } else {
246
+ ImageDecoder.createSource(contentResolver, asset.uri)
247
+ }
248
+ ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
249
+ val maxDimension = maxOf(info.size.width, info.size.height)
250
+ if (maxDimension > sizePx && sizePx > 0) {
251
+ val scale = sizePx.toFloat() / maxDimension.toFloat()
252
+ decoder.setTargetSize(
253
+ (info.size.width * scale).toInt().coerceAtLeast(1),
254
+ (info.size.height * scale).toInt().coerceAtLeast(1)
255
+ )
256
+ }
257
+ // ImageDecoder yields a HARDWARE bitmap by default, which cannot be read back by
258
+ // isMostlyBlack()'s getPixel() and cannot be cached safely alongside software bitmaps.
259
+ decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
260
+ }
261
+ }.getOrNull()
262
+ }
263
+
264
+ private fun loadVideoFrame(asset: CameraAsset, sizePx: Int): Bitmap? =
265
+ runCatching {
266
+ val retriever = MediaMetadataRetriever()
267
+ try {
268
+ val filePath = asset.cachePath ?: asset.uri.path
269
+ if (filePath != null && File(filePath).exists()) {
270
+ retriever.setDataSource(filePath)
271
+ } else {
272
+ contentResolver.openAssetFileDescriptor(asset.uri, "r")?.use { descriptor ->
273
+ retriever.setDataSource(
274
+ descriptor.fileDescriptor,
275
+ descriptor.startOffset,
276
+ descriptor.length
277
+ )
278
+ } ?: return@runCatching null
279
+ }
280
+
281
+ val durationMs =
282
+ retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)?.toLongOrNull() ?: 0L
283
+ val candidateTimesUs = buildList {
284
+ add(1_000L)
285
+ add(100_000L)
286
+ add(500_000L)
287
+ add(1_000_000L)
288
+ if (durationMs > 0) {
289
+ add((durationMs * 500).coerceAtLeast(1_000L))
290
+ }
291
+ }
292
+
293
+ for (timeUs in candidateTimesUs) {
294
+ val frame = extractFrame(retriever, timeUs, MediaMetadataRetriever.OPTION_CLOSEST_SYNC, sizePx)
295
+ ?: extractFrame(retriever, timeUs, MediaMetadataRetriever.OPTION_CLOSEST, sizePx)
296
+ if (frame != null) {
297
+ if (!frame.isMostlyBlack()) {
298
+ return@runCatching downscaleBitmap(frame, sizePx)
299
+ }
300
+ frame.recycle()
301
+ }
302
+ }
303
+
304
+ extractFrame(retriever, 0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC, sizePx)?.let { fallback ->
305
+ downscaleBitmap(fallback, sizePx)
306
+ }
307
+ } finally {
308
+ retriever.release()
309
+ }
310
+ }.getOrNull()
311
+
312
+ /**
313
+ * Pull one frame, scaled DURING extraction wherever the platform allows it.
314
+ *
315
+ * `getFrameAtTime` always hands back a frame at the clip's coded resolution — 8 MB for 1080p,
316
+ * ~33 MB for 4K — which is then thrown away by [downscaleBitmap] to produce a 68 or 120 dp tile.
317
+ * The loop above tries several timestamps, so a single video thumbnail could allocate and
318
+ * discard that full-resolution buffer five times over, and several tiles decoding at once (the
319
+ * grid, the strip, and a just-recorded clip's own preloads) is a realistic way to run a
320
+ * small-heap device out of memory on nothing but thumbnails.
321
+ *
322
+ * `getScaledFrameAtTime` (API 27+) asks the decoder for the size we actually want, so the big
323
+ * intermediate never exists. Below API 27 the old behaviour is unavoidable and is kept.
324
+ *
325
+ * OutOfMemoryError is caught rather than allowed to propagate: it is an Error, so the
326
+ * `runCatching` in the caller would NOT contain it, and a thumbnail that cannot be decoded must
327
+ * degrade to a placeholder instead of taking the process down.
328
+ */
329
+ private fun extractFrame(
330
+ retriever: MediaMetadataRetriever,
331
+ timeUs: Long,
332
+ option: Int,
333
+ sizePx: Int
334
+ ): Bitmap? = try {
335
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1 && sizePx > 0) {
336
+ val videoW = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)?.toIntOrNull()
337
+ val videoH = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)?.toIntOrNull()
338
+ if (videoW != null && videoH != null && videoW > 0 && videoH > 0) {
339
+ val scale = sizePx.toFloat() / maxOf(videoW, videoH).toFloat()
340
+ val targetW = (videoW * scale).toInt().coerceAtLeast(1)
341
+ val targetH = (videoH * scale).toInt().coerceAtLeast(1)
342
+ retriever.getScaledFrameAtTime(timeUs, option, targetW, targetH)
343
+ } else {
344
+ retriever.getScaledFrameAtTime(timeUs, option, sizePx, sizePx)
345
+ }
346
+ } else {
347
+ retriever.getFrameAtTime(timeUs, option)
348
+ }
349
+ } catch (e: Exception) {
350
+ null
351
+ } catch (e: OutOfMemoryError) {
352
+ null
353
+ }
354
+
355
+ companion object {
356
+ /**
357
+ * Size the in-memory thumbnail cache as a fraction of the app's actual heap budget
358
+ * instead of a fixed 24 MB. A fixed cap is simultaneously too big for a low-RAM device
359
+ * (it competes with decode buffers and invites OOM/eviction thrash) and needlessly small
360
+ * on a high-end one. Using 1/8 of the max heap follows the standard Android bitmap-cache
361
+ * guidance and adapts automatically: a 512 MB-heap device gets ~64 MB, while a low-end
362
+ * 64 MB-heap device gets ~8 MB. Runtime.maxMemory() needs no Context, so the constructor
363
+ * signature is unchanged. Clamped to a sane floor/ceiling for predictability.
364
+ */
365
+ private fun cacheSizeKb(): Int {
366
+ val maxHeapKb = (Runtime.getRuntime().maxMemory() / 1024L)
367
+ val eighthKb = (maxHeapKb / 8L)
368
+ return eighthKb.coerceIn(6 * 1024L, 48 * 1024L).toInt()
369
+ }
370
+
371
+ /**
372
+ * On small-heap (≤128 MB) devices decode grid/list thumbnails as RGB_565: 2 bytes per
373
+ * pixel instead of ARGB_8888's 4, halving both decode allocation and cache footprint —
374
+ * so the same LruCache holds twice the thumbnails with zero extra memory. Thumbnails
375
+ * are small photographic previews; the 565 banding is not visible at these sizes.
376
+ * Full-size preview/editor decodes elsewhere keep ARGB_8888 fidelity.
377
+ */
378
+ val PREFER_565: Boolean =
379
+ Runtime.getRuntime().maxMemory() <= 128L * 1024L * 1024L
380
+
381
+ /**
382
+ * [mayHaveAlpha] opts a format out of the RGB_565 saving. RGB_565 has no alpha channel, so a
383
+ * transparent GIF/PNG/WebP/HEIF/AVIF decoded into it loses its transparency to solid BLACK —
384
+ * the tile renders as a black square on exactly the low-RAM devices the optimisation targets.
385
+ * Formats that can carry transparency are worth the extra 2 bytes/pixel; opaque JPEG (every
386
+ * camera capture and every cover frame) still takes the cheap path.
387
+ */
388
+ fun decodeConfig(mayHaveAlpha: Boolean = false): Bitmap.Config =
389
+ if (PREFER_565 && !mayHaveAlpha) Bitmap.Config.RGB_565 else Bitmap.Config.ARGB_8888
390
+
391
+ /**
392
+ * Whether this asset's container can hold transparency. Delegates to [ImageFormatSupport],
393
+ * which reads the MIME type first and the file name / URI extension second, so an asset that
394
+ * arrived without a MIME type is still classified correctly — and so adding a format there
395
+ * automatically fixes its alpha handling here.
396
+ */
397
+ internal fun CameraAsset.mayHaveAlphaChannel(): Boolean =
398
+ ImageFormatSupport.mayHaveAlpha(this)
399
+ }
400
+ }
401
+
402
+ private fun downscaleBitmap(source: Bitmap, targetSize: Int): Bitmap {
403
+ val maxDimension = maxOf(source.width, source.height)
404
+ if (maxDimension <= targetSize) return source
405
+
406
+ val scale = targetSize.toFloat() / maxDimension.toFloat()
407
+ val width = (source.width * scale).toInt().coerceAtLeast(1)
408
+ val height = (source.height * scale).toInt().coerceAtLeast(1)
409
+ val scaled = Bitmap.createScaledBitmap(source, width, height, true)
410
+ if (scaled !== source) {
411
+ source.recycle()
412
+ }
413
+ return scaled
414
+ }
415
+
416
+ /**
417
+ * Heuristic for "the frame extractor gave us nothing usable" — a thumbnail that is black at
418
+ * almost every sample point. Transparent pixels are deliberately NOT counted: a transparent
419
+ * GIF/PNG reads as (0,0,0,0) at every sample, which would condemn a perfectly good thumbnail
420
+ * as a failed decode and send it round the fallback path for the same result.
421
+ */
422
+ private fun Bitmap.isMostlyBlack(): Boolean {
423
+ if (width <= 0 || height <= 0) return true
424
+
425
+ val samplePoints = listOf(
426
+ width / 2 to height / 2,
427
+ width / 4 to height / 4,
428
+ (width * 3) / 4 to height / 4,
429
+ width / 4 to (height * 3) / 4,
430
+ (width * 3) / 4 to (height * 3) / 4
431
+ )
432
+
433
+ var darkSamples = 0
434
+ for ((x, y) in samplePoints) {
435
+ val pixel = getPixel(x.coerceIn(0, width - 1), y.coerceIn(0, height - 1))
436
+ if (Color.alpha(pixel) < 16) continue
437
+ if (Color.red(pixel) < 20 && Color.green(pixel) < 20 && Color.blue(pixel) < 20) {
438
+ darkSamples++
439
+ }
440
+ }
441
+
442
+ return darkSamples >= samplePoints.size - 1
443
+ }
444
+
445
+ private fun BitmapFactory.Options.calculateSampleSize(targetSize: Int): Int {
446
+ var sampleSize = 1
447
+ val halfHeight = outHeight / 2
448
+ val halfWidth = outWidth / 2
449
+
450
+ while (halfHeight / sampleSize >= targetSize && halfWidth / sampleSize >= targetSize) {
451
+ sampleSize *= 2
452
+ }
453
+
454
+ return sampleSize.coerceAtLeast(1)
455
+ }