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.
- package/CustomCamera.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +833 -0
- package/android/build.gradle +135 -0
- package/android/consumer-rules.pro +47 -0
- package/android/src/main/AndroidManifest.xml +72 -0
- package/android/src/main/java/android/print/PdfPrint.java +51 -0
- package/android/src/main/java/com/customcamera/AnimatedImageViewManager.kt +199 -0
- package/android/src/main/java/com/customcamera/AudioPlayerView.kt +322 -0
- package/android/src/main/java/com/customcamera/AudioRecorder.kt +506 -0
- package/android/src/main/java/com/customcamera/BarcodeScannerActivity.kt +361 -0
- package/android/src/main/java/com/customcamera/BlurBackgroundView.kt +199 -0
- package/android/src/main/java/com/customcamera/CacheCleanupManager.kt +386 -0
- package/android/src/main/java/com/customcamera/CameraAspectRatioMode.kt +77 -0
- package/android/src/main/java/com/customcamera/CameraAsset.kt +211 -0
- package/android/src/main/java/com/customcamera/CameraTheme.kt +479 -0
- package/android/src/main/java/com/customcamera/CompressionCacheStore.kt +504 -0
- package/android/src/main/java/com/customcamera/CompressionFileNameUtils.kt +7 -0
- package/android/src/main/java/com/customcamera/CompressionProfile.kt +195 -0
- package/android/src/main/java/com/customcamera/CustomCameraActivity.kt +7291 -0
- package/android/src/main/java/com/customcamera/CustomCameraFileProvider.kt +9 -0
- package/android/src/main/java/com/customcamera/CustomCameraModule.kt +2479 -0
- package/android/src/main/java/com/customcamera/CustomCameraPackage.kt +53 -0
- package/android/src/main/java/com/customcamera/DisplayUtils.kt +9 -0
- package/android/src/main/java/com/customcamera/DocumentPreviewView.kt +846 -0
- package/android/src/main/java/com/customcamera/DrawingView.kt +214 -0
- package/android/src/main/java/com/customcamera/EmojiPickerData.kt +228 -0
- package/android/src/main/java/com/customcamera/FilterStripAdapter.kt +158 -0
- package/android/src/main/java/com/customcamera/GifSupport.kt +215 -0
- package/android/src/main/java/com/customcamera/GlobalAudioPlayer.kt +320 -0
- package/android/src/main/java/com/customcamera/ImageExporter.kt +1394 -0
- package/android/src/main/java/com/customcamera/ImageFormatSupport.kt +163 -0
- package/android/src/main/java/com/customcamera/Localization.kt +206 -0
- package/android/src/main/java/com/customcamera/MediaAdapters.kt +758 -0
- package/android/src/main/java/com/customcamera/MediaCacheManager.kt +782 -0
- package/android/src/main/java/com/customcamera/MediaExportFormats.kt +201 -0
- package/android/src/main/java/com/customcamera/MediaFileIo.kt +310 -0
- package/android/src/main/java/com/customcamera/MediaMetadataStore.kt +494 -0
- package/android/src/main/java/com/customcamera/MediaPreviewEditor.kt +14865 -0
- package/android/src/main/java/com/customcamera/MediaPublisher.kt +423 -0
- package/android/src/main/java/com/customcamera/MediaStorageWorkflow.kt +722 -0
- package/android/src/main/java/com/customcamera/MediaStoreRepository.kt +378 -0
- package/android/src/main/java/com/customcamera/MediaThumbnailView.kt +286 -0
- package/android/src/main/java/com/customcamera/MediaUtils.kt +37 -0
- package/android/src/main/java/com/customcamera/NativeVideoViewManager.kt +845 -0
- package/android/src/main/java/com/customcamera/OfficeDocumentConverter.kt +2791 -0
- package/android/src/main/java/com/customcamera/PendingCaptureCrops.kt +88 -0
- package/android/src/main/java/com/customcamera/PowerPointPreview.kt +4077 -0
- package/android/src/main/java/com/customcamera/RecentPickerStore.kt +74 -0
- package/android/src/main/java/com/customcamera/SecureFileEraser.kt +411 -0
- package/android/src/main/java/com/customcamera/SecureKeyModule.kt +88 -0
- package/android/src/main/java/com/customcamera/SecureMediaDeleter.kt +493 -0
- package/android/src/main/java/com/customcamera/StickerPickerData.kt +107 -0
- package/android/src/main/java/com/customcamera/SvgDrawable.kt +175 -0
- package/android/src/main/java/com/customcamera/SvgIconViewManager.kt +77 -0
- package/android/src/main/java/com/customcamera/TextEntryController.kt +553 -0
- package/android/src/main/java/com/customcamera/ThumbnailLoader.kt +455 -0
- package/android/src/main/java/com/customcamera/UploadService.kt +909 -0
- package/android/src/main/java/com/customcamera/VideoEditUtils.kt +210 -0
- package/android/src/main/java/com/customcamera/VideoRotationTranscoder.kt +1992 -0
- package/android/src/main/java/com/customcamera/gif/AnimatedGifEncoder.kt +473 -0
- package/android/src/main/java/com/customcamera/gif/GifDecoder.kt +690 -0
- package/android/src/main/java/com/customcamera/gif/GifFramesDrawable.kt +160 -0
- package/android/src/main/java/com/customcamera/gif/GifHttp.kt +112 -0
- package/android/src/main/java/com/customcamera/gif/GifImageLoader.kt +186 -0
- package/android/src/main/java/com/customcamera/gif/GifModels.kt +74 -0
- package/android/src/main/java/com/customcamera/gif/GifProvider.kt +49 -0
- package/android/src/main/java/com/customcamera/gif/GifProviders.kt +27 -0
- package/android/src/main/java/com/customcamera/gif/GifRepository.kt +124 -0
- package/android/src/main/java/com/customcamera/gif/GifSafety.kt +33 -0
- package/android/src/main/java/com/customcamera/gif/GiphyGifProvider.kt +146 -0
- package/android/src/main/java/com/customcamera/gif/LZWEncoder.kt +258 -0
- package/android/src/main/java/com/customcamera/gif/NeuQuant.kt +435 -0
- package/android/src/main/java/com/customcamera/gif/TenorGifProvider.kt +147 -0
- package/android/src/main/res/drawable/bg_brush_selected.xml +5 -0
- package/android/src/main/res/drawable/bg_color_swatch.xml +5 -0
- package/android/src/main/res/drawable/bg_editor_toolbar.xml +6 -0
- package/android/src/main/res/drawable/bg_icon_circle.xml +5 -0
- package/android/src/main/res/drawable/bg_icon_circle_light.xml +5 -0
- package/android/src/main/res/drawable/bg_mode_tab_active.xml +6 -0
- package/android/src/main/res/drawable/bg_pill.xml +6 -0
- package/android/src/main/res/drawable/bg_recording_dot.xml +6 -0
- package/android/src/main/res/drawable/bg_selection_badge.xml +8 -0
- package/android/src/main/res/drawable/bg_send_button.xml +5 -0
- package/android/src/main/res/drawable/bg_shutter_inner.xml +5 -0
- package/android/src/main/res/drawable/bg_shutter_recording.xml +6 -0
- package/android/src/main/res/drawable/bg_shutter_ring.xml +8 -0
- package/android/src/main/res/drawable/bg_shutter_video_idle.xml +5 -0
- package/android/src/main/res/drawable/bg_thumbnail.xml +9 -0
- package/android/src/main/res/drawable/ic_aspect_ratio.xml +17 -0
- package/android/src/main/res/drawable/ic_audio.xml +10 -0
- package/android/src/main/res/drawable/ic_back.xml +10 -0
- package/android/src/main/res/drawable/ic_brush_medium.xml +12 -0
- package/android/src/main/res/drawable/ic_brush_thick.xml +12 -0
- package/android/src/main/res/drawable/ic_brush_thin.xml +12 -0
- package/android/src/main/res/drawable/ic_check.xml +17 -0
- package/android/src/main/res/drawable/ic_chevron_down.xml +10 -0
- package/android/src/main/res/drawable/ic_chevron_up.xml +13 -0
- package/android/src/main/res/drawable/ic_clear_drawing.xml +10 -0
- package/android/src/main/res/drawable/ic_close.xml +10 -0
- package/android/src/main/res/drawable/ic_cover.xml +9 -0
- package/android/src/main/res/drawable/ic_crop.xml +10 -0
- package/android/src/main/res/drawable/ic_delete.xml +9 -0
- package/android/src/main/res/drawable/ic_document.xml +10 -0
- package/android/src/main/res/drawable/ic_done.xml +10 -0
- package/android/src/main/res/drawable/ic_download.xml +10 -0
- package/android/src/main/res/drawable/ic_emoji.xml +10 -0
- package/android/src/main/res/drawable/ic_filter.xml +10 -0
- package/android/src/main/res/drawable/ic_flash_auto.xml +7 -0
- package/android/src/main/res/drawable/ic_flash_off.xml +23 -0
- package/android/src/main/res/drawable/ic_flash_on.xml +16 -0
- package/android/src/main/res/drawable/ic_flip.xml +15 -0
- package/android/src/main/res/drawable/ic_gallery.xml +10 -0
- package/android/src/main/res/drawable/ic_gif.xml +24 -0
- package/android/src/main/res/drawable/ic_lock.xml +19 -0
- package/android/src/main/res/drawable/ic_lock_open.xml +22 -0
- package/android/src/main/res/drawable/ic_marker.xml +10 -0
- package/android/src/main/res/drawable/ic_more_vert.xml +18 -0
- package/android/src/main/res/drawable/ic_overlay_close.xml +19 -0
- package/android/src/main/res/drawable/ic_overlay_resize.xml +18 -0
- package/android/src/main/res/drawable/ic_pause.xml +10 -0
- package/android/src/main/res/drawable/ic_photo_mode.xml +10 -0
- package/android/src/main/res/drawable/ic_play.xml +10 -0
- package/android/src/main/res/drawable/ic_redo.xml +10 -0
- package/android/src/main/res/drawable/ic_rotate.xml +9 -0
- package/android/src/main/res/drawable/ic_send.xml +10 -0
- package/android/src/main/res/drawable/ic_share.xml +10 -0
- package/android/src/main/res/drawable/ic_stat_upload.xml +10 -0
- package/android/src/main/res/drawable/ic_sticker.xml +24 -0
- package/android/src/main/res/drawable/ic_text.xml +10 -0
- package/android/src/main/res/drawable/ic_text_align_center.xml +11 -0
- package/android/src/main/res/drawable/ic_text_align_left.xml +11 -0
- package/android/src/main/res/drawable/ic_text_align_right.xml +11 -0
- package/android/src/main/res/drawable/ic_text_bg.xml +11 -0
- package/android/src/main/res/drawable/ic_trim.xml +23 -0
- package/android/src/main/res/drawable/ic_undo.xml +10 -0
- package/android/src/main/res/drawable/ic_video_mode.xml +10 -0
- package/android/src/main/res/drawable/ic_video_note.xml +13 -0
- package/android/src/main/res/drawable/ic_volume_off.xml +9 -0
- package/android/src/main/res/drawable/ic_volume_on.xml +9 -0
- package/android/src/main/res/drawable/sticker_heart.xml +9 -0
- package/android/src/main/res/drawable/sticker_smile.xml +9 -0
- package/android/src/main/res/drawable/sticker_star.xml +9 -0
- package/android/src/main/res/drawable/sticker_thumbsup.xml +9 -0
- package/android/src/main/res/values/colors.xml +34 -0
- package/android/src/main/res/values/ids.xml +15 -0
- package/android/src/main/res/values/strings.xml +15 -0
- package/android/src/main/res/values/styles.xml +20 -0
- package/android/src/main/res/xml/file_paths.xml +34 -0
- package/ios/CustomCamera.h +5 -0
- package/ios/CustomCamera.mm +214 -0
- package/lib/module/AudioPlayerContext.js +233 -0
- package/lib/module/AudioPlayerContext.js.map +1 -0
- package/lib/module/CameraPreview.js +304 -0
- package/lib/module/CameraPreview.js.map +1 -0
- package/lib/module/NativeAnimatedImageView.js +41 -0
- package/lib/module/NativeAnimatedImageView.js.map +1 -0
- package/lib/module/NativeAudioPlayerView.js +33 -0
- package/lib/module/NativeAudioPlayerView.js.map +1 -0
- package/lib/module/NativeBlurView.js +35 -0
- package/lib/module/NativeBlurView.js.map +1 -0
- package/lib/module/NativeCustomCamera.js +128 -0
- package/lib/module/NativeCustomCamera.js.map +1 -0
- package/lib/module/NativeCustomCamera.web.js +143 -0
- package/lib/module/NativeCustomCamera.web.js.map +1 -0
- package/lib/module/NativeCustomVideoView.js +5 -0
- package/lib/module/NativeCustomVideoView.js.map +1 -0
- package/lib/module/NativeDocumentPreviewView.js +156 -0
- package/lib/module/NativeDocumentPreviewView.js.map +1 -0
- package/lib/module/NativeMediaThumbnailView.js +110 -0
- package/lib/module/NativeMediaThumbnailView.js.map +1 -0
- package/lib/module/NativeSvgIcon.js +32 -0
- package/lib/module/NativeSvgIcon.js.map +1 -0
- package/lib/module/ResponsivePreviewContainer.js +171 -0
- package/lib/module/ResponsivePreviewContainer.js.map +1 -0
- package/lib/module/accessibility.js +61 -0
- package/lib/module/accessibility.js.map +1 -0
- package/lib/module/config.js +352 -0
- package/lib/module/config.js.map +1 -0
- package/lib/module/cpuOptimization.js +296 -0
- package/lib/module/cpuOptimization.js.map +1 -0
- package/lib/module/db/DatabaseManager.js +191 -0
- package/lib/module/db/DatabaseManager.js.map +1 -0
- package/lib/module/db/MediaMapper.js +284 -0
- package/lib/module/db/MediaMapper.js.map +1 -0
- package/lib/module/db/MediaRepository.js +242 -0
- package/lib/module/db/MediaRepository.js.map +1 -0
- package/lib/module/db/index.js +41 -0
- package/lib/module/db/index.js.map +1 -0
- package/lib/module/db/migrations.js +180 -0
- package/lib/module/db/migrations.js.map +1 -0
- package/lib/module/db/react-native-sqlcipher.d.js +2 -0
- package/lib/module/db/react-native-sqlcipher.d.js.map +1 -0
- package/lib/module/db/secureKey.js +87 -0
- package/lib/module/db/secureKey.js.map +1 -0
- package/lib/module/db/types.js +57 -0
- package/lib/module/db/types.js.map +1 -0
- package/lib/module/db/useMediaDatabase.js +123 -0
- package/lib/module/db/useMediaDatabase.js.map +1 -0
- package/lib/module/defaultIcons.js +2259 -0
- package/lib/module/defaultIcons.js.map +1 -0
- package/lib/module/deviceCapabilities.js +408 -0
- package/lib/module/deviceCapabilities.js.map +1 -0
- package/lib/module/errors.js +267 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/iconConfig.js +796 -0
- package/lib/module/iconConfig.js.map +1 -0
- package/lib/module/icons.js +514 -0
- package/lib/module/icons.js.map +1 -0
- package/lib/module/index.js +251 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/jest-globals.d.js +2 -0
- package/lib/module/jest-globals.d.js.map +1 -0
- package/lib/module/library/MediaLibrary.js +107 -0
- package/lib/module/library/MediaLibrary.js.map +1 -0
- package/lib/module/library/MediaLibraryScreen.js +2597 -0
- package/lib/module/library/MediaLibraryScreen.js.map +1 -0
- package/lib/module/library/components/AudioPreview.js +74 -0
- package/lib/module/library/components/AudioPreview.js.map +1 -0
- package/lib/module/library/components/DocumentPreview.js +331 -0
- package/lib/module/library/components/DocumentPreview.js.map +1 -0
- package/lib/module/library/components/ExpandableFab.js +343 -0
- package/lib/module/library/components/ExpandableFab.js.map +1 -0
- package/lib/module/library/components/FileTiles.js +251 -0
- package/lib/module/library/components/FileTiles.js.map +1 -0
- package/lib/module/library/components/ImagePreview.js +70 -0
- package/lib/module/library/components/ImagePreview.js.map +1 -0
- package/lib/module/library/components/MediaMetadataHeader.js +175 -0
- package/lib/module/library/components/MediaMetadataHeader.js.map +1 -0
- package/lib/module/library/components/RecordAudioScreen.js +816 -0
- package/lib/module/library/components/RecordAudioScreen.js.map +1 -0
- package/lib/module/library/components/VideoPreview.js +453 -0
- package/lib/module/library/components/VideoPreview.js.map +1 -0
- package/lib/module/library/components/ViewerCounter.js +55 -0
- package/lib/module/library/components/ViewerCounter.js.map +1 -0
- package/lib/module/library/compressionEstimate.js +507 -0
- package/lib/module/library/compressionEstimate.js.map +1 -0
- package/lib/module/library/fileMetadata.js +196 -0
- package/lib/module/library/fileMetadata.js.map +1 -0
- package/lib/module/library/index.js +42 -0
- package/lib/module/library/index.js.map +1 -0
- package/lib/module/library/libraryIconRegistry.js +69 -0
- package/lib/module/library/libraryIconRegistry.js.map +1 -0
- package/lib/module/library/libraryIcons.js +858 -0
- package/lib/module/library/libraryIcons.js.map +1 -0
- package/lib/module/library/localizedFormat.js +181 -0
- package/lib/module/library/localizedFormat.js.map +1 -0
- package/lib/module/library/palette.js +225 -0
- package/lib/module/library/palette.js.map +1 -0
- package/lib/module/library/responsive.js +53 -0
- package/lib/module/library/responsive.js.map +1 -0
- package/lib/module/locales.js +416 -0
- package/lib/module/locales.js.map +1 -0
- package/lib/module/localization.js +1064 -0
- package/lib/module/localization.js.map +1 -0
- package/lib/module/logger.js +20 -0
- package/lib/module/logger.js.map +1 -0
- package/lib/module/lowMemoryCache.js +206 -0
- package/lib/module/lowMemoryCache.js.map +1 -0
- package/lib/module/mediaHistory.js +33 -0
- package/lib/module/mediaHistory.js.map +1 -0
- package/lib/module/mediaStorageService.js +402 -0
- package/lib/module/mediaStorageService.js.map +1 -0
- package/lib/module/memory.js +79 -0
- package/lib/module/memory.js.map +1 -0
- package/lib/module/nativeActions.js +307 -0
- package/lib/module/nativeActions.js.map +1 -0
- package/lib/module/nativeComponentSupport.js +61 -0
- package/lib/module/nativeComponentSupport.js.map +1 -0
- package/lib/module/networkPerformance.js +24 -0
- package/lib/module/networkPerformance.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/performance.js +104 -0
- package/lib/module/performance.js.map +1 -0
- package/lib/module/performanceMonitor.js +155 -0
- package/lib/module/performanceMonitor.js.map +1 -0
- package/lib/module/permissions.js +370 -0
- package/lib/module/permissions.js.map +1 -0
- package/lib/module/rendering.js +40 -0
- package/lib/module/rendering.js.map +1 -0
- package/lib/module/security.js +18 -0
- package/lib/module/security.js.map +1 -0
- package/lib/module/state.js +266 -0
- package/lib/module/state.js.map +1 -0
- package/lib/module/storage.js +400 -0
- package/lib/module/storage.js.map +1 -0
- package/lib/module/theme.js +390 -0
- package/lib/module/theme.js.map +1 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils.js +115 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/module/validation.js +288 -0
- package/lib/module/validation.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/AudioPlayerContext.d.ts +27 -0
- package/lib/typescript/src/AudioPlayerContext.d.ts.map +1 -0
- package/lib/typescript/src/CameraPreview.d.ts +13 -0
- package/lib/typescript/src/CameraPreview.d.ts.map +1 -0
- package/lib/typescript/src/NativeAnimatedImageView.d.ts +33 -0
- package/lib/typescript/src/NativeAnimatedImageView.d.ts.map +1 -0
- package/lib/typescript/src/NativeAudioPlayerView.d.ts +12 -0
- package/lib/typescript/src/NativeAudioPlayerView.d.ts.map +1 -0
- package/lib/typescript/src/NativeBlurView.d.ts +12 -0
- package/lib/typescript/src/NativeBlurView.d.ts.map +1 -0
- package/lib/typescript/src/NativeCustomCamera.d.ts +167 -0
- package/lib/typescript/src/NativeCustomCamera.d.ts.map +1 -0
- package/lib/typescript/src/NativeCustomCamera.web.d.ts +47 -0
- package/lib/typescript/src/NativeCustomCamera.web.d.ts.map +1 -0
- package/lib/typescript/src/NativeCustomVideoView.d.ts +30 -0
- package/lib/typescript/src/NativeCustomVideoView.d.ts.map +1 -0
- package/lib/typescript/src/NativeDocumentPreviewView.d.ts +48 -0
- package/lib/typescript/src/NativeDocumentPreviewView.d.ts.map +1 -0
- package/lib/typescript/src/NativeMediaThumbnailView.d.ts +54 -0
- package/lib/typescript/src/NativeMediaThumbnailView.d.ts.map +1 -0
- package/lib/typescript/src/NativeSvgIcon.d.ts +25 -0
- package/lib/typescript/src/NativeSvgIcon.d.ts.map +1 -0
- package/lib/typescript/src/ResponsivePreviewContainer.d.ts +92 -0
- package/lib/typescript/src/ResponsivePreviewContainer.d.ts.map +1 -0
- package/lib/typescript/src/accessibility.d.ts +20 -0
- package/lib/typescript/src/accessibility.d.ts.map +1 -0
- package/lib/typescript/src/config.d.ts +145 -0
- package/lib/typescript/src/config.d.ts.map +1 -0
- package/lib/typescript/src/cpuOptimization.d.ts +63 -0
- package/lib/typescript/src/cpuOptimization.d.ts.map +1 -0
- package/lib/typescript/src/db/DatabaseManager.d.ts +48 -0
- package/lib/typescript/src/db/DatabaseManager.d.ts.map +1 -0
- package/lib/typescript/src/db/MediaMapper.d.ts +77 -0
- package/lib/typescript/src/db/MediaMapper.d.ts.map +1 -0
- package/lib/typescript/src/db/MediaRepository.d.ts +66 -0
- package/lib/typescript/src/db/MediaRepository.d.ts.map +1 -0
- package/lib/typescript/src/db/index.d.ts +23 -0
- package/lib/typescript/src/db/index.d.ts.map +1 -0
- package/lib/typescript/src/db/migrations.d.ts +31 -0
- package/lib/typescript/src/db/migrations.d.ts.map +1 -0
- package/lib/typescript/src/db/secureKey.d.ts +10 -0
- package/lib/typescript/src/db/secureKey.d.ts.map +1 -0
- package/lib/typescript/src/db/types.d.ts +115 -0
- package/lib/typescript/src/db/types.d.ts.map +1 -0
- package/lib/typescript/src/db/useMediaDatabase.d.ts +41 -0
- package/lib/typescript/src/db/useMediaDatabase.d.ts.map +1 -0
- package/lib/typescript/src/defaultIcons.d.ts +139 -0
- package/lib/typescript/src/defaultIcons.d.ts.map +1 -0
- package/lib/typescript/src/deviceCapabilities.d.ts +92 -0
- package/lib/typescript/src/deviceCapabilities.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +95 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/iconConfig.d.ts +176 -0
- package/lib/typescript/src/iconConfig.d.ts.map +1 -0
- package/lib/typescript/src/icons.d.ts +217 -0
- package/lib/typescript/src/icons.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +104 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/library/MediaLibrary.d.ts +57 -0
- package/lib/typescript/src/library/MediaLibrary.d.ts.map +1 -0
- package/lib/typescript/src/library/MediaLibraryScreen.d.ts +51 -0
- package/lib/typescript/src/library/MediaLibraryScreen.d.ts.map +1 -0
- package/lib/typescript/src/library/components/AudioPreview.d.ts +6 -0
- package/lib/typescript/src/library/components/AudioPreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/DocumentPreview.d.ts +10 -0
- package/lib/typescript/src/library/components/DocumentPreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/ExpandableFab.d.ts +39 -0
- package/lib/typescript/src/library/components/ExpandableFab.d.ts.map +1 -0
- package/lib/typescript/src/library/components/FileTiles.d.ts +8 -0
- package/lib/typescript/src/library/components/FileTiles.d.ts.map +1 -0
- package/lib/typescript/src/library/components/ImagePreview.d.ts +5 -0
- package/lib/typescript/src/library/components/ImagePreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts +16 -0
- package/lib/typescript/src/library/components/MediaMetadataHeader.d.ts.map +1 -0
- package/lib/typescript/src/library/components/RecordAudioScreen.d.ts +8 -0
- package/lib/typescript/src/library/components/RecordAudioScreen.d.ts.map +1 -0
- package/lib/typescript/src/library/components/VideoPreview.d.ts +20 -0
- package/lib/typescript/src/library/components/VideoPreview.d.ts.map +1 -0
- package/lib/typescript/src/library/components/ViewerCounter.d.ts +13 -0
- package/lib/typescript/src/library/components/ViewerCounter.d.ts.map +1 -0
- package/lib/typescript/src/library/compressionEstimate.d.ts +313 -0
- package/lib/typescript/src/library/compressionEstimate.d.ts.map +1 -0
- package/lib/typescript/src/library/fileMetadata.d.ts +33 -0
- package/lib/typescript/src/library/fileMetadata.d.ts.map +1 -0
- package/lib/typescript/src/library/index.d.ts +40 -0
- package/lib/typescript/src/library/index.d.ts.map +1 -0
- package/lib/typescript/src/library/libraryIconRegistry.d.ts +35 -0
- package/lib/typescript/src/library/libraryIconRegistry.d.ts.map +1 -0
- package/lib/typescript/src/library/libraryIcons.d.ts +85 -0
- package/lib/typescript/src/library/libraryIcons.d.ts.map +1 -0
- package/lib/typescript/src/library/localizedFormat.d.ts +49 -0
- package/lib/typescript/src/library/localizedFormat.d.ts.map +1 -0
- package/lib/typescript/src/library/palette.d.ts +104 -0
- package/lib/typescript/src/library/palette.d.ts.map +1 -0
- package/lib/typescript/src/library/responsive.d.ts +15 -0
- package/lib/typescript/src/library/responsive.d.ts.map +1 -0
- package/lib/typescript/src/locales.d.ts +103 -0
- package/lib/typescript/src/locales.d.ts.map +1 -0
- package/lib/typescript/src/localization.d.ts +1051 -0
- package/lib/typescript/src/localization.d.ts.map +1 -0
- package/lib/typescript/src/logger.d.ts +4 -0
- package/lib/typescript/src/logger.d.ts.map +1 -0
- package/lib/typescript/src/lowMemoryCache.d.ts +64 -0
- package/lib/typescript/src/lowMemoryCache.d.ts.map +1 -0
- package/lib/typescript/src/mediaHistory.d.ts +3 -0
- package/lib/typescript/src/mediaHistory.d.ts.map +1 -0
- package/lib/typescript/src/mediaStorageService.d.ts +155 -0
- package/lib/typescript/src/mediaStorageService.d.ts.map +1 -0
- package/lib/typescript/src/memory.d.ts +21 -0
- package/lib/typescript/src/memory.d.ts.map +1 -0
- package/lib/typescript/src/nativeActions.d.ts +174 -0
- package/lib/typescript/src/nativeActions.d.ts.map +1 -0
- package/lib/typescript/src/nativeComponentSupport.d.ts +30 -0
- package/lib/typescript/src/nativeComponentSupport.d.ts.map +1 -0
- package/lib/typescript/src/networkPerformance.d.ts +12 -0
- package/lib/typescript/src/networkPerformance.d.ts.map +1 -0
- package/lib/typescript/src/performance.d.ts +32 -0
- package/lib/typescript/src/performance.d.ts.map +1 -0
- package/lib/typescript/src/performanceMonitor.d.ts +45 -0
- package/lib/typescript/src/performanceMonitor.d.ts.map +1 -0
- package/lib/typescript/src/permissions.d.ts +132 -0
- package/lib/typescript/src/permissions.d.ts.map +1 -0
- package/lib/typescript/src/rendering.d.ts +9 -0
- package/lib/typescript/src/rendering.d.ts.map +1 -0
- package/lib/typescript/src/security.d.ts +6 -0
- package/lib/typescript/src/security.d.ts.map +1 -0
- package/lib/typescript/src/state.d.ts +102 -0
- package/lib/typescript/src/state.d.ts.map +1 -0
- package/lib/typescript/src/storage.d.ts +117 -0
- package/lib/typescript/src/storage.d.ts.map +1 -0
- package/lib/typescript/src/theme.d.ts +15 -0
- package/lib/typescript/src/theme.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +802 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils.d.ts +50 -0
- package/lib/typescript/src/utils.d.ts.map +1 -0
- package/lib/typescript/src/validation.d.ts +52 -0
- package/lib/typescript/src/validation.d.ts.map +1 -0
- package/package.json +204 -0
- package/react-native.config.js +17 -0
- package/src/AudioPlayerContext.tsx +220 -0
- package/src/CameraPreview.tsx +352 -0
- package/src/NativeAnimatedImageView.tsx +71 -0
- package/src/NativeAudioPlayerView.tsx +39 -0
- package/src/NativeBlurView.tsx +40 -0
- package/src/NativeCustomCamera.ts +305 -0
- package/src/NativeCustomCamera.web.ts +192 -0
- package/src/NativeCustomVideoView.ts +30 -0
- package/src/NativeDocumentPreviewView.tsx +196 -0
- package/src/NativeMediaThumbnailView.tsx +167 -0
- package/src/NativeSvgIcon.tsx +46 -0
- package/src/ResponsivePreviewContainer.tsx +227 -0
- package/src/accessibility.ts +80 -0
- package/src/config.ts +406 -0
- package/src/cpuOptimization.ts +365 -0
- package/src/db/DatabaseManager.ts +212 -0
- package/src/db/MediaMapper.ts +377 -0
- package/src/db/MediaRepository.ts +313 -0
- package/src/db/index.ts +73 -0
- package/src/db/migrations.ts +222 -0
- package/src/db/react-native-sqlcipher.d.ts +76 -0
- package/src/db/secureKey.ts +100 -0
- package/src/db/types.ts +123 -0
- package/src/db/useMediaDatabase.ts +191 -0
- package/src/defaultIcons.tsx +2390 -0
- package/src/deviceCapabilities.ts +438 -0
- package/src/errors.ts +350 -0
- package/src/iconConfig.tsx +962 -0
- package/src/icons.tsx +788 -0
- package/src/index.tsx +763 -0
- package/src/jest-globals.d.ts +9 -0
- package/src/library/MediaLibrary.tsx +141 -0
- package/src/library/MediaLibraryScreen.tsx +3003 -0
- package/src/library/components/AudioPreview.tsx +73 -0
- package/src/library/components/DocumentPreview.tsx +361 -0
- package/src/library/components/ExpandableFab.tsx +342 -0
- package/src/library/components/FileTiles.tsx +252 -0
- package/src/library/components/ImagePreview.tsx +72 -0
- package/src/library/components/MediaMetadataHeader.tsx +208 -0
- package/src/library/components/RecordAudioScreen.tsx +845 -0
- package/src/library/components/VideoPreview.tsx +478 -0
- package/src/library/components/ViewerCounter.tsx +71 -0
- package/src/library/compressionEstimate.ts +662 -0
- package/src/library/fileMetadata.ts +212 -0
- package/src/library/index.ts +66 -0
- package/src/library/libraryIconRegistry.tsx +86 -0
- package/src/library/libraryIcons.tsx +928 -0
- package/src/library/localizedFormat.ts +207 -0
- package/src/library/palette.ts +281 -0
- package/src/library/responsive.ts +49 -0
- package/src/locales.ts +381 -0
- package/src/localization.ts +1947 -0
- package/src/logger.ts +20 -0
- package/src/lowMemoryCache.ts +218 -0
- package/src/mediaHistory.ts +42 -0
- package/src/mediaStorageService.ts +546 -0
- package/src/memory.ts +102 -0
- package/src/nativeActions.ts +422 -0
- package/src/nativeComponentSupport.ts +60 -0
- package/src/networkPerformance.ts +30 -0
- package/src/performance.ts +137 -0
- package/src/performanceMonitor.ts +219 -0
- package/src/permissions.ts +403 -0
- package/src/rendering.ts +47 -0
- package/src/security.ts +19 -0
- package/src/state.ts +333 -0
- package/src/storage.ts +453 -0
- package/src/theme.tsx +467 -0
- package/src/types.ts +946 -0
- package/src/utils.ts +177 -0
- package/src/validation.ts +372 -0
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
package com.customcamera
|
|
2
|
+
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.pm.PackageManager
|
|
6
|
+
import android.media.MediaRecorder
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import android.os.Build
|
|
9
|
+
import android.os.Handler
|
|
10
|
+
import android.os.HandlerThread
|
|
11
|
+
import android.os.Looper
|
|
12
|
+
import androidx.core.content.ContextCompat
|
|
13
|
+
import com.facebook.react.bridge.Arguments
|
|
14
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
15
|
+
import com.facebook.react.bridge.WritableMap
|
|
16
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
17
|
+
import java.io.File
|
|
18
|
+
import java.io.IOException
|
|
19
|
+
import java.util.concurrent.CopyOnWriteArrayList
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Singleton that manages audio recording using Android's native MediaRecorder API.
|
|
23
|
+
*
|
|
24
|
+
* No third-party libraries are used — only android.media.MediaRecorder, which is
|
|
25
|
+
* part of the Android SDK. Supports pause/resume (API 24+), amplitude metering
|
|
26
|
+
* for waveform visualisation, and emits JS events for real-time monitoring.
|
|
27
|
+
*
|
|
28
|
+
* Threading model:
|
|
29
|
+
* - All MediaRecorder operations run on the calling (React Native bridge) thread;
|
|
30
|
+
* MediaRecorder is not thread-safe so callers must not overlap invocations.
|
|
31
|
+
* - Metering runs on a dedicated background HandlerThread.
|
|
32
|
+
* - JS events fire on the main thread so React Native can consume them immediately.
|
|
33
|
+
*/
|
|
34
|
+
object AudioRecorder {
|
|
35
|
+
|
|
36
|
+
private const val TAG = "AudioRecorder"
|
|
37
|
+
private const val EVENT_AUDIO_RECORDER_STATE = "onAudioRecorderState"
|
|
38
|
+
private const val EVENT_AUDIO_AMPLITUDE = "onAudioAmplitude"
|
|
39
|
+
|
|
40
|
+
/** Current high-level state of the recorder. */
|
|
41
|
+
enum class State { IDLE, RECORDING, PAUSED, ERROR }
|
|
42
|
+
|
|
43
|
+
/** Output format presets that map to [MediaRecorder.OutputFormat] constants. */
|
|
44
|
+
enum class OutputFormat(val apiLevel: Int = 1) {
|
|
45
|
+
MPEG_4,
|
|
46
|
+
AAC_ADTS,
|
|
47
|
+
AMR_NB,
|
|
48
|
+
AMR_WB,
|
|
49
|
+
THREE_GPP;
|
|
50
|
+
|
|
51
|
+
internal fun toMediaRecorderFormat(): Int = when (this) {
|
|
52
|
+
MPEG_4 -> MediaRecorder.OutputFormat.MPEG_4
|
|
53
|
+
AAC_ADTS -> MediaRecorder.OutputFormat.AAC_ADTS
|
|
54
|
+
AMR_NB -> MediaRecorder.OutputFormat.AMR_NB
|
|
55
|
+
AMR_WB -> MediaRecorder.OutputFormat.AMR_WB
|
|
56
|
+
THREE_GPP -> MediaRecorder.OutputFormat.THREE_GPP
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Audio encoder presets that map to [MediaRecorder.AudioEncoder] constants. */
|
|
61
|
+
enum class AudioEncoder {
|
|
62
|
+
AAC, AMR_NB, AMR_WB, HE_AAC;
|
|
63
|
+
|
|
64
|
+
internal fun toMediaRecorderEncoder(): Int = when (this) {
|
|
65
|
+
AAC -> MediaRecorder.AudioEncoder.AAC
|
|
66
|
+
AMR_NB -> MediaRecorder.AudioEncoder.AMR_NB
|
|
67
|
+
AMR_WB -> MediaRecorder.AudioEncoder.AMR_WB
|
|
68
|
+
HE_AAC -> MediaRecorder.AudioEncoder.HE_AAC
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Audio source presets that map to [MediaRecorder.AudioSource] constants. */
|
|
73
|
+
enum class AudioSource {
|
|
74
|
+
MIC, CAMCORDER, VOICE_RECOGNITION, VOICE_COMMUNICATION;
|
|
75
|
+
|
|
76
|
+
internal fun toMediaRecorderSource(): Int = when (this) {
|
|
77
|
+
MIC -> MediaRecorder.AudioSource.MIC
|
|
78
|
+
CAMCORDER -> MediaRecorder.AudioSource.CAMCORDER
|
|
79
|
+
VOICE_RECOGNITION -> MediaRecorder.AudioSource.VOICE_RECOGNITION
|
|
80
|
+
VOICE_COMMUNICATION -> MediaRecorder.AudioSource.VOICE_COMMUNICATION
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Configuration passed from JS when starting a recording.
|
|
86
|
+
* Every field has a sensible default so JS callers can omit everything.
|
|
87
|
+
*/
|
|
88
|
+
data class Config(
|
|
89
|
+
val outputFormat: OutputFormat = OutputFormat.MPEG_4,
|
|
90
|
+
val audioEncoder: AudioEncoder = AudioEncoder.AAC,
|
|
91
|
+
val audioSource: AudioSource = AudioSource.MIC,
|
|
92
|
+
val bitRate: Int = 128000,
|
|
93
|
+
val sampleRate: Int = 44100,
|
|
94
|
+
val fileName: String? = null, // null → auto-generate
|
|
95
|
+
val meteringIntervalMs: Long = 0L, // 0 = no metering
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// ── State ──────────────────────────────────────────────────────────────────
|
|
99
|
+
|
|
100
|
+
@Volatile
|
|
101
|
+
private var state: State = State.IDLE
|
|
102
|
+
|
|
103
|
+
@Volatile
|
|
104
|
+
private var startTimestamp: Long = 0L
|
|
105
|
+
|
|
106
|
+
@Volatile
|
|
107
|
+
private var totalPausedDuration: Long = 0L
|
|
108
|
+
|
|
109
|
+
@Volatile
|
|
110
|
+
private var pauseTimestamp: Long = 0L
|
|
111
|
+
|
|
112
|
+
private var mediaRecorder: MediaRecorder? = null
|
|
113
|
+
private var outputFilePath: String = ""
|
|
114
|
+
private var activeConfig: Config = Config()
|
|
115
|
+
private var reactContext: ReactApplicationContext? = null
|
|
116
|
+
|
|
117
|
+
// Handler on the main looper for emitting JS events.
|
|
118
|
+
private val mainHandler = Handler(Looper.getMainLooper())
|
|
119
|
+
|
|
120
|
+
// Metering thread.
|
|
121
|
+
private var meteringThread: HandlerThread? = null
|
|
122
|
+
private var meteringHandler: Handler? = null
|
|
123
|
+
private var meteringEnabled = false
|
|
124
|
+
|
|
125
|
+
// ── Public API ─────────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
/** Returns the current state. Thread-safe because [state] is volatile. */
|
|
128
|
+
fun getState(): State = state
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* True when the recorder is either recording or paused — i.e. a recording
|
|
132
|
+
* session is active and stopRecording() should eventually be called.
|
|
133
|
+
*/
|
|
134
|
+
fun isActive(): Boolean = state == State.RECORDING || state == State.PAUSED
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Must be called once before any recording operation so the singleton can
|
|
138
|
+
* emit JS events. Safe to call multiple times (overwrites the reference).
|
|
139
|
+
*/
|
|
140
|
+
fun setReactContext(context: ReactApplicationContext) {
|
|
141
|
+
reactContext = context
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Start recording audio to [outputDir] (defaults to the app's cacheDir).
|
|
146
|
+
*
|
|
147
|
+
* @param context Android context (used to resolve dirs).
|
|
148
|
+
* @param config Recording parameters.
|
|
149
|
+
* @return A map containing `{ filePath, mimeType }` for the new recording.
|
|
150
|
+
* @throws IllegalStateException if already recording.
|
|
151
|
+
* @throws SecurityException if RECORD_AUDIO permission is not granted.
|
|
152
|
+
* @throws IOException if the output file cannot be created.
|
|
153
|
+
*/
|
|
154
|
+
@Throws(IllegalStateException::class, SecurityException::class, IOException::class)
|
|
155
|
+
fun startRecording(context: Context, config: Config = Config()): WritableMap {
|
|
156
|
+
if (state == State.RECORDING) {
|
|
157
|
+
throw IllegalStateException("Audio recorder is already recording")
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Check permission.
|
|
161
|
+
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
|
|
162
|
+
!= PackageManager.PERMISSION_GRANTED
|
|
163
|
+
) {
|
|
164
|
+
throw SecurityException("RECORD_AUDIO permission is not granted")
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Release any lingering recorder from a previous incomplete session.
|
|
168
|
+
releaseInternal()
|
|
169
|
+
|
|
170
|
+
activeConfig = config
|
|
171
|
+
|
|
172
|
+
// Determine output file path.
|
|
173
|
+
val dir = File(context.cacheDir, "audio_recordings").apply { mkdirs() }
|
|
174
|
+
val fileName = config.fileName
|
|
175
|
+
?: "recording_${System.currentTimeMillis()}.${extensionFor(config.outputFormat)}"
|
|
176
|
+
outputFilePath = File(dir, fileName).absolutePath
|
|
177
|
+
|
|
178
|
+
// Configure and start MediaRecorder.
|
|
179
|
+
val recorder = MediaRecorder()
|
|
180
|
+
mediaRecorder = recorder
|
|
181
|
+
|
|
182
|
+
recorder.setAudioSource(config.audioSource.toMediaRecorderSource())
|
|
183
|
+
recorder.setOutputFormat(config.outputFormat.toMediaRecorderFormat())
|
|
184
|
+
recorder.setAudioEncoder(config.audioEncoder.toMediaRecorderEncoder())
|
|
185
|
+
recorder.setAudioSamplingRate(config.sampleRate)
|
|
186
|
+
recorder.setAudioEncodingBitRate(config.bitRate)
|
|
187
|
+
recorder.setOutputFile(outputFilePath)
|
|
188
|
+
|
|
189
|
+
recorder.prepare()
|
|
190
|
+
recorder.start()
|
|
191
|
+
|
|
192
|
+
state = State.RECORDING
|
|
193
|
+
startTimestamp = System.currentTimeMillis()
|
|
194
|
+
totalPausedDuration = 0L
|
|
195
|
+
pauseTimestamp = 0L
|
|
196
|
+
|
|
197
|
+
// Start metering loop if configured.
|
|
198
|
+
if (config.meteringIntervalMs > 0L) {
|
|
199
|
+
startMetering(config.meteringIntervalMs)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
emitStateEvent()
|
|
203
|
+
Log.i(TAG, "Recording started → $outputFilePath")
|
|
204
|
+
|
|
205
|
+
return Arguments.createMap().apply {
|
|
206
|
+
putString("filePath", outputFilePath)
|
|
207
|
+
putString("mimeType", mimeTypeFor(config.outputFormat))
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Cancel the current recording and delete the temporary file.
|
|
213
|
+
*
|
|
214
|
+
* @return `true` if the temp file was deleted, `false` if no file existed.
|
|
215
|
+
* @throws IllegalStateException if no recording is active.
|
|
216
|
+
*/
|
|
217
|
+
@Throws(IllegalStateException::class)
|
|
218
|
+
fun cancelRecording(): Boolean {
|
|
219
|
+
if (!isActive()) {
|
|
220
|
+
throw IllegalStateException("No active recording to cancel")
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
val filePath = outputFilePath
|
|
224
|
+
|
|
225
|
+
stopMetering()
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
mediaRecorder?.apply {
|
|
229
|
+
try {
|
|
230
|
+
stop()
|
|
231
|
+
} catch (e: RuntimeException) {
|
|
232
|
+
Log.w(TAG, "MediaRecorder.stop() threw during cancel", e)
|
|
233
|
+
}
|
|
234
|
+
release()
|
|
235
|
+
}
|
|
236
|
+
mediaRecorder = null
|
|
237
|
+
|
|
238
|
+
// Delete the temporary file.
|
|
239
|
+
val deleted = if (filePath.isNotEmpty()) {
|
|
240
|
+
val f = File(filePath)
|
|
241
|
+
if (f.exists()) f.delete() else false
|
|
242
|
+
} else false
|
|
243
|
+
|
|
244
|
+
state = State.IDLE
|
|
245
|
+
activeConfig = Config()
|
|
246
|
+
startTimestamp = 0L
|
|
247
|
+
totalPausedDuration = 0L
|
|
248
|
+
pauseTimestamp = 0L
|
|
249
|
+
outputFilePath = ""
|
|
250
|
+
|
|
251
|
+
emitStateEvent()
|
|
252
|
+
Log.i(TAG, "Recording cancelled — temp file deleted=$deleted")
|
|
253
|
+
return deleted
|
|
254
|
+
} catch (e: Exception) {
|
|
255
|
+
state = State.ERROR
|
|
256
|
+
emitStateEvent()
|
|
257
|
+
Log.e(TAG, "Error cancelling recording", e)
|
|
258
|
+
throw e
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Stop the current recording session and finalise the audio file.
|
|
264
|
+
*
|
|
265
|
+
* @return A map containing the recorded file metadata:
|
|
266
|
+
* `{ filePath, durationMs, fileSizeBytes, mimeType }`.
|
|
267
|
+
* @throws IllegalStateException if no recording is active.
|
|
268
|
+
*/
|
|
269
|
+
@Throws(IllegalStateException::class)
|
|
270
|
+
fun stopRecording(): WritableMap {
|
|
271
|
+
if (!isActive()) {
|
|
272
|
+
throw IllegalStateException("No active recording to stop")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
val filePath = outputFilePath
|
|
276
|
+
val mimeType = mimeTypeFor(activeConfig.outputFormat)
|
|
277
|
+
|
|
278
|
+
stopMetering()
|
|
279
|
+
|
|
280
|
+
val durationMs: Long
|
|
281
|
+
val fileSizeBytes: Long
|
|
282
|
+
|
|
283
|
+
try {
|
|
284
|
+
// Stop & release the recorder.
|
|
285
|
+
mediaRecorder?.apply {
|
|
286
|
+
try {
|
|
287
|
+
stop() // May throw if called while paused on some OEMs; catch and continue.
|
|
288
|
+
} catch (e: RuntimeException) {
|
|
289
|
+
Log.w(TAG, "MediaRecorder.stop() threw — file may be incomplete", e)
|
|
290
|
+
}
|
|
291
|
+
release()
|
|
292
|
+
}
|
|
293
|
+
mediaRecorder = null
|
|
294
|
+
|
|
295
|
+
// Calculate effective duration, accounting for pauses.
|
|
296
|
+
val now = System.currentTimeMillis()
|
|
297
|
+
if (pauseTimestamp > 0L) {
|
|
298
|
+
totalPausedDuration += now - pauseTimestamp
|
|
299
|
+
}
|
|
300
|
+
durationMs = now - startTimestamp - totalPausedDuration
|
|
301
|
+
|
|
302
|
+
// Read final file size.
|
|
303
|
+
fileSizeBytes = File(filePath).let { if (it.exists()) it.length() else 0L }
|
|
304
|
+
} catch (e: Exception) {
|
|
305
|
+
state = State.ERROR
|
|
306
|
+
emitStateEvent()
|
|
307
|
+
Log.e(TAG, "Error stopping recording", e)
|
|
308
|
+
throw e
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
state = State.IDLE
|
|
312
|
+
emitStateEvent()
|
|
313
|
+
Log.i(TAG, "Recording stopped — duration=${durationMs}ms size=${fileSizeBytes}bytes")
|
|
314
|
+
|
|
315
|
+
return Arguments.createMap().apply {
|
|
316
|
+
putString("filePath", filePath)
|
|
317
|
+
putDouble("durationMs", durationMs.toDouble())
|
|
318
|
+
putDouble("fileSizeBytes", fileSizeBytes.toDouble())
|
|
319
|
+
putString("mimeType", mimeType)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Pause the current recording.
|
|
325
|
+
*
|
|
326
|
+
* Requires API 24+ (Build.VERSION_CODES.N). On older API levels this
|
|
327
|
+
* throws [UnsupportedOperationException].
|
|
328
|
+
*/
|
|
329
|
+
@Throws(IllegalStateException::class, UnsupportedOperationException::class)
|
|
330
|
+
fun pauseRecording() {
|
|
331
|
+
if (state != State.RECORDING) {
|
|
332
|
+
throw IllegalStateException("Cannot pause — recorder is not recording (state=$state)")
|
|
333
|
+
}
|
|
334
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
335
|
+
throw UnsupportedOperationException("Pause/resume requires API 24+")
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
mediaRecorder?.pause()
|
|
339
|
+
state = State.PAUSED
|
|
340
|
+
pauseTimestamp = System.currentTimeMillis()
|
|
341
|
+
stopMetering()
|
|
342
|
+
emitStateEvent()
|
|
343
|
+
Log.i(TAG, "Recording paused")
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Resume a paused recording.
|
|
348
|
+
*
|
|
349
|
+
* Requires API 24+. On older API levels this throws [UnsupportedOperationException].
|
|
350
|
+
*/
|
|
351
|
+
@Throws(IllegalStateException::class, UnsupportedOperationException::class)
|
|
352
|
+
fun resumeRecording() {
|
|
353
|
+
if (state != State.PAUSED) {
|
|
354
|
+
throw IllegalStateException("Cannot resume — recorder is not paused (state=$state)")
|
|
355
|
+
}
|
|
356
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
|
357
|
+
throw UnsupportedOperationException("Pause/resume requires API 24+")
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
mediaRecorder?.resume()
|
|
361
|
+
state = State.RECORDING
|
|
362
|
+
totalPausedDuration += System.currentTimeMillis() - pauseTimestamp
|
|
363
|
+
pauseTimestamp = 0L
|
|
364
|
+
|
|
365
|
+
if (activeConfig.meteringIntervalMs > 0L) {
|
|
366
|
+
startMetering(activeConfig.meteringIntervalMs)
|
|
367
|
+
}
|
|
368
|
+
emitStateEvent()
|
|
369
|
+
Log.i(TAG, "Recording resumed")
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get the current recording state as a WritableMap suitable for bridging to JS.
|
|
374
|
+
*/
|
|
375
|
+
fun getStateMap(): WritableMap = Arguments.createMap().apply {
|
|
376
|
+
putString("state", state.name.lowercase())
|
|
377
|
+
putString("filePath", outputFilePath)
|
|
378
|
+
putDouble("durationMs", computeElapsedMs().toDouble())
|
|
379
|
+
putBoolean("isPauseSupported", Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get the current maximum amplitude from MediaRecorder (for metering).
|
|
384
|
+
* Returns 0 if no recorder is active.
|
|
385
|
+
*/
|
|
386
|
+
fun getAmplitude(): Int {
|
|
387
|
+
return try {
|
|
388
|
+
mediaRecorder?.maxAmplitude ?: 0
|
|
389
|
+
} catch (e: Exception) {
|
|
390
|
+
0
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ── Metering ───────────────────────────────────────────────────────────────
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Start periodic amplitude sampling on a background thread.
|
|
398
|
+
* Emits [EVENT_AUDIO_AMPLITUDE] JS events every [intervalMs].
|
|
399
|
+
*/
|
|
400
|
+
private fun startMetering(intervalMs: Long) {
|
|
401
|
+
stopMetering()
|
|
402
|
+
meteringEnabled = true
|
|
403
|
+
|
|
404
|
+
meteringThread = HandlerThread("AudioMetering").apply { start() }
|
|
405
|
+
meteringHandler = Handler(meteringThread!!.looper)
|
|
406
|
+
|
|
407
|
+
meteringHandler?.post(object : Runnable {
|
|
408
|
+
override fun run() {
|
|
409
|
+
if (!meteringEnabled) return
|
|
410
|
+
val amp = getAmplitude()
|
|
411
|
+
emitAmplitudeEvent(amp)
|
|
412
|
+
meteringHandler?.postDelayed(this, intervalMs)
|
|
413
|
+
}
|
|
414
|
+
})
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/** Stop the metering loop and tear down its thread. */
|
|
418
|
+
private fun stopMetering() {
|
|
419
|
+
meteringEnabled = false
|
|
420
|
+
meteringHandler?.removeCallbacksAndMessages(null)
|
|
421
|
+
meteringHandler = null
|
|
422
|
+
meteringThread?.quitSafely()
|
|
423
|
+
meteringThread = null
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// ── Internal helpers ───────────────────────────────────────────────────────
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Compute the effective recording duration, excluding paused time.
|
|
430
|
+
*/
|
|
431
|
+
private fun computeElapsedMs(): Long {
|
|
432
|
+
if (startTimestamp == 0L) return 0L
|
|
433
|
+
val now = System.currentTimeMillis()
|
|
434
|
+
var paused = totalPausedDuration
|
|
435
|
+
if (pauseTimestamp > 0L) {
|
|
436
|
+
paused += now - pauseTimestamp
|
|
437
|
+
}
|
|
438
|
+
return now - startTimestamp - paused
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Release the current MediaRecorder without finalising the file.
|
|
443
|
+
* Called before starting a new recording or during cleanup.
|
|
444
|
+
*/
|
|
445
|
+
private fun releaseInternal() {
|
|
446
|
+
stopMetering()
|
|
447
|
+
try {
|
|
448
|
+
mediaRecorder?.apply {
|
|
449
|
+
try {
|
|
450
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
451
|
+
// On API 24+ we can safely stop. On older versions this may throw.
|
|
452
|
+
try { stop() } catch (_: RuntimeException) {}
|
|
453
|
+
}
|
|
454
|
+
} catch (_: Exception) {}
|
|
455
|
+
release()
|
|
456
|
+
}
|
|
457
|
+
} catch (_: Exception) {}
|
|
458
|
+
mediaRecorder = null
|
|
459
|
+
state = State.IDLE
|
|
460
|
+
startTimestamp = 0L
|
|
461
|
+
totalPausedDuration = 0L
|
|
462
|
+
pauseTimestamp = 0L
|
|
463
|
+
outputFilePath = ""
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/** Emit a state-change event to JS. */
|
|
467
|
+
private fun emitStateEvent() {
|
|
468
|
+
val ctx = reactContext ?: return
|
|
469
|
+
val map = getStateMap()
|
|
470
|
+
mainHandler.post {
|
|
471
|
+
try {
|
|
472
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
473
|
+
.emit(EVENT_AUDIO_RECORDER_STATE, map)
|
|
474
|
+
} catch (_: Exception) {}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Emit an amplitude metering event to JS. */
|
|
479
|
+
private fun emitAmplitudeEvent(amplitude: Int) {
|
|
480
|
+
val ctx = reactContext ?: return
|
|
481
|
+
mainHandler.post {
|
|
482
|
+
try {
|
|
483
|
+
ctx.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
484
|
+
.emit(EVENT_AUDIO_AMPLITUDE, amplitude)
|
|
485
|
+
} catch (_: Exception) {}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// ── MIME / extension helpers ───────────────────────────────────────────────
|
|
490
|
+
|
|
491
|
+
private fun mimeTypeFor(fmt: OutputFormat): String = when (fmt) {
|
|
492
|
+
OutputFormat.MPEG_4 -> "audio/mp4"
|
|
493
|
+
OutputFormat.AAC_ADTS -> "audio/aac"
|
|
494
|
+
OutputFormat.AMR_NB -> "audio/amr"
|
|
495
|
+
OutputFormat.AMR_WB -> "audio/amr-wb"
|
|
496
|
+
OutputFormat.THREE_GPP -> "audio/3gpp"
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private fun extensionFor(fmt: OutputFormat): String = when (fmt) {
|
|
500
|
+
OutputFormat.MPEG_4 -> "m4a"
|
|
501
|
+
OutputFormat.AAC_ADTS -> "aac"
|
|
502
|
+
OutputFormat.AMR_NB -> "amr"
|
|
503
|
+
OutputFormat.AMR_WB -> "amr"
|
|
504
|
+
OutputFormat.THREE_GPP -> "3gp"
|
|
505
|
+
}
|
|
506
|
+
}
|