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,504 @@
1
+ package com.customcamera
2
+
3
+ import android.content.ContentValues
4
+ import android.content.Context
5
+ import android.util.Log
6
+ import java.io.File
7
+
8
+ /**
9
+ * One persisted compression: the on-disk output path plus every fact the request asks to record
10
+ * about the source and the encode. No video bytes live here — the file stays on disk under
11
+ * [CompressionCacheStore.outputDir]; this is only its index row.
12
+ *
13
+ * Identity is [sourceFingerprint] (a streaming content hash of the ORIGINAL clip, never its size)
14
+ * paired with [settingsSignature] (every knob that changes the encode), so the same content
15
+ * compressed with the same settings — even a brand-new asset with identical bytes after an app
16
+ * restart — resolves to the same row.
17
+ */
18
+ data class CompressionCacheEntry(
19
+ val sourceFingerprint: String,
20
+ val settingsSignature: String,
21
+ val sourceSize: Long,
22
+ val sourceDurationMs: Long?,
23
+ val sourceWidth: Int?,
24
+ val sourceHeight: Int?,
25
+ val sourceFrameRate: Int?,
26
+ val sourceCodec: String?,
27
+ val compressionPercent: Int,
28
+ val profileName: String,
29
+ val bitrateScale: Float?,
30
+ val maxEncodedEdge: Int?,
31
+ val preferHevc: Boolean?,
32
+ val outputPath: String,
33
+ val outputSize: Long?,
34
+ val outputCodec: String?,
35
+ val createdAt: Long
36
+ )
37
+
38
+ /**
39
+ * The persistent compression index, abstracted from its storage so the composition rules can be
40
+ * unit-tested against an in-memory fake ([InMemoryCompressionCacheIndex]) without the SQLCipher
41
+ * native libraries, while production uses [SqlCipherCompressionCacheIndex].
42
+ *
43
+ * A lookup only ever returns an entry whose output file still exists on disk; a row whose file has
44
+ * gone (eviction, external cleanup) is dropped and reported as a miss, so a hit always means a
45
+ * reusable file is really there.
46
+ */
47
+ interface CompressionCacheIndex {
48
+ /** The entry for this content+settings whose file still exists, or null (evicting a dead row). */
49
+ fun lookup(sourceFingerprint: String, settingsSignature: String): CompressionCacheEntry?
50
+
51
+ /** Insert or replace the row for [entry]'s (fingerprint, settings) key. */
52
+ fun put(entry: CompressionCacheEntry)
53
+
54
+ /** Drop every row (and delete every output file) built from [sourceFingerprint]. */
55
+ fun deleteByFingerprint(sourceFingerprint: String)
56
+
57
+ /** Drop the row pointing at [outputPath] (and delete that file). */
58
+ fun deleteByOutputPath(outputPath: String)
59
+
60
+ /** Drop rows whose output file no longer exists. */
61
+ fun pruneMissingFiles()
62
+
63
+ /** Age-prune (older than [maxAgeMs]) then size-prune to [maxTotalBytes], deleting evicted files. */
64
+ fun enforceLimits(maxTotalBytes: Long, maxAgeMs: Long)
65
+ }
66
+
67
+ /** The pure, storage-independent key composition. Tested directly. */
68
+ object CompressionCacheKeys {
69
+ /**
70
+ * Bumped whenever [VideoRotationTranscoder.compress] changes what it PRODUCES for the same inputs,
71
+ * so outputs written by an older encoder are never served after an upgrade. Folded into the
72
+ * signature below, it makes every prior row a miss without touching its other components. Bumped
73
+ * to 2 for the "best quality per size" encode (full resolution kept, high-quality profile + VBR,
74
+ * raised quality floor) — files cached by the old downscaling encoder must not be reused. Bumped to
75
+ * 3 for the bits-per-pixel QUALITY FLOOR ([VideoRotationTranscoder.qualityFloorBitRate]): the heavy
76
+ * levels now encode at a higher rate than they used to, so a version-2 row for the same level holds
77
+ * a visibly worse file than the one that would be produced today.
78
+ */
79
+ private const val ENCODER_BEHAVIOR_VERSION = 3
80
+
81
+ /**
82
+ * Everything that changes the encode, folded into one string: the encoder-behaviour version, the
83
+ * level, the profile, and the three plan knobs that alter the OUTPUT ([bitrateScale],
84
+ * [maxEncodedEdge], [preferHevc]). A different device/profile produces a different signature, so
85
+ * an incompatible output is never reused. Deliberately excludes threading hints (priority/operating
86
+ * rate) — they change speed, not the resulting file.
87
+ */
88
+ fun settingsSignature(
89
+ percent: Int,
90
+ profileName: String,
91
+ bitrateScale: Float,
92
+ maxEncodedEdge: Int,
93
+ preferHevc: Boolean
94
+ ): String =
95
+ "enc=$ENCODER_BEHAVIOR_VERSION|p=$percent|profile=$profileName|bs=$bitrateScale|edge=$maxEncodedEdge|hevc=$preferHevc"
96
+ }
97
+
98
+ /**
99
+ * A storage-independent [CompressionCacheIndex] backed by a plain map. It applies EXACTLY the same
100
+ * lookup/eviction rules as the SQLCipher implementation, so a unit test can lock those rules down
101
+ * without the native SQLCipher libraries (which a bare JVM cannot load). File existence is checked
102
+ * with real [File.exists] calls, so a test drives "file went missing" by deleting a temp file.
103
+ */
104
+ class InMemoryCompressionCacheIndex : CompressionCacheIndex {
105
+ private val rows = LinkedHashMap<String, CompressionCacheEntry>()
106
+
107
+ private fun keyOf(fingerprint: String, signature: String) = "$fingerprint@$signature"
108
+
109
+ override fun lookup(sourceFingerprint: String, settingsSignature: String): CompressionCacheEntry? {
110
+ val key = keyOf(sourceFingerprint, settingsSignature)
111
+ val entry = rows[key] ?: return null
112
+ if (!File(entry.outputPath).exists()) {
113
+ rows.remove(key)
114
+ return null
115
+ }
116
+ return entry
117
+ }
118
+
119
+ override fun put(entry: CompressionCacheEntry) {
120
+ rows[keyOf(entry.sourceFingerprint, entry.settingsSignature)] = entry
121
+ }
122
+
123
+ override fun deleteByFingerprint(sourceFingerprint: String) {
124
+ val gone = rows.values.filter { it.sourceFingerprint == sourceFingerprint }
125
+ gone.forEach { deleteFile(it.outputPath); rows.remove(keyOf(it.sourceFingerprint, it.settingsSignature)) }
126
+ }
127
+
128
+ override fun deleteByOutputPath(outputPath: String) {
129
+ val gone = rows.values.filter { it.outputPath == outputPath }
130
+ gone.forEach { deleteFile(it.outputPath); rows.remove(keyOf(it.sourceFingerprint, it.settingsSignature)) }
131
+ }
132
+
133
+ override fun pruneMissingFiles() {
134
+ rows.values.filter { !File(it.outputPath).exists() }
135
+ .forEach { rows.remove(keyOf(it.sourceFingerprint, it.settingsSignature)) }
136
+ }
137
+
138
+ override fun enforceLimits(maxTotalBytes: Long, maxAgeMs: Long) {
139
+ val now = System.currentTimeMillis()
140
+ // Age prune first…
141
+ rows.values.filter { now - it.createdAt > maxAgeMs }
142
+ .forEach { deleteFile(it.outputPath); rows.remove(keyOf(it.sourceFingerprint, it.settingsSignature)) }
143
+ // …then size prune, oldest first, until the total output footprint is under the cap.
144
+ var total = rows.values.sumOf { it.outputSize ?: 0L }
145
+ if (total <= maxTotalBytes) return
146
+ val oldestFirst = rows.values.sortedBy { it.createdAt }
147
+ for (entry in oldestFirst) {
148
+ if (total <= maxTotalBytes) break
149
+ deleteFile(entry.outputPath)
150
+ rows.remove(keyOf(entry.sourceFingerprint, entry.settingsSignature))
151
+ total -= entry.outputSize ?: 0L
152
+ }
153
+ }
154
+
155
+ private fun deleteFile(path: String) {
156
+ runCatching { File(path).takeIf { it.exists() }?.delete() }
157
+ }
158
+ }
159
+
160
+ /**
161
+ * The production [CompressionCacheIndex]: a SQLCipher-encrypted `video_compression_cache` table in
162
+ * its own `compression_cache.db`, opened with the Keystore-sealed passphrase. Every method is
163
+ * wrapped so a storage error degrades to "cache miss / no-op" rather than failing a compression —
164
+ * the cache is an optimisation, never a dependency.
165
+ *
166
+ * The output files themselves live on disk (no BLOBs); this only stores their paths + metadata.
167
+ */
168
+ class SqlCipherCompressionCacheIndex(
169
+ context: Context,
170
+ passphrase: String
171
+ ) : CompressionCacheIndex {
172
+
173
+ private val helper = OpenHelper(context.applicationContext)
174
+ private val db: net.sqlcipher.database.SQLiteDatabase = helper.getWritableDatabase(passphrase)
175
+
176
+ override fun lookup(sourceFingerprint: String, settingsSignature: String): CompressionCacheEntry? {
177
+ return runCatching {
178
+ synchronized(db) {
179
+ db.query(
180
+ TABLE, null,
181
+ "$COL_FINGERPRINT = ? AND $COL_SIGNATURE = ?",
182
+ arrayOf(sourceFingerprint, settingsSignature),
183
+ null, null, null
184
+ ).use { cursor ->
185
+ if (!cursor.moveToFirst()) return@runCatching null
186
+ val entry = readEntry(cursor)
187
+ if (!File(entry.outputPath).exists()) {
188
+ db.delete(
189
+ TABLE, "$COL_FINGERPRINT = ? AND $COL_SIGNATURE = ?",
190
+ arrayOf(sourceFingerprint, settingsSignature)
191
+ )
192
+ return@runCatching null
193
+ }
194
+ entry
195
+ }
196
+ }
197
+ }.getOrElse {
198
+ Log.w(TAG, "Compression cache lookup failed", it)
199
+ null
200
+ }
201
+ }
202
+
203
+ override fun put(entry: CompressionCacheEntry) {
204
+ runCatching {
205
+ val values = ContentValues().apply {
206
+ put(COL_FINGERPRINT, entry.sourceFingerprint)
207
+ put(COL_SIGNATURE, entry.settingsSignature)
208
+ put(COL_SOURCE_SIZE, entry.sourceSize)
209
+ putOrNull(COL_SOURCE_DURATION, entry.sourceDurationMs)
210
+ putOrNull(COL_SOURCE_WIDTH, entry.sourceWidth)
211
+ putOrNull(COL_SOURCE_HEIGHT, entry.sourceHeight)
212
+ putOrNull(COL_SOURCE_FRAME_RATE, entry.sourceFrameRate)
213
+ put(COL_SOURCE_CODEC, entry.sourceCodec)
214
+ put(COL_PERCENT, entry.compressionPercent)
215
+ put(COL_PROFILE, entry.profileName)
216
+ putOrNull(COL_BITRATE_SCALE, entry.bitrateScale)
217
+ putOrNull(COL_MAX_EDGE, entry.maxEncodedEdge)
218
+ putOrNull(COL_PREFER_HEVC, entry.preferHevc?.let { if (it) 1 else 0 })
219
+ put(COL_OUTPUT_PATH, entry.outputPath)
220
+ putOrNull(COL_OUTPUT_SIZE, entry.outputSize)
221
+ put(COL_OUTPUT_CODEC, entry.outputCodec)
222
+ put(COL_CREATED_AT, entry.createdAt)
223
+ }
224
+ synchronized(db) {
225
+ db.insertWithOnConflict(
226
+ TABLE, null, values, net.sqlcipher.database.SQLiteDatabase.CONFLICT_REPLACE
227
+ )
228
+ }
229
+ }.onFailure { Log.w(TAG, "Compression cache put failed", it) }
230
+ }
231
+
232
+ override fun deleteByFingerprint(sourceFingerprint: String) {
233
+ runCatching {
234
+ synchronized(db) {
235
+ collectPaths("$COL_FINGERPRINT = ?", arrayOf(sourceFingerprint)).forEach(::deleteFile)
236
+ db.delete(TABLE, "$COL_FINGERPRINT = ?", arrayOf(sourceFingerprint))
237
+ }
238
+ }.onFailure { Log.w(TAG, "Compression cache deleteByFingerprint failed", it) }
239
+ }
240
+
241
+ override fun deleteByOutputPath(outputPath: String) {
242
+ runCatching {
243
+ synchronized(db) {
244
+ deleteFile(outputPath)
245
+ db.delete(TABLE, "$COL_OUTPUT_PATH = ?", arrayOf(outputPath))
246
+ }
247
+ }.onFailure { Log.w(TAG, "Compression cache deleteByOutputPath failed", it) }
248
+ }
249
+
250
+ override fun pruneMissingFiles() {
251
+ runCatching {
252
+ synchronized(db) {
253
+ collectPaths(null, null).filter { !File(it).exists() }.forEach { path ->
254
+ db.delete(TABLE, "$COL_OUTPUT_PATH = ?", arrayOf(path))
255
+ }
256
+ }
257
+ }.onFailure { Log.w(TAG, "Compression cache pruneMissingFiles failed", it) }
258
+ }
259
+
260
+ override fun enforceLimits(maxTotalBytes: Long, maxAgeMs: Long) {
261
+ runCatching {
262
+ synchronized(db) {
263
+ // Age prune: delete files then rows older than the cutoff.
264
+ val cutoff = System.currentTimeMillis() - maxAgeMs
265
+ collectPaths("$COL_CREATED_AT < ?", arrayOf(cutoff.toString())).forEach(::deleteFile)
266
+ db.delete(TABLE, "$COL_CREATED_AT < ?", arrayOf(cutoff.toString()))
267
+
268
+ // Size prune: walk oldest-first, deleting until the total output footprint fits the cap.
269
+ var total = db.rawQuery("SELECT COALESCE(SUM($COL_OUTPUT_SIZE), 0) FROM $TABLE", null)
270
+ .use { if (it.moveToFirst()) it.getLong(0) else 0L }
271
+ if (total <= maxTotalBytes) return@synchronized
272
+ db.query(
273
+ TABLE, arrayOf(COL_OUTPUT_PATH, COL_OUTPUT_SIZE),
274
+ null, null, null, null, "$COL_CREATED_AT ASC"
275
+ ).use { cursor ->
276
+ while (cursor.moveToNext() && total > maxTotalBytes) {
277
+ val path = cursor.getString(0)
278
+ val size = if (cursor.isNull(1)) 0L else cursor.getLong(1)
279
+ deleteFile(path)
280
+ db.delete(TABLE, "$COL_OUTPUT_PATH = ?", arrayOf(path))
281
+ total -= size
282
+ }
283
+ }
284
+ }
285
+ }.onFailure { Log.w(TAG, "Compression cache enforceLimits failed", it) }
286
+ }
287
+
288
+ /** Close the underlying database (used when the whole cache is being cleared). */
289
+ fun close() {
290
+ runCatching { synchronized(db) { db.close() } }
291
+ }
292
+
293
+ private fun collectPaths(where: String?, args: Array<String>?): List<String> {
294
+ val paths = ArrayList<String>()
295
+ db.query(TABLE, arrayOf(COL_OUTPUT_PATH), where, args, null, null, null).use { cursor ->
296
+ while (cursor.moveToNext()) paths.add(cursor.getString(0))
297
+ }
298
+ return paths
299
+ }
300
+
301
+ private fun deleteFile(path: String) {
302
+ runCatching { File(path).takeIf { it.exists() }?.delete() }
303
+ }
304
+
305
+ private fun readEntry(c: android.database.Cursor): CompressionCacheEntry = CompressionCacheEntry(
306
+ sourceFingerprint = c.getString(c.getColumnIndexOrThrow(COL_FINGERPRINT)),
307
+ settingsSignature = c.getString(c.getColumnIndexOrThrow(COL_SIGNATURE)),
308
+ sourceSize = c.getLong(c.getColumnIndexOrThrow(COL_SOURCE_SIZE)),
309
+ sourceDurationMs = c.longOrNull(COL_SOURCE_DURATION),
310
+ sourceWidth = c.intOrNull(COL_SOURCE_WIDTH),
311
+ sourceHeight = c.intOrNull(COL_SOURCE_HEIGHT),
312
+ sourceFrameRate = c.intOrNull(COL_SOURCE_FRAME_RATE),
313
+ sourceCodec = c.stringOrNull(COL_SOURCE_CODEC),
314
+ compressionPercent = c.getInt(c.getColumnIndexOrThrow(COL_PERCENT)),
315
+ profileName = c.getString(c.getColumnIndexOrThrow(COL_PROFILE)),
316
+ bitrateScale = c.floatOrNull(COL_BITRATE_SCALE),
317
+ maxEncodedEdge = c.intOrNull(COL_MAX_EDGE),
318
+ preferHevc = c.intOrNull(COL_PREFER_HEVC)?.let { it != 0 },
319
+ outputPath = c.getString(c.getColumnIndexOrThrow(COL_OUTPUT_PATH)),
320
+ outputSize = c.longOrNull(COL_OUTPUT_SIZE),
321
+ outputCodec = c.stringOrNull(COL_OUTPUT_CODEC),
322
+ createdAt = c.getLong(c.getColumnIndexOrThrow(COL_CREATED_AT))
323
+ )
324
+
325
+ private class OpenHelper(context: Context) : net.sqlcipher.database.SQLiteOpenHelper(
326
+ context, DB_NAME, null, DB_VERSION
327
+ ) {
328
+ override fun onCreate(db: net.sqlcipher.database.SQLiteDatabase) {
329
+ db.execSQL(CREATE_TABLE)
330
+ db.execSQL(CREATE_INDEX_CREATED_AT)
331
+ db.execSQL(CREATE_INDEX_FINGERPRINT)
332
+ }
333
+
334
+ override fun onUpgrade(db: net.sqlcipher.database.SQLiteDatabase, oldVersion: Int, newVersion: Int) {
335
+ // The table is a pure cache — nothing here is authoritative — so a schema bump can safely
336
+ // rebuild it. The output files it points at are cleaned up by the disk-side prune.
337
+ db.execSQL("DROP TABLE IF EXISTS $TABLE")
338
+ onCreate(db)
339
+ }
340
+ }
341
+
342
+ companion object {
343
+ private const val DB_NAME = "compression_cache.db"
344
+ private const val DB_VERSION = 1
345
+ private const val TABLE = "video_compression_cache"
346
+
347
+ private const val COL_FINGERPRINT = "source_fingerprint"
348
+ private const val COL_SIGNATURE = "settings_signature"
349
+ private const val COL_SOURCE_SIZE = "source_size"
350
+ private const val COL_SOURCE_DURATION = "source_duration_ms"
351
+ private const val COL_SOURCE_WIDTH = "source_width"
352
+ private const val COL_SOURCE_HEIGHT = "source_height"
353
+ private const val COL_SOURCE_FRAME_RATE = "source_frame_rate"
354
+ private const val COL_SOURCE_CODEC = "source_codec"
355
+ private const val COL_PERCENT = "compression_percent"
356
+ private const val COL_PROFILE = "profile_name"
357
+ private const val COL_BITRATE_SCALE = "bitrate_scale"
358
+ private const val COL_MAX_EDGE = "max_encoded_edge"
359
+ private const val COL_PREFER_HEVC = "prefer_hevc"
360
+ private const val COL_OUTPUT_PATH = "output_path"
361
+ private const val COL_OUTPUT_SIZE = "output_size"
362
+ private const val COL_OUTPUT_CODEC = "output_codec"
363
+ private const val COL_CREATED_AT = "created_at"
364
+
365
+ private val CREATE_TABLE = """
366
+ CREATE TABLE IF NOT EXISTS $TABLE (
367
+ $COL_FINGERPRINT TEXT NOT NULL,
368
+ $COL_SIGNATURE TEXT NOT NULL,
369
+ $COL_SOURCE_SIZE INTEGER NOT NULL,
370
+ $COL_SOURCE_DURATION INTEGER,
371
+ $COL_SOURCE_WIDTH INTEGER,
372
+ $COL_SOURCE_HEIGHT INTEGER,
373
+ $COL_SOURCE_FRAME_RATE INTEGER,
374
+ $COL_SOURCE_CODEC TEXT,
375
+ $COL_PERCENT INTEGER NOT NULL,
376
+ $COL_PROFILE TEXT NOT NULL,
377
+ $COL_BITRATE_SCALE REAL,
378
+ $COL_MAX_EDGE INTEGER,
379
+ $COL_PREFER_HEVC INTEGER,
380
+ $COL_OUTPUT_PATH TEXT NOT NULL,
381
+ $COL_OUTPUT_SIZE INTEGER,
382
+ $COL_OUTPUT_CODEC TEXT,
383
+ $COL_CREATED_AT INTEGER NOT NULL,
384
+ PRIMARY KEY ($COL_FINGERPRINT, $COL_SIGNATURE)
385
+ )
386
+ """.trimIndent()
387
+
388
+ private val CREATE_INDEX_CREATED_AT =
389
+ "CREATE INDEX IF NOT EXISTS idx_${TABLE}_created_at ON $TABLE ($COL_CREATED_AT)"
390
+ private val CREATE_INDEX_FINGERPRINT =
391
+ "CREATE INDEX IF NOT EXISTS idx_${TABLE}_fingerprint ON $TABLE ($COL_FINGERPRINT)"
392
+
393
+ private const val TAG = "CompressionCacheStore"
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Process-wide entry point to the persistent compression cache. Opens the encrypted database lazily
399
+ * on first use (loading the SQLCipher native libs and the Keystore passphrase), runs an
400
+ * opportunistic prune, and thereafter serves lookups/puts. Every call degrades to a no-op / miss if
401
+ * the database could not be opened, so a device without the native lib simply never caches — it
402
+ * never crashes.
403
+ */
404
+ object CompressionCacheStore {
405
+ private const val TAG = "CompressionCacheStore"
406
+ /** Mirrors [CacheCleanupManager]: 500 MB of compressed outputs kept, 7-day age cap. */
407
+ private const val MAX_TOTAL_BYTES = 500L * 1024L * 1024L
408
+ private const val MAX_AGE_MS = 7L * 24L * 60L * 60L * 1000L
409
+
410
+ private var index: CompressionCacheIndex? = null
411
+ private var attempted = false
412
+
413
+ /** The dedicated dir holding the persistent MASTER outputs. Never wiped by the editor's close(). */
414
+ fun outputDir(context: Context): File =
415
+ File(context.cacheDir, "compression_cache").apply { mkdirs() }
416
+
417
+ /** [CompressionCacheKeys.settingsSignature] — surfaced here so callers need one import. */
418
+ fun settingsSignature(
419
+ percent: Int, profileName: String, bitrateScale: Float, maxEncodedEdge: Int, preferHevc: Boolean
420
+ ): String = CompressionCacheKeys.settingsSignature(percent, profileName, bitrateScale, maxEncodedEdge, preferHevc)
421
+
422
+ @Synchronized
423
+ private fun index(context: Context): CompressionCacheIndex? {
424
+ index?.let { return it }
425
+ // A failed open (missing native lib, bad key) is permanent for this process — don't re-attempt
426
+ // and re-log on every compression.
427
+ if (attempted) return null
428
+ attempted = true
429
+ return try {
430
+ net.sqlcipher.database.SQLiteDatabase.loadLibs(context.applicationContext)
431
+ val key = SecureKeyModule.getOrCreateDatabaseKey(context)
432
+ val impl = SqlCipherCompressionCacheIndex(context.applicationContext, key)
433
+ // Opportunistic maintenance on first open.
434
+ impl.pruneMissingFiles()
435
+ impl.enforceLimits(MAX_TOTAL_BYTES, MAX_AGE_MS)
436
+ index = impl
437
+ impl
438
+ } catch (t: Throwable) {
439
+ Log.e(TAG, "Persistent compression cache disabled: could not open encrypted DB", t)
440
+ null
441
+ }
442
+ }
443
+
444
+ fun lookup(context: Context, sourceFingerprint: String, settingsSignature: String): CompressionCacheEntry? =
445
+ index(context)?.lookup(sourceFingerprint, settingsSignature)
446
+
447
+ fun put(context: Context, entry: CompressionCacheEntry) {
448
+ val idx = index(context) ?: return
449
+ idx.put(entry)
450
+ idx.enforceLimits(MAX_TOTAL_BYTES, MAX_AGE_MS)
451
+ }
452
+
453
+ fun deleteByFingerprint(context: Context, sourceFingerprint: String) {
454
+ index(context)?.deleteByFingerprint(sourceFingerprint)
455
+ }
456
+
457
+ /**
458
+ * Wipe the whole persistent cache — close and delete the database, and empty the output dir.
459
+ * Called from the plugin's cache-clear path so "clear cache" reclaims these files too. The next
460
+ * use re-opens a fresh, empty database.
461
+ */
462
+ @Synchronized
463
+ fun clearAll(context: Context) {
464
+ runCatching { (index as? SqlCipherCompressionCacheIndex)?.close() }
465
+ index = null
466
+ attempted = false
467
+ runCatching {
468
+ val ctx = context.applicationContext
469
+ ctx.deleteDatabase("compression_cache.db")
470
+ outputDir(ctx).listFiles()?.forEach { runCatching { it.delete() } }
471
+ }.onFailure { Log.w(TAG, "Failed to clear persistent compression cache", it) }
472
+ }
473
+ }
474
+
475
+ // ── ContentValues / Cursor nullable helpers ────────────────────────────────────────────────
476
+ // File-private so both the store and any future SQLCipher-backed table can reuse them; kept at
477
+ // top level (rather than as member extensions) so there is no dual-receiver resolution to reason
478
+ // about at the call sites inside ContentValues { } blocks.
479
+
480
+ private fun ContentValues.putOrNull(key: String, value: Long?) {
481
+ if (value == null) putNull(key) else put(key, value)
482
+ }
483
+
484
+ private fun ContentValues.putOrNull(key: String, value: Int?) {
485
+ if (value == null) putNull(key) else put(key, value)
486
+ }
487
+
488
+ private fun ContentValues.putOrNull(key: String, value: Float?) {
489
+ if (value == null) putNull(key) else put(key, value)
490
+ }
491
+
492
+ private fun android.database.Cursor.stringOrNull(name: String): String? =
493
+ getColumnIndex(name).let { if (it < 0 || isNull(it)) null else getString(it) }
494
+
495
+ private fun android.database.Cursor.intOrNull(name: String): Int? =
496
+ getColumnIndex(name).let { if (it < 0 || isNull(it)) null else getInt(it) }
497
+
498
+ private fun android.database.Cursor.longOrNull(name: String): Long? =
499
+ getColumnIndex(name).let { if (it < 0 || isNull(it)) null else getLong(it) }
500
+
501
+ private fun android.database.Cursor.floatOrNull(name: String): Float? =
502
+ getColumnIndex(name).let { if (it < 0 || isNull(it)) null else getFloat(it) }
503
+
504
+
@@ -0,0 +1,7 @@
1
+ package com.customcamera
2
+
3
+ internal object CompressionFileNameUtils {
4
+ fun createUniqueFileName(prefix: String, extension: String): String {
5
+ return "${prefix}_${System.currentTimeMillis()}_${System.nanoTime()}.${extension.trimStart('.')}"
6
+ }
7
+ }