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,378 @@
1
+ package com.customcamera
2
+
3
+ import android.content.ContentResolver
4
+ import android.content.ContentUris
5
+ import android.database.ContentObserver
6
+ import android.net.Uri
7
+ import android.os.Build
8
+ import android.os.Bundle
9
+ import android.os.Handler
10
+ import android.os.Looper
11
+ import android.provider.MediaStore
12
+
13
+ class MediaStoreRepository(
14
+ private val contentResolver: ContentResolver
15
+ ) {
16
+ private val mediaUri: Uri = MediaStore.Files.getContentUri("external")
17
+
18
+ fun queryRecent(
19
+ mediaTypes: String,
20
+ limit: Int,
21
+ offset: Int
22
+ ): List<CameraAsset> {
23
+ val selection = buildSelection(mediaTypes)
24
+ val projection = arrayOf(
25
+ MediaStore.Files.FileColumns._ID,
26
+ MediaStore.Files.FileColumns.MEDIA_TYPE,
27
+ MediaStore.MediaColumns.DISPLAY_NAME,
28
+ MediaStore.MediaColumns.MIME_TYPE,
29
+ MediaStore.MediaColumns.WIDTH,
30
+ MediaStore.MediaColumns.HEIGHT,
31
+ MediaStore.MediaColumns.SIZE,
32
+ MediaStore.MediaColumns.DATE_TAKEN,
33
+ MediaStore.MediaColumns.DATE_MODIFIED,
34
+ MediaStore.Video.VideoColumns.DURATION
35
+ )
36
+
37
+ // MediaProvider validates sort-order SQL, and how strictly it does so varies by OEM and API
38
+ // level. If the COALESCE expression is ever rejected, fall back to the plain column sort
39
+ // rather than letting the whole media list fail to load — a degraded ordering is recoverable,
40
+ // an empty gallery is not.
41
+ val cursor = runQuery(projection, selection, SORT_ORDER, limit, offset)
42
+ ?: runQuery(projection, selection, FALLBACK_SORT_ORDER, limit, offset)
43
+
44
+ cursor?.use {
45
+ val idColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
46
+ val mediaTypeColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE)
47
+ val displayNameColumn = it.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
48
+ val mimeTypeColumn = it.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE)
49
+ val widthColumn = it.getColumnIndex(MediaStore.MediaColumns.WIDTH)
50
+ val heightColumn = it.getColumnIndex(MediaStore.MediaColumns.HEIGHT)
51
+ val sizeColumn = it.getColumnIndex(MediaStore.MediaColumns.SIZE)
52
+ val dateTakenColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_TAKEN)
53
+ val dateModifiedColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)
54
+ val durationColumn = it.getColumnIndex(MediaStore.Video.VideoColumns.DURATION)
55
+ val assets = mutableListOf<CameraAsset>()
56
+
57
+ while (it.moveToNext()) {
58
+ val id = it.getLong(idColumn)
59
+ val mediaType = it.getInt(mediaTypeColumn)
60
+ val mime = it.getNullableString(mimeTypeColumn)
61
+ // Map the MediaStore MEDIA_TYPE (and, for documents, the MIME type) onto the
62
+ // app-wide asset type vocabulary: "photo", "video", "audio", "document".
63
+ val type = when (mediaType) {
64
+ MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> "video"
65
+ MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO -> "audio"
66
+ MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> "photo"
67
+ // Anything else is decided by the dedicated document media type (value 6,
68
+ // API 30+) and the MIME family — never blindly defaulted to "photo", which
69
+ // is what made documents surface under the Photos filter. A non-image/
70
+ // video/audio file with no better signal is treated as a document.
71
+ else -> when {
72
+ mediaType == MEDIA_TYPE_DOCUMENT -> "document"
73
+ isDocumentMime(mime) -> "document"
74
+ mime != null && mime.startsWith("audio/") -> "audio"
75
+ mime != null && mime.startsWith("video/") -> "video"
76
+ ImageFormatSupport.isImageMimeType(mime) -> "photo"
77
+ // No usable MIME type: fall back to the file extension before giving up, which is
78
+ // what rescues rows scanned without a type at all (common for files another app
79
+ // wrote into its own Android/media directory).
80
+ ImageFormatSupport.fromFileName(it.getNullableString(displayNameColumn)) != null ->
81
+ "photo"
82
+ else -> "document"
83
+ }
84
+ }
85
+ // Build a type-appropriate, openable content URI. The typed collections (Images/Video/
86
+ // Audio) are used only when MediaStore itself filed the row under that MEDIA_TYPE — an
87
+ // id from the Files collection is not guaranteed to resolve inside a typed collection,
88
+ // so a row recognised as an image purely by its MIME type keeps the generic Files URI
89
+ // it is known to be addressable by. Documents likewise resolve through Files.
90
+ val contentUri = when {
91
+ mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO ->
92
+ ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id)
93
+ mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO ->
94
+ ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
95
+ mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE ->
96
+ ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
97
+ else -> ContentUris.withAppendedId(mediaUri, id)
98
+ }
99
+ val dateTaken = it.getNullableLong(dateTakenColumn)
100
+ ?: (it.getNullableLong(dateModifiedColumn)?.times(1000))
101
+
102
+ assets.add(
103
+ CameraAsset(
104
+ id = id.toString(),
105
+ uri = contentUri,
106
+ type = type,
107
+ fileName = it.getNullableString(displayNameColumn),
108
+ mimeType = mime,
109
+ width = it.getNullableInt(widthColumn),
110
+ height = it.getNullableInt(heightColumn),
111
+ duration = it.getNullableLong(durationColumn),
112
+ size = it.getNullableLong(sizeColumn),
113
+ dateTaken = dateTaken
114
+ )
115
+ )
116
+ }
117
+
118
+ return assets
119
+ }
120
+
121
+ return emptyList()
122
+ }
123
+
124
+ /**
125
+ * Run the paged media query with [sortOrder], returning null if the provider rejects it so the
126
+ * caller can retry with a simpler ordering. Only a *rejected query* yields null — a query that
127
+ * legitimately matches nothing still returns an empty cursor, so an empty gallery never
128
+ * triggers a pointless second round trip.
129
+ */
130
+ private fun runQuery(
131
+ projection: Array<String>,
132
+ selection: String,
133
+ sortOrder: String,
134
+ limit: Int,
135
+ offset: Int
136
+ ): android.database.Cursor? =
137
+ runCatching {
138
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
139
+ contentResolver.query(
140
+ mediaUri,
141
+ projection,
142
+ Bundle().apply {
143
+ putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
144
+ putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, emptyArray<String>())
145
+ putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, sortOrder)
146
+ putInt(ContentResolver.QUERY_ARG_LIMIT, limit)
147
+ putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
148
+ },
149
+ null
150
+ )
151
+ } else {
152
+ @Suppress("DEPRECATION")
153
+ contentResolver.query(
154
+ mediaUri,
155
+ projection,
156
+ selection,
157
+ emptyArray<String>(),
158
+ "$sortOrder LIMIT $limit OFFSET $offset"
159
+ )
160
+ }
161
+ }.getOrNull()
162
+
163
+ fun registerObserver(onChanged: () -> Unit): ContentObserver {
164
+ val observer =
165
+ object : ContentObserver(Handler(Looper.getMainLooper())) {
166
+ override fun onChange(selfChange: Boolean) {
167
+ onChanged()
168
+ }
169
+
170
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
171
+ onChanged()
172
+ }
173
+ }
174
+ contentResolver.registerContentObserver(
175
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
176
+ true,
177
+ observer
178
+ )
179
+ contentResolver.registerContentObserver(
180
+ MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
181
+ true,
182
+ observer
183
+ )
184
+ contentResolver.registerContentObserver(
185
+ MediaStore.Files.getContentUri("external"),
186
+ true,
187
+ observer
188
+ )
189
+ return observer
190
+ }
191
+
192
+ fun unregisterObserver(observer: ContentObserver) {
193
+ contentResolver.unregisterContentObserver(observer)
194
+ }
195
+
196
+ fun assetFromUri(uri: Uri, type: String): CameraAsset =
197
+ queryAsset(uri, type)
198
+ ?: CameraAsset(
199
+ id = uri.lastPathSegment ?: uri.toString(),
200
+ uri = uri,
201
+ type = type,
202
+ fileName = null,
203
+ mimeType = null,
204
+ width = null,
205
+ height = null,
206
+ duration = null,
207
+ size = null,
208
+ dateTaken = System.currentTimeMillis()
209
+ )
210
+
211
+ private fun queryAsset(uri: Uri, type: String): CameraAsset? {
212
+ val baseProjection = arrayOf(
213
+ MediaStore.MediaColumns._ID,
214
+ MediaStore.MediaColumns.DISPLAY_NAME,
215
+ MediaStore.MediaColumns.MIME_TYPE,
216
+ MediaStore.MediaColumns.WIDTH,
217
+ MediaStore.MediaColumns.HEIGHT,
218
+ MediaStore.MediaColumns.SIZE,
219
+ MediaStore.MediaColumns.DATE_TAKEN,
220
+ MediaStore.MediaColumns.DATE_MODIFIED
221
+ )
222
+ val projection =
223
+ if (type == "video") {
224
+ baseProjection + MediaStore.Video.VideoColumns.DURATION
225
+ } else {
226
+ baseProjection
227
+ }
228
+
229
+ contentResolver.query(uri, projection, null, null, null)?.use {
230
+ if (!it.moveToFirst()) {
231
+ return null
232
+ }
233
+
234
+ val idColumn = it.getColumnIndex(MediaStore.MediaColumns._ID)
235
+ val displayNameColumn = it.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
236
+ val mimeTypeColumn = it.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)
237
+ val widthColumn = it.getColumnIndex(MediaStore.MediaColumns.WIDTH)
238
+ val heightColumn = it.getColumnIndex(MediaStore.MediaColumns.HEIGHT)
239
+ val sizeColumn = it.getColumnIndex(MediaStore.MediaColumns.SIZE)
240
+ val dateTakenColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_TAKEN)
241
+ val dateModifiedColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)
242
+ val durationColumn = it.getColumnIndex(MediaStore.Video.VideoColumns.DURATION)
243
+ val id = it.getNullableLong(idColumn)?.toString() ?: (uri.lastPathSegment ?: uri.toString())
244
+ val dateTaken = it.getNullableLong(dateTakenColumn)
245
+ ?: (it.getNullableLong(dateModifiedColumn)?.times(1000))
246
+
247
+ return CameraAsset(
248
+ id = id,
249
+ uri = uri,
250
+ type = type,
251
+ fileName = it.getNullableString(displayNameColumn),
252
+ mimeType = it.getNullableString(mimeTypeColumn),
253
+ width = it.getNullableInt(widthColumn),
254
+ height = it.getNullableInt(heightColumn),
255
+ duration = it.getNullableLong(durationColumn),
256
+ size = it.getNullableLong(sizeColumn),
257
+ dateTaken = dateTaken ?: System.currentTimeMillis()
258
+ )
259
+ }
260
+
261
+ return null
262
+ }
263
+
264
+ private fun buildSelection(mediaTypes: String): String {
265
+ val videoType = MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
266
+ val audioType = MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO
267
+ val typeCol = MediaStore.Files.FileColumns.MEDIA_TYPE
268
+ return when (mediaTypes) {
269
+ // "image" is the gallery-filter alias for the app's internal "photo" type.
270
+ "photo", "image" -> buildImageSelection()
271
+ "video" -> "$typeCol = $videoType"
272
+ "audio" -> "$typeCol = $audioType"
273
+ "document" -> buildDocumentSelection()
274
+ // Default ("all"/"Recent"): images + videos, matching the prior behaviour.
275
+ else -> "(${buildImageSelection()} OR $typeCol = $videoType)"
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Images are matched on MEDIA_TYPE **or** an `image/…` MIME type.
281
+ *
282
+ * MEDIA_TYPE alone is not sufficient in practice. Files written by other apps into their own
283
+ * `Android/media/…` directory (WhatsApp being the common case) are indexed by a scan that
284
+ * sometimes lands them under MEDIA_TYPE_NONE while still recording the correct MIME type — so
285
+ * a MEDIA_TYPE-only filter silently hid every one of them. Matching the MIME family as well
286
+ * recovers those rows, and covers every image container the platform knows — including the
287
+ * HEIF and AVIF files that motivated this — without enumerating formats here.
288
+ */
289
+ private fun buildImageSelection(): String {
290
+ val typeCol = MediaStore.Files.FileColumns.MEDIA_TYPE
291
+ val mimeCol = MediaStore.MediaColumns.MIME_TYPE
292
+ val imageType = MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
293
+ return "($typeCol = $imageType OR $mimeCol LIKE 'image/%')"
294
+ }
295
+
296
+ /**
297
+ * Documents are not a single MediaStore MEDIA_TYPE across all Android versions, so match
298
+ * on both the dedicated document media type (value 6, added in API 30) and a curated list
299
+ * of document MIME types (PDF, Word, Excel, PowerPoint, text, etc.). The literal 6 is used
300
+ * so the selection compiles and runs on every supported API level.
301
+ */
302
+ private fun buildDocumentSelection(): String {
303
+ val typeCol = MediaStore.Files.FileColumns.MEDIA_TYPE
304
+ val mimeCol = MediaStore.MediaColumns.MIME_TYPE
305
+ val mimeList = DOCUMENT_MIME_TYPES.joinToString(",") { "'$it'" }
306
+ return "($typeCol = $MEDIA_TYPE_DOCUMENT OR $mimeCol IN ($mimeList))"
307
+ }
308
+
309
+ private fun isDocumentMime(mime: String?): Boolean {
310
+ if (mime == null) return false
311
+ return DOCUMENT_MIME_TYPES.contains(mime)
312
+ }
313
+
314
+ companion object {
315
+ // MediaStore.Files.FileColumns.MEDIA_TYPE_DOCUMENT — the dedicated document media
316
+ // type added in API 30. Referenced as a literal so the code compiles and the
317
+ // selection/classification agree on every supported API level.
318
+ private const val MEDIA_TYPE_DOCUMENT = 6
319
+
320
+ /**
321
+ * Newest-first ordering that treats an undated row as being as old as its file, instead of
322
+ * as having no date at all.
323
+ *
324
+ * DATE_TAKEN is populated from EXIF/container metadata, so it is NULL for a large share of
325
+ * real-world images — anything stripped of EXIF in transit, which includes essentially every
326
+ * photo received through WhatsApp. Ordering by `DATE_TAKEN DESC` alone sorts those NULLs
327
+ * *after* every dated row in SQLite, so they were pushed past the paging window (and far
328
+ * past the strip's 80-item cap) and appeared nowhere at all — the reported "images don't
329
+ * show up" bug.
330
+ *
331
+ * COALESCE falls back to DATE_MODIFIED (stored in seconds, hence ×1000) so undated files
332
+ * interleave by their real recency. This mirrors the per-row `dateTaken` fallback the
333
+ * cursor read already performed, which is why the list and its ordering disagreed.
334
+ */
335
+ private val SORT_ORDER =
336
+ "COALESCE(${MediaStore.MediaColumns.DATE_TAKEN}, " +
337
+ "${MediaStore.MediaColumns.DATE_MODIFIED} * 1000) DESC, " +
338
+ "${MediaStore.Files.FileColumns._ID} DESC"
339
+
340
+ /**
341
+ * Ordering used only if a provider refuses [SORT_ORDER]'s COALESCE expression. DATE_MODIFIED
342
+ * leads because, unlike DATE_TAKEN, it is populated for every row — so undated images still
343
+ * appear in a sensible position instead of being banished to the end of the list.
344
+ */
345
+ private val FALLBACK_SORT_ORDER =
346
+ "${MediaStore.MediaColumns.DATE_MODIFIED} DESC, " +
347
+ "${MediaStore.Files.FileColumns._ID} DESC"
348
+
349
+ // Supported document MIME types for the "Document" gallery filter. Kept in sync with
350
+ // buildDocumentSelection() so the SQL filter and the per-row type mapping agree.
351
+ private val DOCUMENT_MIME_TYPES = setOf(
352
+ "application/pdf",
353
+ "application/msword",
354
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
355
+ "application/vnd.ms-excel",
356
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
357
+ "application/vnd.ms-powerpoint",
358
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
359
+ "text/plain",
360
+ "text/csv",
361
+ "application/rtf",
362
+ "text/rtf",
363
+ "application/vnd.oasis.opendocument.text",
364
+ "application/vnd.oasis.opendocument.spreadsheet",
365
+ "application/vnd.oasis.opendocument.presentation",
366
+ "application/epub+zip"
367
+ )
368
+ }
369
+ }
370
+
371
+ private fun android.database.Cursor.getNullableString(index: Int): String? =
372
+ if (index < 0 || isNull(index)) null else getString(index)
373
+
374
+ private fun android.database.Cursor.getNullableInt(index: Int): Int? =
375
+ if (index < 0 || isNull(index)) null else getInt(index)
376
+
377
+ private fun android.database.Cursor.getNullableLong(index: Int): Long? =
378
+ if (index < 0 || isNull(index)) null else getLong(index)
@@ -0,0 +1,286 @@
1
+ package com.customcamera
2
+
3
+ import android.content.Context
4
+ import android.net.Uri
5
+ import android.widget.ImageView
6
+ import com.facebook.react.uimanager.SimpleViewManager
7
+ import com.facebook.react.uimanager.ThemedReactContext
8
+ import com.facebook.react.uimanager.annotations.ReactProp
9
+
10
+ /**
11
+ * ReactNative view manager for media-list thumbnails.
12
+ *
13
+ * A JS `<Image>` can only show a still that Fresco can decode: a video file renders nothing at
14
+ * all (there is no frame to read without an extractor), and HEIF/AVIF need the platform decoders.
15
+ * Both cases used to leave the row with an empty tile, so list rows fell back to a generic
16
+ * file-style placeholder. This view hands the work to [ThumbnailLoader] — the same loader the
17
+ * native camera strip and grid already use — so a row shows the media's OWN first usable frame:
18
+ * the user-picked cover if the clip has one, else an extracted video frame, else the decoded
19
+ * image, all downsampled to the tile and served from a shared bitmap cache.
20
+ *
21
+ * Prop contract mirrors [AnimatedImageViewManager]: `src` with a same-request guard so a recycled
22
+ * FlatList cell never keeps the previous row's frame, plus the few asset fields the loader needs
23
+ * to pick a decode path.
24
+ */
25
+ class MediaThumbnailViewManager : SimpleViewManager<MediaThumbnailView>() {
26
+ override fun getName() = "CustomMediaThumbnail"
27
+
28
+ override fun createViewInstance(reactContext: ThemedReactContext): MediaThumbnailView =
29
+ MediaThumbnailView(reactContext)
30
+
31
+ override fun onDropViewInstance(view: MediaThumbnailView) {
32
+ view.release()
33
+ super.onDropViewInstance(view)
34
+ }
35
+
36
+ /**
37
+ * ReactNative applies props one setter at a time and in no guaranteed order, so a setter must
38
+ * only RECORD its value — the decode is started once here, when the whole batch has landed.
39
+ *
40
+ * Requesting from the setters instead made the request identity depend on which props happened
41
+ * to have arrived: a `src` that landed before `mimeType`/`fileName` pinned the decode to a
42
+ * format-hint-less asset, and the later hints — being absent from the request key — never
43
+ * triggered a retry. Video frames never noticed (MediaMetadataRetriever reads the container
44
+ * itself), but every still goes through [ImageFormatSupport], which answers "can this device
45
+ * decode it" and "does it carry alpha" from exactly those two fields.
46
+ */
47
+ protected override fun onAfterUpdateTransaction(view: MediaThumbnailView) {
48
+ super.onAfterUpdateTransaction(view)
49
+ view.onPropsApplied()
50
+ }
51
+
52
+ /** The media's own URI (`content://`, `file://` or a plain path). */
53
+ @ReactProp(name = "src")
54
+ fun setSrc(view: MediaThumbnailView, src: String?) {
55
+ view.setSrc(src)
56
+ }
57
+
58
+ /**
59
+ * `video` selects frame extraction; anything else is decoded as a still. Both the JS
60
+ * (`photo`) and database (`image`) spellings mean the same thing here.
61
+ */
62
+ @ReactProp(name = "mediaType")
63
+ fun setMediaType(view: MediaThumbnailView, mediaType: String?) {
64
+ view.setMediaType(mediaType)
65
+ }
66
+
67
+ /** Cached copy of the file, read in preference to the URI when it still exists on disk. */
68
+ @ReactProp(name = "cachePath")
69
+ fun setCachePath(view: MediaThumbnailView, cachePath: String?) {
70
+ view.setCachePath(cachePath)
71
+ }
72
+
73
+ /** A video's user-selected cover frame; overrides frame extraction when present. */
74
+ @ReactProp(name = "coverUri")
75
+ fun setCoverUri(view: MediaThumbnailView, coverUri: String?) {
76
+ view.setCoverUri(coverUri)
77
+ }
78
+
79
+ /** Used to decide decodability and alpha handling — see ImageFormatSupport. */
80
+ @ReactProp(name = "mimeType")
81
+ fun setMimeType(view: MediaThumbnailView, mimeType: String?) {
82
+ view.setMimeType(mimeType)
83
+ }
84
+
85
+ /** Fallback format hint for assets that arrived without a MIME type. */
86
+ @ReactProp(name = "fileName")
87
+ fun setFileName(view: MediaThumbnailView, fileName: String?) {
88
+ view.setFileName(fileName)
89
+ }
90
+
91
+ /** `cover` (default) or `contain`, matching the `resizeMode` names used on the JS side. */
92
+ @ReactProp(name = "resizeMode")
93
+ fun setResizeMode(view: MediaThumbnailView, resizeMode: String?) {
94
+ view.setResizeMode(resizeMode)
95
+ }
96
+
97
+ /**
98
+ * Decode target in pixels, for the rare case a consumer wants a sharper (or cheaper) bitmap
99
+ * than the tile's own measured size. 0 (the default) means "use the measured size".
100
+ */
101
+ @ReactProp(name = "thumbnailSize", defaultInt = 0)
102
+ fun setThumbnailSize(view: MediaThumbnailView, thumbnailSize: Int) {
103
+ view.setThumbnailSize(thumbnailSize)
104
+ }
105
+ }
106
+
107
+ /**
108
+ * An [ImageView] that paints the asset's own thumbnail, decoded off the main thread by the
109
+ * shared [ThumbnailLoader].
110
+ *
111
+ * Nothing here writes to the file: every path is a read-only decode.
112
+ */
113
+ class MediaThumbnailView(context: Context) : ImageView(context) {
114
+ private var src: String? = null
115
+ private var mediaType: String? = null
116
+ private var cachePath: String? = null
117
+ private var coverUri: String? = null
118
+ private var mimeType: String? = null
119
+ private var fileName: String? = null
120
+ private var requestedSize = 0
121
+
122
+ /**
123
+ * Identity of the request currently painted or in flight, so a re-layout or a prop update that
124
+ * changes nothing the loader reads does not re-decode. It carries every field that CAN change
125
+ * the decode — source, kind, cache copy, format hints and target size — because a field left
126
+ * out of it is a field whose later arrival looks like a repeat of the request already made.
127
+ */
128
+ private var currentKey: String? = null
129
+
130
+ /**
131
+ * Bumped on every request. A decode that finishes after its row was recycled compares its
132
+ * captured value against this and drops the bitmap instead of painting it into the wrong row.
133
+ */
134
+ private var generation = 0
135
+
136
+ init {
137
+ scaleType = ScaleType.CENTER_CROP
138
+ adjustViewBounds = false
139
+ cropToPadding = true
140
+ }
141
+
142
+ // Every setter only records its value; the decode starts from onPropsApplied() once the whole
143
+ // prop batch has landed — see MediaThumbnailViewManager.onAfterUpdateTransaction.
144
+ fun setSrc(src: String?) {
145
+ this.src = src
146
+ }
147
+
148
+ fun setMediaType(mediaType: String?) {
149
+ this.mediaType = mediaType
150
+ }
151
+
152
+ fun setCachePath(cachePath: String?) {
153
+ this.cachePath = cachePath
154
+ }
155
+
156
+ fun setCoverUri(coverUri: String?) {
157
+ this.coverUri = coverUri
158
+ }
159
+
160
+ fun setMimeType(mimeType: String?) {
161
+ this.mimeType = mimeType
162
+ }
163
+
164
+ fun setFileName(fileName: String?) {
165
+ this.fileName = fileName
166
+ }
167
+
168
+ fun setResizeMode(resizeMode: String?) {
169
+ scaleType = if (resizeMode == "contain") ScaleType.FIT_CENTER else ScaleType.CENTER_CROP
170
+ adjustViewBounds = false
171
+ cropToPadding = true
172
+ }
173
+
174
+ fun setThumbnailSize(thumbnailSize: Int) {
175
+ this.requestedSize = thumbnailSize
176
+ }
177
+
178
+ /** Every prop in this update has been applied — decode against the complete set. */
179
+ fun onPropsApplied() {
180
+ requestThumbnail()
181
+ }
182
+
183
+ /** Drop the frame and orphan any in-flight decode; called when ReactNative discards the view. */
184
+ fun release() {
185
+ generation++
186
+ currentKey = null
187
+ setImageDrawable(null)
188
+ }
189
+
190
+ // The tile's decode size is its own measured size, so the request usually can't be made until
191
+ // the first layout pass. Props set before then leave `currentKey` null and land here instead.
192
+ override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
193
+ super.onSizeChanged(width, height, oldWidth, oldHeight)
194
+ requestThumbnail()
195
+ }
196
+
197
+ private fun requestThumbnail() {
198
+ val source = src
199
+ if (source.isNullOrEmpty()) {
200
+ generation++
201
+ currentKey = null
202
+ setImageDrawable(null)
203
+ return
204
+ }
205
+
206
+ val size = decodeSize()
207
+ if (size <= 0) return
208
+
209
+ // Parsed before the request is claimed, so an unusable URI leaves `currentKey` untouched and
210
+ // a later, valid prop update is not mistaken for a repeat of this one.
211
+ val uri = runCatching { Uri.parse(source) }.getOrNull() ?: return
212
+
213
+ // Identity of the decode, not of the file: every field the loader reads belongs here, or a
214
+ // later prop update that genuinely changes the result is mistaken for a repeat of this one.
215
+ val key = "${coverUri ?: source}|${normalizedType()}|$cachePath|$mimeType|$fileName|$size"
216
+ if (key == currentKey) return
217
+ currentKey = key
218
+
219
+ generation++
220
+ val token = generation
221
+ // Clear first: a recycled cell must never show the previous row's frame while this one
222
+ // decodes.
223
+ setImageDrawable(null)
224
+
225
+ val asset = CameraAsset(
226
+ id = key,
227
+ uri = uri,
228
+ type = normalizedType(),
229
+ fileName = fileName,
230
+ mimeType = mimeType,
231
+ width = null,
232
+ height = null,
233
+ duration = null,
234
+ size = null,
235
+ dateTaken = null,
236
+ cachePath = cachePath,
237
+ coverUri = coverUri
238
+ )
239
+
240
+ sharedLoader(context).requestBitmap(asset, size) { bitmap ->
241
+ if (token == generation) {
242
+ setImageBitmap(bitmap)
243
+ }
244
+ }
245
+ }
246
+
247
+ /** The tile's measured size wins; the prop is only a fallback for a not-yet-laid-out view. */
248
+ private fun decodeSize(): Int {
249
+ val measured = maxOf(width, height)
250
+ return if (measured > 0) measured else requestedSize
251
+ }
252
+
253
+ /** [ThumbnailLoader] only distinguishes `video` from everything else. */
254
+ private fun normalizedType(): String = if (mediaType == "video") "video" else "photo"
255
+
256
+ companion object {
257
+ /**
258
+ * One loader for every thumbnail view in the app: its LruCache is what makes scrolling back
259
+ * up a list free, and a per-view loader would cache nothing and start a coroutine scope per
260
+ * row. Sized against the heap and bounded by [ThumbnailLoader], so it is never cleared —
261
+ * `clear()` would cancel the scope shared by every other row.
262
+ */
263
+ @Volatile
264
+ private var loader: ThumbnailLoader? = null
265
+
266
+ private fun sharedLoader(context: Context): ThumbnailLoader =
267
+ loader ?: synchronized(this) {
268
+ loader ?: ThumbnailLoader(context.applicationContext.contentResolver)
269
+ .also { loader = it }
270
+ }
271
+
272
+ /**
273
+ * Drop any cached bitmap decoded from [source] on the shared loader.
274
+ *
275
+ * Used by [SecureMediaDeleter]: a decoded thumbnail outlives the file it came from, so a
276
+ * securely erased photo would keep rendering in the media list from cache — and that bitmap
277
+ * is a real, if downscaled, copy of media the user asked to be destroyed. A no-op when no
278
+ * thumbnail has been requested yet in this process (the loader is created lazily), which is
279
+ * correct: an unbuilt cache holds nothing to evict.
280
+ */
281
+ fun evictFromSharedCache(source: String?) {
282
+ if (source.isNullOrEmpty()) return
283
+ loader?.evictSource(source)
284
+ }
285
+ }
286
+ }