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,493 @@
1
+ package com.customcamera
2
+
3
+ import android.content.Context
4
+ import android.net.Uri
5
+ import android.provider.MediaStore
6
+ import android.util.Log
7
+ import kotlinx.coroutines.Dispatchers
8
+ import kotlinx.coroutines.withContext
9
+ import java.io.File
10
+ import java.util.Locale
11
+
12
+ /**
13
+ * The single native entry point for destroying one media item and every trace of it.
14
+ *
15
+ * Deleting a photo or video in this app is not one delete. A single captured clip can exist as the
16
+ * published file under `media_documents/`, the cache file it was published from, a compressed
17
+ * variant under `compressed/`, a format-converted copy under `exported/`, an edited render and a
18
+ * chosen cover frame under `edited/`, a downloaded copy under `downloads/`, a decoded bitmap in
19
+ * [ThumbnailLoader]'s LruCache, an entry in [MediaCacheManager]'s metadata, another in
20
+ * [MediaMetadataStore], and a row in SQLCipher. Unlinking only the file the UI happened to be
21
+ * showing leaves the media fully recoverable from any of the others โ€” and leaves the library
22
+ * counting an item whose file is gone.
23
+ *
24
+ * So the order here is deliberate and each step depends on the one before it:
25
+ *
26
+ * 1. Resolve every on-disk copy of this asset โ€” the primary file plus each derived variant.
27
+ * 2. [SecureFileEraser] overwrites each one with encrypted CSPRNG garbage `2 MiB` longer than
28
+ * the file, flushes it to storage, then unlinks it. The primary file's outcome is what the
29
+ * caller is told about; a variant that resists deletion is logged and reported separately
30
+ * rather than being allowed to fail the whole operation silently.
31
+ * 3. Only then are the in-memory and on-disk REFERENCES dropped (thumbnail bitmaps, cache
32
+ * metadata, storage metadata). Dropping them first would lose the very paths step 1 needs,
33
+ * which is how an orphaned copy survives a delete.
34
+ *
35
+ * The SQLCipher row is deliberately NOT touched here โ€” it is owned by the JS repository layer
36
+ * ([MediaRepository]), which removes it once this reports the file is gone. Native and JS writing
37
+ * to the same encrypted database from two directions is how rows and files drift apart.
38
+ *
39
+ * Every method is `suspend` on [Dispatchers.IO]. A 2 GB clip takes real seconds to overwrite, and
40
+ * none of it may run on the main thread.
41
+ */
42
+ internal object SecureMediaDeleter {
43
+ private const val TAG = "SecureMediaDeleter"
44
+
45
+ /**
46
+ * Cache subdirectories that can hold a copy โ€” or a derivative โ€” of a media file.
47
+ *
48
+ * Every one of these is swept for files belonging to the asset being deleted, because each is a
49
+ * place the editor, the compressor, the exporter or the viewer is known to write a second copy
50
+ * of the same pixels. Kept as a list of names (rather than resolved paths) so it stays readable
51
+ * next to `file_paths.xml`, which declares the same set.
52
+ */
53
+ private val DERIVED_CACHE_DIRS = listOf(
54
+ "camera_media_cache", // MediaCacheManager's own store
55
+ "media_cache", // CacheCleanupManager's sweep target
56
+ "media",
57
+ "compressed", // Compress Video / Compress Image variants
58
+ "compression_cache", // persistent CompressionCacheStore master outputs
59
+ "exported", // "Export By" format conversions
60
+ "edited", // baked edits + chosen video cover frames
61
+ "downloads", // viewer "download a copy"
62
+ "shared" // copies staged for the share sheet
63
+ )
64
+
65
+ /** What one secure deletion actually achieved. */
66
+ data class Report(
67
+ /** True when the primary file is gone from the filesystem. */
68
+ val deleted: Boolean,
69
+ /**
70
+ * True ONLY when the primary file's bytes were overwritten and flushed before it was
71
+ * unlinked. When this is false the caller must NOT tell the user the media was securely
72
+ * erased, even if [deleted] is true โ€” see [SecureFileEraser.Result.securelyOverwritten].
73
+ */
74
+ val securelyOverwritten: Boolean,
75
+ /** Exact size of the primary file before the overwrite. */
76
+ val originalBytes: Long,
77
+ /** Bytes written over the primary file, including the padding past its end. */
78
+ val overwrittenBytes: Long,
79
+ /** Extra copies (variants, thumbnails on disk, temp files) securely erased alongside it. */
80
+ val derivedFilesErased: Int,
81
+ /** Extra copies that could NOT be erased. Non-zero means media may still be on the device. */
82
+ val derivedFilesFailed: Int,
83
+ /** Present when any part of the operation failed. */
84
+ val error: String? = null
85
+ ) {
86
+ /** Rolled-up truth for the UI: everything found was overwritten, nothing was left behind. */
87
+ val fullySecure: Boolean get() = deleted && securelyOverwritten && derivedFilesFailed == 0
88
+ }
89
+
90
+ /**
91
+ * Securely delete the media behind [uriString], plus every cached copy, derived variant and
92
+ * metadata reference the app holds for it.
93
+ *
94
+ * [assetId] and [fileName], when known, widen the sweep in step 1: derived variants are named
95
+ * after the asset id (`cmp_<id>_p60.mp4`, `edit_<id>_webp.webp`, `cover_<id>_<ts>.jpg`) rather
96
+ * than after the primary file, so without the id those copies are invisible to a path-based
97
+ * search and would survive the delete.
98
+ *
99
+ * [extraUris] is for copies the CALLER knows about that name-matching cannot find โ€” most
100
+ * importantly the cache file of an item whose published document is the primary. Each is erased
101
+ * with the same guarantees as the primary and counted among the derived copies.
102
+ */
103
+ suspend fun secureDelete(
104
+ context: Context,
105
+ uriString: String,
106
+ assetId: String? = null,
107
+ fileName: String? = null,
108
+ coverUri: String? = null,
109
+ extraUris: List<String> = emptyList()
110
+ ): Report = withContext(Dispatchers.IO) {
111
+ val appContext = context.applicationContext
112
+ val primary = resolveFile(appContext, uriString)
113
+
114
+ // Collect derived copies BEFORE the primary is erased: the primary's own name is one of the
115
+ // things that identifies them, and once it is renamed-and-unlinked that link is gone.
116
+ val derived = collectDerivedFiles(
117
+ appContext,
118
+ primary = primary,
119
+ assetId = assetId,
120
+ fileName = fileName ?: primary?.name,
121
+ coverUri = coverUri,
122
+ extraUris = extraUris
123
+ )
124
+
125
+ val primaryResult = erasePrimary(appContext, uriString, primary)
126
+
127
+ var erased = 0
128
+ var failed = 0
129
+ for (file in derived) {
130
+ val result = SecureFileEraser.erase(file)
131
+ if (result.securelyOverwritten && !file.exists()) {
132
+ erased++
133
+ } else {
134
+ failed++
135
+ Log.e(
136
+ TAG,
137
+ "Derived copy survived secure deletion: ${file.absolutePath} (${result.error ?: "unlink failed"})"
138
+ )
139
+ }
140
+ }
141
+
142
+ // References last โ€” see the class doc. These are best-effort by design: a stale metadata entry
143
+ // is a cosmetic bug, whereas failing the whole delete over one would leave the caller unsure
144
+ // whether the media is gone.
145
+ dropReferences(appContext, uriString, assetId, coverUri)
146
+
147
+ val report = Report(
148
+ deleted = primaryResult.deleted,
149
+ securelyOverwritten = primaryResult.securelyOverwritten,
150
+ originalBytes = primaryResult.originalBytes,
151
+ overwrittenBytes = primaryResult.overwrittenBytes,
152
+ derivedFilesErased = erased,
153
+ derivedFilesFailed = failed,
154
+ error = primaryResult.error
155
+ )
156
+
157
+ if (report.fullySecure) {
158
+ Log.i(
159
+ TAG,
160
+ "Securely deleted $uriString: ${MediaFileIo.formatMegabytes(report.originalBytes)} " +
161
+ "overwritten with ${MediaFileIo.formatMegabytes(report.overwrittenBytes)} of garbage, " +
162
+ "$erased derived copies erased"
163
+ )
164
+ } else {
165
+ // Deliberately ERROR level, and deliberately explicit about which guarantee was missed. A
166
+ // failed secure-erase that reads like a success in logcat is how this silently degrades to
167
+ // an ordinary delete.
168
+ Log.e(
169
+ TAG,
170
+ "INSECURE DELETION for $uriString โ€” deleted=${report.deleted}, " +
171
+ "overwritten=${report.securelyOverwritten}, derivedFailures=$failed, " +
172
+ "error=${report.error ?: "none"}"
173
+ )
174
+ }
175
+
176
+ report
177
+ }
178
+
179
+ /** Outcome of the primary file/URI, before derived copies are considered. */
180
+ private data class PrimaryResult(
181
+ val deleted: Boolean,
182
+ val securelyOverwritten: Boolean,
183
+ val originalBytes: Long,
184
+ val overwrittenBytes: Long,
185
+ val error: String? = null
186
+ )
187
+
188
+ /**
189
+ * Erase whatever [uriString] actually points at.
190
+ *
191
+ * A path-backed URI takes the [File] route โ€” overwrite in place, rename, unlink. A
192
+ * `content://` URI cannot be renamed or unlinked as a path, so its bytes are overwritten
193
+ * through a read/write descriptor obtained from the provider and the row is then deleted
194
+ * through the resolver. Providers may refuse `"rw"` (many are read-only); that refusal is
195
+ * reported as a failed overwrite rather than hidden, because the row delete that follows would
196
+ * otherwise look like a secure erase while leaving the file's blocks intact.
197
+ */
198
+ private fun erasePrimary(context: Context, uriString: String, primary: File?): PrimaryResult {
199
+ if (primary != null) {
200
+ val result = SecureFileEraser.erase(primary)
201
+ return PrimaryResult(
202
+ deleted = !primary.exists(),
203
+ securelyOverwritten = result.securelyOverwritten,
204
+ originalBytes = result.originalBytes,
205
+ overwrittenBytes = result.overwrittenBytes,
206
+ error = result.error
207
+ )
208
+ }
209
+
210
+ val uri = runCatching { Uri.parse(uriString) }.getOrNull()
211
+ ?: return PrimaryResult(false, false, 0L, 0L, "Unparseable uri: $uriString")
212
+
213
+ if (uri.scheme != "content") {
214
+ // http/https or anything else with no local bytes: nothing to overwrite and nothing to
215
+ // delete. Reported as not-deleted so the caller does not claim a wipe it never performed.
216
+ return PrimaryResult(false, false, 0L, 0L, "No local file behind $uriString")
217
+ }
218
+
219
+ val resolver = context.contentResolver
220
+ var overwrite: SecureFileEraser.Result? = null
221
+ val openError = runCatching {
222
+ resolver.openFileDescriptor(uri, "rw")?.use { pfd ->
223
+ overwrite = SecureFileEraser.overwrite(pfd)
224
+ } ?: throw IllegalStateException("Provider returned no descriptor")
225
+ }.exceptionOrNull()
226
+
227
+ if (openError != null) {
228
+ Log.e(TAG, "Could not open $uriString read/write for secure overwrite", openError)
229
+ }
230
+
231
+ val rowsDeleted = runCatching { resolver.delete(uri, null, null) }.getOrDefault(0)
232
+ val result = overwrite
233
+
234
+ return PrimaryResult(
235
+ deleted = rowsDeleted > 0,
236
+ securelyOverwritten = result?.securelyOverwritten == true,
237
+ originalBytes = result?.originalBytes ?: 0L,
238
+ overwrittenBytes = result?.overwrittenBytes ?: 0L,
239
+ error = when {
240
+ result?.error != null -> result.error
241
+ openError != null -> "Provider refused read/write access: ${openError.message}"
242
+ rowsDeleted <= 0 -> "Provider deleted no rows for $uriString"
243
+ else -> null
244
+ }
245
+ )
246
+ }
247
+
248
+ /**
249
+ * The on-disk file behind [uriString], or null when there is not one we can address as a path.
250
+ *
251
+ * `file://` and bare paths resolve directly. A `content://` URI from this app's own FileProvider
252
+ * is mapped back to its path through [providerRoot], which mirrors `file_paths.xml` exactly โ€”
253
+ * that turns a provider-backed delete into the far stronger path-based erase (overwrite, rename,
254
+ * unlink) instead of depending on the provider honouring an `"rw"` open.
255
+ *
256
+ * The mapping is a lookup, deliberately NOT a search of candidate directories: this path feeds
257
+ * an irreversible overwrite, and a URI resolved to the wrong directory would destroy an
258
+ * unrelated file that happened to share a name. A foreign provider's URI stays null and is
259
+ * handled through the descriptor route, since only that provider knows how its authority maps
260
+ * to storage.
261
+ */
262
+ private fun resolveFile(context: Context, uriString: String): File? {
263
+ if (uriString.isBlank()) return null
264
+
265
+ val uri = runCatching { Uri.parse(uriString) }.getOrNull() ?: return null
266
+ val scheme = uri.scheme
267
+
268
+ if (scheme == null || scheme == "file") {
269
+ val path = if (scheme == null) uriString else uri.path
270
+ return path?.takeIf { it.isNotBlank() }?.let { File(it) }?.takeIf { it.isFile }
271
+ }
272
+
273
+ if (scheme != "content") return null
274
+ // Only OUR authority may be mapped back to a path.
275
+ if (uri.authority != "${context.packageName}.camera.fileprovider") return null
276
+
277
+ val segments = uri.pathSegments
278
+ if (segments.size < 2) return null
279
+
280
+ // FileProvider URIs are /<declared-name>/<path/under/that/root>.
281
+ val rootDir = providerRoot(context, segments[0]) ?: return null
282
+ val relative = segments.drop(1).joinToString("/")
283
+ val candidate = File(rootDir, relative)
284
+
285
+ // Confine the result to the declared root: a URI carrying `..` segments must not be able to
286
+ // steer an overwrite outside the directory its provider entry exposes.
287
+ val rootPath = runCatching { rootDir.canonicalPath }.getOrNull() ?: return null
288
+ val candidatePath = runCatching { candidate.canonicalPath }.getOrNull() ?: return null
289
+ if (candidatePath != rootPath && !candidatePath.startsWith("$rootPath${File.separator}")) {
290
+ Log.w(TAG, "Refusing to resolve $uriString outside its provider root")
291
+ return null
292
+ }
293
+
294
+ return candidate.takeIf { it.isFile }
295
+ }
296
+
297
+ /**
298
+ * Directory behind one `file_paths.xml` entry name, or null for a name this app never declared.
299
+ *
300
+ * Kept as a `when` over the declared names rather than a map so it stays obviously
301
+ * side-by-side-comparable with the XML โ€” if an entry is added there and not here, a provider URI
302
+ * for it resolves to null and falls back to the descriptor route rather than resolving wrongly.
303
+ */
304
+ private fun providerRoot(context: Context, name: String): File? {
305
+ val externalFiles = context.getExternalFilesDir(null)
306
+ return when (name) {
307
+ "camera_media_cache" -> File(context.cacheDir, "camera_media_cache")
308
+ "edited_images" -> File(context.cacheDir, "edited")
309
+ "compressed_videos" -> File(context.cacheDir, "compressed")
310
+ "exported_media" -> File(context.cacheDir, "exported")
311
+ "shared_media" -> File(context.cacheDir, "shared")
312
+ "documents" -> externalFiles
313
+ "media_documents" -> externalFiles?.let { File(it, "media_documents") }
314
+ "photos" -> externalFiles?.let { File(it, "media_documents/photos") }
315
+ "videos" -> externalFiles?.let { File(it, "media_documents/videos") }
316
+ else -> null
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Every OTHER file on disk that holds this asset's content.
322
+ *
323
+ * Matching is by file name against two keys โ€” the asset id and the primary file's base name โ€”
324
+ * because that is how each producer in this codebase names its output
325
+ * ([CompressionFileNameUtils.createUniqueFileName] prefixes with `<verb>_<assetId>`, and
326
+ * [MediaCacheManager.generateFileName] names the cache file after the id itself). The primary
327
+ * file is excluded so it is never erased twice, and directory entries are skipped.
328
+ *
329
+ * A blank/short id is ignored rather than matched loosely: a two-character key would match
330
+ * unrelated media and this function's output is fed straight into an irreversible overwrite.
331
+ */
332
+ private fun collectDerivedFiles(
333
+ context: Context,
334
+ primary: File?,
335
+ assetId: String?,
336
+ fileName: String?,
337
+ coverUri: String?,
338
+ extraUris: List<String>
339
+ ): List<File> {
340
+ val keys = mutableSetOf<String>()
341
+ assetId?.trim()?.lowercase(Locale.US)?.takeIf { it.length >= MIN_MATCH_KEY_LENGTH }?.let(keys::add)
342
+ fileName?.let { name ->
343
+ File(name).nameWithoutExtension
344
+ .trim()
345
+ .lowercase(Locale.US)
346
+ .takeIf { it.length >= MIN_MATCH_KEY_LENGTH }
347
+ ?.let(keys::add)
348
+ }
349
+
350
+ val primaryPath = primary?.absolutePath
351
+ val found = LinkedHashMap<String, File>()
352
+
353
+ fun consider(file: File) {
354
+ if (!file.isFile) return
355
+ val path = file.absolutePath
356
+ if (path == primaryPath) return
357
+ if (!found.containsKey(path)) found[path] = file
358
+ }
359
+
360
+ // The chosen cover frame is a real JPEG of a video frame โ€” an image of the deleted media โ€”
361
+ // so it is erased with the same guarantees rather than merely unlinked.
362
+ coverUri?.let { cover -> resolveFile(context, cover)?.let(::consider) }
363
+
364
+ // Copies the caller named explicitly (e.g. the cache file behind a published document).
365
+ for (extra in extraUris) {
366
+ resolveFile(context, extra)?.let(::consider)
367
+ }
368
+
369
+ // The cache manager's own record is authoritative for the cached copy, and survives even when
370
+ // the published file is what the UI handed us.
371
+ assetId?.takeIf { it.isNotBlank() }?.let { id ->
372
+ MediaCacheManager.getInstance(context).getMetadata(id)?.cachePath
373
+ ?.let { File(it) }
374
+ ?.let(::consider)
375
+ }
376
+
377
+ if (keys.isEmpty()) return found.values.toList()
378
+
379
+ val roots = listOfNotNull(context.cacheDir, context.getExternalFilesDir(null), context.filesDir)
380
+ for (root in roots) {
381
+ for (dirName in DERIVED_CACHE_DIRS) {
382
+ val dir = File(root, dirName)
383
+ if (!dir.isDirectory) continue
384
+ // listFiles (one level) rather than a recursive walk: every producer writes variants flat
385
+ // into these directories, and a deep walk over the external files dir would drag in
386
+ // unrelated app data on a device with a large media library.
387
+ dir.listFiles()?.forEach { file ->
388
+ val name = file.name.lowercase(Locale.US)
389
+ if (keys.any { key -> containsDelimitedKey(name, key) }) consider(file)
390
+ }
391
+ }
392
+ }
393
+
394
+ return found.values.toList()
395
+ }
396
+
397
+ /**
398
+ * True when [name] contains [key] as a whole token rather than as an arbitrary substring.
399
+ *
400
+ * Every producer builds its output name as `<verb>_<key>_<suffix>` or `<key>.<ext>`, so a match
401
+ * must begin at the start of the name or immediately after a `_`/`-`. A bare `contains` would
402
+ * let a short-but-legal key like `picture` also match `mypicture.jpg`, and the caller feeds this
403
+ * straight into an irreversible overwrite โ€” a false positive here destroys an unrelated file.
404
+ */
405
+ private fun containsDelimitedKey(name: String, key: String): Boolean {
406
+ var from = 0
407
+ while (true) {
408
+ val at = name.indexOf(key, from)
409
+ if (at < 0) return false
410
+ if (at == 0 || name[at - 1] == '_' || name[at - 1] == '-') return true
411
+ from = at + 1
412
+ }
413
+ }
414
+
415
+ /**
416
+ * Drop every REFERENCE to the deleted media: cached bitmaps, cache metadata, storage metadata,
417
+ * and any MediaStore thumbnail rows.
418
+ *
419
+ * All of it is best-effort and none of it can fail the deletion โ€” the bytes are already gone by
420
+ * the time this runs. What it prevents is the deleted item continuing to appear: a thumbnail
421
+ * served from the LruCache renders a photo whose file no longer exists, and a surviving metadata
422
+ * entry keeps it in the cache-size accounting and the duplicate index forever.
423
+ */
424
+ private suspend fun dropReferences(
425
+ context: Context,
426
+ uriString: String,
427
+ assetId: String?,
428
+ coverUri: String?
429
+ ) {
430
+ // Decoded bitmaps first: this is the only reference that can still be DISPLAYED.
431
+ runCatching { MediaThumbnailView.evictFromSharedCache(uriString) }
432
+ .onFailure { Log.w(TAG, "Failed to evict thumbnail for $uriString", it) }
433
+ coverUri?.let { cover ->
434
+ runCatching { MediaThumbnailView.evictFromSharedCache(cover) }
435
+ .onFailure { Log.w(TAG, "Failed to evict cover thumbnail", it) }
436
+ }
437
+
438
+ assetId?.takeIf { it.isNotBlank() }?.let { id ->
439
+ // deleteCachedMedia also unlinks the cache file, which by now is already erased and gone โ€”
440
+ // it treats a missing file as deleted, so this just clears the metadata entry.
441
+ runCatching { MediaCacheManager.getInstance(context).deleteCachedMedia(id) }
442
+ .onFailure { Log.w(TAG, "Failed to clear cache metadata for $id", it) }
443
+ runCatching { MediaMetadataStore.getInstance(context).removeMedia(id) }
444
+ .onFailure { Log.w(TAG, "Failed to clear storage metadata for $id", it) }
445
+ }
446
+
447
+ // MediaStore keeps its own generated thumbnails for anything that was ever published to the
448
+ // gallery. Deleting the media row does not always take them with it, leaving a recoverable
449
+ // downscaled copy of a "securely deleted" photo in the system's thumbnail store.
450
+ runCatching { deleteMediaStoreThumbnails(context, uriString) }
451
+ .onFailure { Log.w(TAG, "Failed to clear MediaStore thumbnails for $uriString", it) }
452
+ }
453
+
454
+ /**
455
+ * Remove MediaStore's own thumbnail rows for a published file.
456
+ *
457
+ * Only meaningful pre-Android Q: from Q onward thumbnails live in a system-managed store that
458
+ * is keyed to the media row and removed with it, and the legacy `Images.Thumbnails` table is no
459
+ * longer writable by apps.
460
+ */
461
+ private fun deleteMediaStoreThumbnails(context: Context, uriString: String) {
462
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) return
463
+
464
+ val path = resolveFile(context, uriString)?.absolutePath
465
+ ?: Uri.parse(uriString).path
466
+ ?: return
467
+
468
+ val resolver = context.contentResolver
469
+ @Suppress("DEPRECATION")
470
+ val imageId = resolver.query(
471
+ MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
472
+ arrayOf(MediaStore.Images.Media._ID),
473
+ "${MediaStore.Images.Media.DATA} = ?",
474
+ arrayOf(path),
475
+ null
476
+ )?.use { cursor ->
477
+ if (cursor.moveToFirst()) cursor.getLong(0) else null
478
+ } ?: return
479
+
480
+ @Suppress("DEPRECATION")
481
+ resolver.delete(
482
+ MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
483
+ "${MediaStore.Images.Thumbnails.IMAGE_ID} = ?",
484
+ arrayOf(imageId.toString())
485
+ )
486
+ }
487
+
488
+ /**
489
+ * Shortest name fragment accepted as an identity match in [collectDerivedFiles]. Anything
490
+ * shorter risks matching unrelated files, and the match feeds an irreversible overwrite.
491
+ */
492
+ private const val MIN_MATCH_KEY_LENGTH = 6
493
+ }
@@ -0,0 +1,107 @@
1
+ package com.customcamera
2
+
3
+ import android.content.Context
4
+ import android.graphics.Paint
5
+
6
+ /**
7
+ * Categorized sticker collection for the preview editor's sticker picker.
8
+ *
9
+ * A sticker is either:
10
+ * - [Sticker.Res] โ€” a bundled vector drawable (the classic hand-authored set, listed in
11
+ * R.array.editor_stickers so the collection stays resource-extensible), or
12
+ * - [Sticker.Emoji] โ€” an emoji glyph rendered to a high-resolution bitmap at pick time.
13
+ *
14
+ * Emoji-backed stickers are what make the collection complete and scalable: adding a
15
+ * sticker is a one-line data change with zero new binary assets, and nothing is
16
+ * rasterized until the user actually picks it (nothing but text is held in memory while
17
+ * browsing โ€” important on low-end devices).
18
+ *
19
+ * Like [EmojiPickerData], glyphs are filtered once per process through [Paint.hasGlyph]
20
+ * so devices with older emoji fonts never show tofu boxes.
21
+ */
22
+ internal object StickerPickerData {
23
+
24
+ sealed class Sticker {
25
+ data class Res(val resId: Int) : Sticker()
26
+ data class Emoji(val glyph: String) : Sticker()
27
+ }
28
+
29
+ data class Category(val icon: String, val label: String, val stickers: List<Sticker>)
30
+
31
+ private val emojiCategories: List<Pair<Pair<String, String>, List<String>>> = listOf(
32
+ ("๐Ÿ˜‚" to "Faces") to listOf(
33
+ "๐Ÿ˜€", "๐Ÿ˜‚", "๐Ÿคฃ", "๐Ÿฅฐ", "๐Ÿ˜", "๐Ÿ˜Ž", "๐Ÿคฉ", "๐Ÿ˜œ", "๐Ÿคช", "๐Ÿฅณ",
34
+ "๐Ÿ˜‡", "๐Ÿค—", "๐Ÿค”", "๐Ÿ˜", "๐Ÿ˜ด", "๐Ÿฅบ", "๐Ÿ˜ญ", "๐Ÿ˜ฑ", "๐Ÿคฏ", "๐Ÿฅถ",
35
+ "๐Ÿ˜ˆ", "๐Ÿค–", "๐Ÿ’€", "๐Ÿ‘ป", "๐Ÿคก", "๐Ÿ‘ฝ", "๐Ÿ’ฉ", "๐Ÿ˜บ", "๐Ÿ™ˆ", "๐Ÿ™‰", "๐Ÿ™Š"
36
+ ),
37
+ ("โค๏ธ" to "Love") to listOf(
38
+ "โค๏ธ", "๐Ÿงก", "๐Ÿ’›", "๐Ÿ’š", "๐Ÿ’™", "๐Ÿ’œ", "๐Ÿ–ค", "๐Ÿค", "๐Ÿ’•", "๐Ÿ’ž",
39
+ "๐Ÿ’–", "๐Ÿ’˜", "๐Ÿ’", "๐Ÿ’—", "๐Ÿ’“", "๐Ÿ’”", "๐Ÿ’‹", "๐Ÿ˜˜", "๐ŸŒน", "๐Ÿฅ€",
40
+ "๐Ÿ’", "๐Ÿ’", "๐Ÿ’’", "๐Ÿ•Š๏ธ"
41
+ ),
42
+ ("๐ŸŽ‰" to "Celebration") to listOf(
43
+ "๐ŸŽ‰", "๐ŸŽŠ", "๐ŸŽˆ", "๐ŸŽ", "๐ŸŽ€", "๐ŸŽ‚", "๐Ÿง", "๐Ÿฐ", "๐Ÿฅ‚", "๐Ÿพ",
44
+ "๐Ÿ†", "๐Ÿฅ‡", "๐Ÿฅˆ", "๐Ÿฅ‰", "๐Ÿ…", "โœจ", "๐ŸŽ†", "๐ŸŽ‡", "๐ŸŽ„", "๐ŸŽƒ",
45
+ "๐ŸŽ…", "๐Ÿช„", "๐ŸŽฏ", "๐ŸŽฎ", "๐ŸŽฒ", "๐ŸŽธ", "๐ŸŽค", "๐ŸŽง"
46
+ ),
47
+ ("๐Ÿผ" to "Animals") to listOf(
48
+ "๐Ÿถ", "๐Ÿฑ", "๐Ÿผ", "๐Ÿจ", "๐Ÿฆ", "๐Ÿฏ", "๐ŸฆŠ", "๐Ÿฐ", "๐Ÿธ", "๐Ÿต",
49
+ "๐Ÿฆ„", "๐Ÿท", "๐Ÿฎ", "๐Ÿ”", "๐Ÿง", "๐Ÿฆ‰", "๐Ÿฆ‹", "๐Ÿข", "๐Ÿ™", "๐Ÿฆˆ",
50
+ "๐Ÿฆ–", "๐Ÿณ", "๐Ÿ", "๐Ÿž", "๐Ÿฆœ", "๐Ÿฆฉ"
51
+ ),
52
+ ("๐Ÿ•" to "Food") to listOf(
53
+ "๐Ÿ•", "๐Ÿ”", "๐ŸŸ", "๐ŸŒญ", "๐Ÿฟ", "๐ŸŒฎ", "๐Ÿฉ", "๐Ÿช", "๐Ÿฆ", "๐Ÿซ",
54
+ "๐Ÿญ", "๐Ÿ“", "๐Ÿ‰", "๐Ÿ’", "๐Ÿ‘", "๐Ÿฅ‘", "๐Ÿ", "๐Ÿฅ•", "๐Ÿœ", "๐Ÿฃ",
55
+ "โ˜•", "๐Ÿงƒ", "๐Ÿฅค", "๐Ÿบ"
56
+ ),
57
+ ("๐ŸŒˆ" to "Vibes") to listOf(
58
+ "๐ŸŒˆ", "โญ", "๐ŸŒŸ", "๐ŸŒ™", "โ˜€๏ธ", "โšก", "๐Ÿ”ฅ", "โ„๏ธ", "๐ŸŒŠ", "๐ŸŒธ",
59
+ "๐ŸŒบ", "๐ŸŒป", "๐Ÿ€", "๐ŸŒต", "๐Ÿ", "๐Ÿ’Ž", "๐Ÿ’ฏ", "โœ…", "โŒ", "๐Ÿ’ค",
60
+ "๐Ÿ’ฅ", "๐Ÿ’ซ", "๐Ÿš€", "๐Ÿ›ธ", "โšฝ", "๐Ÿ€", "๐Ÿ‘‘", "๐Ÿฆธ"
61
+ )
62
+ )
63
+
64
+ @Volatile
65
+ private var cache: List<Category>? = null
66
+
67
+ fun invalidateCache() {
68
+ cache = null
69
+ }
70
+
71
+ /**
72
+ * The full categorized collection: the classic drawable stickers (resolved from
73
+ * R.array.editor_stickers) followed by the emoji-backed categories, filtered to
74
+ * glyphs this device's emoji font can render. Computed once per process.
75
+ */
76
+ fun categories(context: Context): List<Category> {
77
+ cache?.let { return it }
78
+ synchronized(this) {
79
+ cache?.let { return it }
80
+
81
+ val result = mutableListOf<Category>()
82
+
83
+ val names = context.resources.getStringArray(R.array.editor_stickers)
84
+ val classic = names.mapNotNull { name ->
85
+ val id = context.resources.getIdentifier(name, "drawable", context.packageName)
86
+ if (id != 0) Sticker.Res(id) else null
87
+ }
88
+ if (classic.isNotEmpty()) {
89
+ result.add(Category(icon = "โญ", label = Localization.getString("categoryClassic", "Classic"), stickers = classic))
90
+ }
91
+
92
+ val paint = Paint()
93
+ for ((header, glyphs) in emojiCategories) {
94
+ val (icon, defaultLabel) = header
95
+ val labelKey = "category" + defaultLabel.replace(" ", "")
96
+ val label = Localization.getString(labelKey, defaultLabel)
97
+ val supported = glyphs.filter { paint.hasGlyph(it) }
98
+ if (supported.isNotEmpty()) {
99
+ result.add(Category(icon, label, supported.map { Sticker.Emoji(it) }))
100
+ }
101
+ }
102
+
103
+ cache = result
104
+ return result
105
+ }
106
+ }
107
+ }