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,215 @@
1
+ package com.customcamera
2
+
3
+ import android.content.Context
4
+ import android.net.Uri
5
+ import java.io.File
6
+ import java.io.InputStream
7
+ import java.util.Locale
8
+
9
+ /**
10
+ * Single source of truth for "is this a GIF, and does it actually animate".
11
+ *
12
+ * The codebase already carries several independent extension/MIME tables (ThumbnailLoader's
13
+ * alpha-capable set, MediaCacheManager.MediaFormat, MediaPublisher.getMimeType,
14
+ * MediaPreviewEditor.mediaExtension). Those each answer a different question and are left
15
+ * alone; this object exists so the GIF-specific decisions — badge, animated playback,
16
+ * flatten-confirmation — all read the same answer instead of adding a fifth drifting list.
17
+ *
18
+ * Detection uses BOTH signals required of it: the declared MIME type first, then the file
19
+ * extension. The precedence mirrors [ThumbnailLoader.mayHaveAlphaChannel] so the two never
20
+ * disagree about the same file.
21
+ */
22
+ object GifSupport {
23
+ private const val MIME_GIF = "image/gif"
24
+ private const val EXTENSION_GIF = "gif"
25
+
26
+ /**
27
+ * How far into the file [isAnimated] is willing to read looking for a second frame. An
28
+ * animated GIF's second Image Descriptor almost always lands within the first frame's
29
+ * compressed data, so a bounded read answers the question without loading a large file into
30
+ * memory on the main path.
31
+ */
32
+ private const val ANIMATION_SCAN_LIMIT_BYTES = 256 * 1024
33
+
34
+ /** `GIF87a` / `GIF89a` — the two legal GIF signatures, as raw header bytes. */
35
+ private val SIGNATURE_87A = "GIF87a".toByteArray(Charsets.US_ASCII)
36
+ private val SIGNATURE_89A = "GIF89a".toByteArray(Charsets.US_ASCII)
37
+
38
+ /**
39
+ * True when [asset] is a GIF, judged by its declared MIME type first and its file name /
40
+ * path extension second.
41
+ *
42
+ * A declared, recognised `image/` type is authoritative — an asset that says it is
43
+ * `image/jpeg` is not reclassified as a GIF just because something in its path ends in
44
+ * `.gif`. Only an absent or non-image MIME type falls through to the extension, which is
45
+ * what rescues assets that arrived from a provider reporting no type at all.
46
+ */
47
+ fun isGif(asset: CameraAsset): Boolean {
48
+ asset.mimeType?.lowercase(Locale.US)?.let { mime ->
49
+ if (mime == MIME_GIF) return true
50
+ if (mime.startsWith("image/")) return false
51
+ }
52
+ val name = asset.fileName ?: asset.cachePath ?: asset.uri.path ?: return false
53
+ return extensionOf(name) == EXTENSION_GIF
54
+ }
55
+
56
+ /**
57
+ * URI-only overload for call sites that hold no [CameraAsset] — resolves the MIME type
58
+ * through the ContentResolver (which reads the provider's own record) before falling back
59
+ * to the path extension.
60
+ */
61
+ fun isGif(context: Context, uri: Uri?): Boolean {
62
+ if (uri == null) return false
63
+ val declared = runCatching { context.contentResolver.getType(uri) }
64
+ .getOrNull()
65
+ ?.lowercase(Locale.US)
66
+ if (declared == MIME_GIF) return true
67
+ if (declared != null && declared.startsWith("image/")) return false
68
+ return extensionOf(uri.path ?: return false) == EXTENSION_GIF
69
+ }
70
+
71
+ /** True when [data] starts with a GIF signature, regardless of what it claims to be. */
72
+ fun hasGifSignature(data: ByteArray): Boolean =
73
+ startsWith(data, SIGNATURE_87A) || startsWith(data, SIGNATURE_89A)
74
+
75
+ /**
76
+ * True when [file] is a GIF holding more than one frame.
77
+ *
78
+ * A single-frame `.gif` is, for every purpose here, an ordinary still image: it can be
79
+ * edited and exported without losing anything, so it must NOT trigger the
80
+ * convert-to-image confirmation. Only a genuinely animated GIF has animation to lose.
81
+ *
82
+ * Failure to parse returns true — the safe direction. Treating an unreadable GIF as
83
+ * animated means the user is asked before it is flattened; the opposite default would
84
+ * destroy animation silently, which is exactly what this is here to prevent.
85
+ */
86
+ fun isAnimated(file: File): Boolean =
87
+ runCatching {
88
+ if (!file.exists() || file.length() < SIGNATURE_87A.size) return false
89
+ file.inputStream().use { readIsAnimated(it) }
90
+ }.getOrDefault(true)
91
+
92
+ /** [isAnimated] for content:// sources, reading through the resolver. */
93
+ fun isAnimated(context: Context, uri: Uri): Boolean =
94
+ runCatching {
95
+ context.contentResolver.openInputStream(uri)?.use { readIsAnimated(it) } ?: true
96
+ }.getOrDefault(true)
97
+
98
+ /**
99
+ * Walks the GIF block structure looking for a SECOND Image Descriptor (0x2C).
100
+ *
101
+ * A GIF is a header + Logical Screen Descriptor + optional Global Color Table, followed by
102
+ * a stream of blocks. Each frame is introduced by an Image Descriptor; extension blocks
103
+ * (0x21) and the trailer (0x3B) sit between them. Counting descriptors is therefore an
104
+ * exact frame count — far more reliable than looking for a Netscape looping extension,
105
+ * which plenty of animated GIFs omit.
106
+ *
107
+ * Bails out (returning false) once [ANIMATION_SCAN_LIMIT_BYTES] is consumed: a file whose
108
+ * first frame alone exceeds that is large enough that walking the rest would cost more than
109
+ * the answer is worth on a scroll or send path.
110
+ */
111
+ private fun readIsAnimated(stream: InputStream): Boolean {
112
+ val header = ByteArray(6)
113
+ if (stream.read(header) != header.size) return false
114
+ if (!hasGifSignature(header)) return false
115
+
116
+ // Logical Screen Descriptor: width(2) height(2) packed(1) bgIndex(1) aspect(1).
117
+ val screen = ByteArray(7)
118
+ if (stream.read(screen) != screen.size) return false
119
+ val packed = screen[4].toInt() and 0xFF
120
+ if (packed and 0x80 != 0) {
121
+ // Global Color Table present: 3 bytes per entry, 2^(N+1) entries.
122
+ val entries = 1 shl ((packed and 0x07) + 1)
123
+ if (!skipFully(stream, entries * 3)) return false
124
+ }
125
+
126
+ var consumed = header.size + screen.size
127
+ var imageDescriptors = 0
128
+ while (consumed < ANIMATION_SCAN_LIMIT_BYTES) {
129
+ val block = stream.read()
130
+ if (block < 0) return false
131
+ consumed++
132
+ when (block) {
133
+ 0x2C -> {
134
+ // Image Descriptor: left(2) top(2) width(2) height(2) packed(1).
135
+ imageDescriptors++
136
+ if (imageDescriptors > 1) return true
137
+ val descriptor = ByteArray(9)
138
+ if (stream.read(descriptor) != descriptor.size) return false
139
+ consumed += descriptor.size
140
+ val localPacked = descriptor[8].toInt() and 0xFF
141
+ if (localPacked and 0x80 != 0) {
142
+ val entries = 1 shl ((localPacked and 0x07) + 1)
143
+ if (!skipFully(stream, entries * 3)) return false
144
+ consumed += entries * 3
145
+ }
146
+ // LZW minimum code size, then the frame's data sub-blocks.
147
+ if (stream.read() < 0) return false
148
+ consumed++
149
+ consumed += skipSubBlocks(stream) ?: return false
150
+ }
151
+ 0x21 -> {
152
+ // Extension block: one label byte, then data sub-blocks.
153
+ if (stream.read() < 0) return false
154
+ consumed++
155
+ consumed += skipSubBlocks(stream) ?: return false
156
+ }
157
+ // Trailer — end of file reached with at most one frame.
158
+ 0x3B -> return false
159
+ // Anything else means the stream is not a GIF we can walk; stop rather than guess.
160
+ else -> return false
161
+ }
162
+ }
163
+ return false
164
+ }
165
+
166
+ /**
167
+ * Skips a chain of GIF data sub-blocks (each a length byte followed by that many bytes,
168
+ * terminated by a zero-length block). Returns the number of bytes consumed, or null if the
169
+ * stream ended mid-chain.
170
+ */
171
+ private fun skipSubBlocks(stream: InputStream): Int? {
172
+ var consumed = 0
173
+ while (true) {
174
+ val size = stream.read()
175
+ if (size < 0) return null
176
+ consumed++
177
+ if (size == 0) return consumed
178
+ if (!skipFully(stream, size)) return null
179
+ consumed += size
180
+ }
181
+ }
182
+
183
+ /**
184
+ * [InputStream.skip] is permitted to skip fewer bytes than asked without being at the end
185
+ * of the stream, so a single call is not enough to keep the block walk aligned.
186
+ */
187
+ private fun skipFully(stream: InputStream, count: Int): Boolean {
188
+ var remaining = count.toLong()
189
+ while (remaining > 0) {
190
+ val skipped = stream.skip(remaining)
191
+ if (skipped <= 0) {
192
+ // skip() made no progress — fall back to a read to distinguish "end of stream" from
193
+ // "this stream just doesn't implement skip".
194
+ if (stream.read() < 0) return false
195
+ remaining--
196
+ } else {
197
+ remaining -= skipped
198
+ }
199
+ }
200
+ return true
201
+ }
202
+
203
+ private fun extensionOf(path: String): String =
204
+ path.substringAfterLast('/', path)
205
+ .substringAfterLast('.', "")
206
+ .lowercase(Locale.US)
207
+
208
+ private fun startsWith(data: ByteArray, signature: ByteArray): Boolean {
209
+ if (data.size < signature.size) return false
210
+ for (i in signature.indices) {
211
+ if (data[i] != signature[i]) return false
212
+ }
213
+ return true
214
+ }
215
+ }
@@ -0,0 +1,320 @@
1
+ package com.customcamera
2
+
3
+ import android.content.Context
4
+ import android.media.AudioAttributes
5
+ import android.media.AudioFocusRequest
6
+ import android.media.AudioManager
7
+ import android.media.MediaPlayer
8
+ import android.net.Uri
9
+ import android.os.Build
10
+ import android.os.Handler
11
+ import android.os.Looper
12
+ import com.facebook.react.bridge.Arguments
13
+ import com.facebook.react.bridge.ReactApplicationContext
14
+ import com.facebook.react.modules.core.DeviceEventManagerModule
15
+
16
+ object GlobalAudioPlayer {
17
+ var mediaPlayer: MediaPlayer? = null
18
+ var currentUri: Uri? = null
19
+ var currentTitle: String = ""
20
+ var prepared = false
21
+ var playbackActive = false
22
+ private val mainHandler = Handler(Looper.getMainLooper())
23
+ var reactContext: ReactApplicationContext? = null
24
+
25
+ val listeners = mutableSetOf<Listener>()
26
+
27
+ // ------------------------------------------------------------------
28
+ // Audio focus — the platform mechanism for call/interruption handling
29
+ // ------------------------------------------------------------------
30
+ // An incoming or outgoing phone call (telephony ring, in-call audio, and VoIP apps
31
+ // alike) takes TRANSIENT audio focus and returns it when the call ends. Participating
32
+ // in audio focus is therefore the root-cause fix for "audio keeps playing during a
33
+ // call": on transient loss we pause and remember the intent to resume; on regain we
34
+ // resume from the exact paused position (MediaPlayer.pause() preserves it). The same
35
+ // listener also handles every other interruption class correctly: permanent loss
36
+ // (another media app starts) pauses without auto-resume, and duck requests
37
+ // (navigation prompts, notifications) just lower the volume.
38
+
39
+ private const val DUCK_VOLUME = 0.2f
40
+
41
+ private var audioManager: AudioManager? = null
42
+ private var focusRequest: AudioFocusRequest? = null // API 26+
43
+ private var hasAudioFocus = false
44
+ // True only when WE paused because of a transient focus loss (call). A user-initiated
45
+ // pause clears it, so pausing manually during a call never auto-resumes afterwards.
46
+ private var resumeOnFocusGain = false
47
+
48
+ private val playbackAttributes: AudioAttributes = AudioAttributes.Builder()
49
+ .setUsage(AudioAttributes.USAGE_MEDIA)
50
+ .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
51
+ .build()
52
+
53
+ private val focusChangeListener = AudioManager.OnAudioFocusChangeListener { change ->
54
+ // Serialize with all other player mutations on the main thread.
55
+ mainHandler.post {
56
+ when (change) {
57
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT -> {
58
+ // Call ringing / in progress (or similar short interruption): pause,
59
+ // keep our focus request registered, and resume when focus returns.
60
+ if (playbackActive) {
61
+ resumeOnFocusGain = true
62
+ pauseInternal(fromFocusLoss = true)
63
+ }
64
+ }
65
+ AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK -> {
66
+ // Short prompt where we may keep playing quietly. Only delivered on
67
+ // pre-O devices — on O+ the system ducks automatically without a
68
+ // callback because the focus request doesn't opt out of auto-duck.
69
+ try { mediaPlayer?.setVolume(DUCK_VOLUME, DUCK_VOLUME) } catch (_: Exception) {}
70
+ }
71
+ AudioManager.AUDIOFOCUS_LOSS -> {
72
+ // Permanent loss (another app started media playback): pause and give
73
+ // up focus. Standard platform behavior is to NOT auto-resume here.
74
+ resumeOnFocusGain = false
75
+ if (playbackActive) {
76
+ pauseInternal(fromFocusLoss = false)
77
+ } else {
78
+ abandonAudioFocus()
79
+ }
80
+ }
81
+ AudioManager.AUDIOFOCUS_GAIN -> {
82
+ // Interruption over: restore full volume and, if the interruption is
83
+ // what paused us (call ended), resume from the preserved position.
84
+ try { mediaPlayer?.setVolume(1f, 1f) } catch (_: Exception) {}
85
+ if (resumeOnFocusGain) {
86
+ resumeOnFocusGain = false
87
+ resume()
88
+ }
89
+ }
90
+ }
91
+ }
92
+ }
93
+
94
+ /** Lazily bind the AudioManager from any available context. */
95
+ private fun ensureAudioManager(context: Context?): AudioManager? {
96
+ audioManager?.let { return it }
97
+ val appContext = context?.applicationContext ?: reactContext?.applicationContext
98
+ audioManager = appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager
99
+ return audioManager
100
+ }
101
+
102
+ /**
103
+ * Request audio focus before starting playback. Returns true when playback may start.
104
+ * Degrades gracefully: if no AudioManager can be resolved, playback proceeds exactly
105
+ * as it did before this feature existed.
106
+ */
107
+ private fun requestAudioFocus(): Boolean {
108
+ if (hasAudioFocus) return true
109
+ val am = ensureAudioManager(null) ?: return true
110
+ val result = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
111
+ val request = focusRequest ?: AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
112
+ .setAudioAttributes(playbackAttributes)
113
+ .setOnAudioFocusChangeListener(focusChangeListener, mainHandler)
114
+ .build()
115
+ .also { focusRequest = it }
116
+ am.requestAudioFocus(request)
117
+ } else {
118
+ @Suppress("DEPRECATION")
119
+ am.requestAudioFocus(
120
+ focusChangeListener,
121
+ AudioManager.STREAM_MUSIC,
122
+ AudioManager.AUDIOFOCUS_GAIN
123
+ )
124
+ }
125
+ hasAudioFocus = result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
126
+ return hasAudioFocus
127
+ }
128
+
129
+ private fun abandonAudioFocus() {
130
+ resumeOnFocusGain = false
131
+ if (!hasAudioFocus) return
132
+ hasAudioFocus = false
133
+ val am = audioManager ?: return
134
+ try {
135
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
136
+ focusRequest?.let { am.abandonAudioFocusRequest(it) }
137
+ } else {
138
+ @Suppress("DEPRECATION")
139
+ am.abandonAudioFocus(focusChangeListener)
140
+ }
141
+ } catch (_: Exception) {}
142
+ }
143
+
144
+ interface Listener {
145
+ fun onProgressChanged(position: Int, duration: Int)
146
+ fun onStateChanged(isPlaying: Boolean, prepared: Boolean)
147
+ fun onSourceChanged(uri: Uri?)
148
+ fun onCompletion()
149
+ }
150
+
151
+ private val progressUpdater = object : Runnable {
152
+ override fun run() {
153
+ val mp = mediaPlayer
154
+ if (mp != null && prepared) {
155
+ val duration = try { mp.duration } catch (e: Exception) { 0 }
156
+ val pos = try { mp.currentPosition } catch (e: Exception) { 0 }
157
+ if (duration > 0) {
158
+ listeners.forEach { it.onProgressChanged(pos, duration) }
159
+
160
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
161
+ ?.emit("onAudioProgress", Arguments.createMap().apply {
162
+ putInt("position", pos)
163
+ putInt("duration", duration)
164
+ })
165
+ }
166
+ }
167
+ if (playbackActive) mainHandler.postDelayed(this, 250)
168
+ }
169
+ }
170
+
171
+ fun play(context: Context, uri: Uri?, title: String) {
172
+ if (uri == null) return
173
+ // Bind the AudioManager while a context is guaranteed available, so focus
174
+ // requests/abandons never depend on the React context being set.
175
+ ensureAudioManager(context)
176
+ if (uri == currentUri) {
177
+ resume()
178
+ return
179
+ }
180
+ release()
181
+ currentUri = uri
182
+ currentTitle = title
183
+ prepared = false
184
+ listeners.forEach { it.onSourceChanged(uri) }
185
+
186
+ mediaPlayer = MediaPlayer().apply {
187
+ setAudioAttributes(playbackAttributes)
188
+ setOnPreparedListener { mp ->
189
+ prepared = true
190
+ listeners.forEach { it.onStateChanged(playbackActive, prepared) }
191
+
192
+ // Emitting the new audio state
193
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
194
+ ?.emit("onAudioStateChange", Arguments.createMap().apply {
195
+ putBoolean("isPlaying", playbackActive)
196
+ putString("uri", uri.toString())
197
+ putString("title", title)
198
+ putInt("duration", mp.duration)
199
+ })
200
+ }
201
+ setOnCompletionListener { mp ->
202
+ playbackActive = false
203
+ // Track finished naturally — nothing left to interrupt, so release focus
204
+ // (and any pending call-resume intent) back to the system.
205
+ abandonAudioFocus()
206
+ listeners.forEach { it.onCompletion() }
207
+ mainHandler.removeCallbacks(progressUpdater)
208
+ try { mp.seekTo(0) } catch (e: Exception) {}
209
+
210
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
211
+ ?.emit("onAudioStateChange", Arguments.createMap().apply {
212
+ putBoolean("isPlaying", false)
213
+ putBoolean("didComplete", true)
214
+ })
215
+ }
216
+ setOnErrorListener { _, _, _ -> true }
217
+ try {
218
+ setDataSource(context, uri)
219
+ prepareAsync()
220
+ } catch (e: Exception) {
221
+ }
222
+ }
223
+ }
224
+
225
+ fun resume() {
226
+ val mp = mediaPlayer ?: return
227
+ // Every start goes through here (resume() is the only mp.start() call site), so
228
+ // this is the single choke point for acquiring audio focus. If the system denies
229
+ // focus (e.g. a call is literally in progress), do not start — the player stays
230
+ // paused at its current position and the UI keeps showing the play button.
231
+ if (!requestAudioFocus()) return
232
+ try {
233
+ mp.setVolume(1f, 1f) // clear any duck applied during an interruption
234
+ mp.start()
235
+ playbackActive = true
236
+ listeners.forEach { it.onStateChanged(playbackActive, prepared) }
237
+ mainHandler.post(progressUpdater)
238
+
239
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
240
+ ?.emit("onAudioStateChange", Arguments.createMap().apply {
241
+ putBoolean("isPlaying", true)
242
+ currentUri?.let { putString("uri", it.toString()) }
243
+ })
244
+ } catch (e: Exception) {}
245
+ }
246
+
247
+ fun pause() {
248
+ // Explicit (user/JS-initiated) pause: cancel any pending call-interruption
249
+ // auto-resume — if the user pauses during a call, the end of the call must not
250
+ // restart playback behind their back.
251
+ resumeOnFocusGain = false
252
+ pauseInternal(fromFocusLoss = false)
253
+ }
254
+
255
+ /**
256
+ * Shared pause path. [fromFocusLoss] true = a transient interruption (call) paused
257
+ * us, so keep the focus request registered — that's what makes the system deliver
258
+ * AUDIOFOCUS_GAIN when the call ends, driving the auto-resume. False = a deliberate
259
+ * stop; release focus so other apps get clean gain events.
260
+ */
261
+ private fun pauseInternal(fromFocusLoss: Boolean) {
262
+ val mp = mediaPlayer ?: return
263
+ try {
264
+ if (mp.isPlaying) mp.pause()
265
+ } catch (e: Exception) {}
266
+ playbackActive = false
267
+ if (!fromFocusLoss) abandonAudioFocus()
268
+ listeners.forEach { it.onStateChanged(playbackActive, prepared) }
269
+ mainHandler.removeCallbacks(progressUpdater)
270
+
271
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
272
+ ?.emit("onAudioStateChange", Arguments.createMap().apply {
273
+ putBoolean("isPlaying", false)
274
+ currentUri?.let { putString("uri", it.toString()) }
275
+ })
276
+ }
277
+
278
+ fun togglePlayback() {
279
+ if (playbackActive) pause() else resume()
280
+ }
281
+
282
+ fun seekTo(positionMs: Int) {
283
+ val mp = mediaPlayer ?: return
284
+ if (!prepared) return
285
+ try {
286
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
287
+ mp.seekTo(positionMs.toLong(), MediaPlayer.SEEK_CLOSEST)
288
+ } else {
289
+ mp.seekTo(positionMs)
290
+ }
291
+ val duration = mp.duration
292
+ listeners.forEach { it.onProgressChanged(positionMs, duration) }
293
+
294
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
295
+ ?.emit("onAudioProgress", Arguments.createMap().apply {
296
+ putInt("position", positionMs)
297
+ putInt("duration", duration)
298
+ })
299
+ } catch (e: Exception) {}
300
+ }
301
+
302
+ fun release() {
303
+ mainHandler.removeCallbacks(progressUpdater)
304
+ playbackActive = false
305
+ prepared = false
306
+ abandonAudioFocus()
307
+ mediaPlayer?.let { mp ->
308
+ try { mp.stop() } catch (e: Exception) {}
309
+ try { mp.reset() } catch (e: Exception) {}
310
+ mp.release()
311
+ }
312
+ mediaPlayer = null
313
+ currentUri = null
314
+ currentTitle = ""
315
+ listeners.forEach { it.onSourceChanged(null) }
316
+
317
+ reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
318
+ ?.emit("onAudioClose", Arguments.createMap())
319
+ }
320
+ }