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,386 @@
|
|
|
1
|
+
package com.customcamera
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Environment
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import kotlinx.coroutines.Dispatchers
|
|
7
|
+
import kotlinx.coroutines.withContext
|
|
8
|
+
import java.io.File
|
|
9
|
+
import java.util.concurrent.TimeUnit
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* CacheCleanupManager handles automatic and manual cleanup of cache files.
|
|
13
|
+
*
|
|
14
|
+
* Features:
|
|
15
|
+
* - Automatic cleanup of old files (configurable age)
|
|
16
|
+
* - Size-based cleanup (when cache exceeds max size)
|
|
17
|
+
* - Cleanup of orphaned files (files without metadata)
|
|
18
|
+
* - Cleanup of duplicate files
|
|
19
|
+
* - Retry mechanism for failed deletions
|
|
20
|
+
* - Background scheduling support
|
|
21
|
+
*/
|
|
22
|
+
class CacheCleanupManager private constructor(private val context: Context) {
|
|
23
|
+
|
|
24
|
+
companion object {
|
|
25
|
+
private const val TAG = "CacheCleanupManager"
|
|
26
|
+
|
|
27
|
+
// Default cleanup configuration
|
|
28
|
+
private const val DEFAULT_MAX_CACHE_SIZE_MB = 500L
|
|
29
|
+
private const val DEFAULT_CLEANUP_AGE_DAYS = 7
|
|
30
|
+
private const val DEFAULT_ORPHAN_FILE_AGE_HOURS = 24
|
|
31
|
+
|
|
32
|
+
@Volatile
|
|
33
|
+
private var instance: CacheCleanupManager? = null
|
|
34
|
+
|
|
35
|
+
fun getInstance(context: Context): CacheCleanupManager {
|
|
36
|
+
return instance ?: synchronized(this) {
|
|
37
|
+
instance ?: CacheCleanupManager(context.applicationContext).also { instance = it }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private val metadataStore = MediaMetadataStore.getInstance(context)
|
|
43
|
+
private var maxCacheSizeMB = DEFAULT_MAX_CACHE_SIZE_MB
|
|
44
|
+
private var cleanupAgeDays = DEFAULT_CLEANUP_AGE_DAYS
|
|
45
|
+
private var orphanFileAgeHours = DEFAULT_ORPHAN_FILE_AGE_HOURS
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Perform cleanup based on age
|
|
49
|
+
*/
|
|
50
|
+
suspend fun cleanupOldFiles(olderThanDays: Int = cleanupAgeDays): CleanupResult {
|
|
51
|
+
return withContext(Dispatchers.IO) {
|
|
52
|
+
try {
|
|
53
|
+
val cacheDir = getCacheDir()
|
|
54
|
+
if (!cacheDir.exists()) {
|
|
55
|
+
return@withContext CleanupResult(
|
|
56
|
+
success = true,
|
|
57
|
+
filesRemoved = 0,
|
|
58
|
+
spaceFreedMB = 0.0
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
val cutoffTime = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(olderThanDays.toLong())
|
|
63
|
+
var filesRemoved = 0
|
|
64
|
+
var spaceFreed = 0L
|
|
65
|
+
val erroredFiles = mutableListOf<ErroredFile>()
|
|
66
|
+
|
|
67
|
+
// Build a cachePath -> mediaId lookup ONCE up front instead of re-reading and
|
|
68
|
+
// re-scanning the entire metadata store for every deleted file (previously an
|
|
69
|
+
// O(files × metadata) pass that dominated cleanup on large caches). One O(metadata)
|
|
70
|
+
// build + O(1) lookups is equivalent in behaviour and far cheaper.
|
|
71
|
+
val pathToMediaId: Map<String, String> = metadataStore.getAllMedia()
|
|
72
|
+
.mapNotNull { m -> m.cachePath?.let { it to m.id } }
|
|
73
|
+
.toMap()
|
|
74
|
+
|
|
75
|
+
cacheDir.walkTopDown().forEach { file ->
|
|
76
|
+
if (file.isFile && file.lastModified() < cutoffTime) {
|
|
77
|
+
try {
|
|
78
|
+
spaceFreed += file.length()
|
|
79
|
+
if (file.delete()) {
|
|
80
|
+
filesRemoved++
|
|
81
|
+
// Remove from metadata by matching cachePath -> id
|
|
82
|
+
try {
|
|
83
|
+
pathToMediaId[file.absolutePath]?.let { metadataStore.removeMedia(it) }
|
|
84
|
+
} catch (e: Exception) {
|
|
85
|
+
Log.w(TAG, "Failed to remove metadata for deleted file: ${file.absolutePath}", e)
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
erroredFiles.add(ErroredFile(file.absolutePath, "Failed to delete"))
|
|
89
|
+
}
|
|
90
|
+
} catch (e: Exception) {
|
|
91
|
+
erroredFiles.add(ErroredFile(file.absolutePath, e.message ?: "Unknown error"))
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
Log.i(TAG, "Cleanup completed: $filesRemoved files removed, ${spaceFreed / (1024 * 1024)}MB freed")
|
|
97
|
+
|
|
98
|
+
CleanupResult(
|
|
99
|
+
success = true,
|
|
100
|
+
filesRemoved = filesRemoved,
|
|
101
|
+
spaceFreedMB = spaceFreed.toDouble() / (1024 * 1024),
|
|
102
|
+
erroredFiles = erroredFiles
|
|
103
|
+
)
|
|
104
|
+
} catch (e: Exception) {
|
|
105
|
+
Log.e(TAG, "Cleanup operation failed", e)
|
|
106
|
+
CleanupResult(
|
|
107
|
+
success = false,
|
|
108
|
+
filesRemoved = 0,
|
|
109
|
+
spaceFreedMB = 0.0,
|
|
110
|
+
erroredFiles = emptyList(),
|
|
111
|
+
error = e.message
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Cleanup when cache size exceeds maximum
|
|
119
|
+
*/
|
|
120
|
+
suspend fun cleanupBySize(maxSizeMB: Long = maxCacheSizeMB): CleanupResult {
|
|
121
|
+
return withContext(Dispatchers.IO) {
|
|
122
|
+
try {
|
|
123
|
+
val cacheDir = getCacheDir()
|
|
124
|
+
if (!cacheDir.exists()) {
|
|
125
|
+
return@withContext CleanupResult(
|
|
126
|
+
success = true,
|
|
127
|
+
filesRemoved = 0,
|
|
128
|
+
spaceFreedMB = 0.0
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
val currentSizeBytes = cacheDir.getTotalSize()
|
|
133
|
+
val maxSizeBytes = maxSizeMB * 1024 * 1024
|
|
134
|
+
|
|
135
|
+
if (currentSizeBytes <= maxSizeBytes) {
|
|
136
|
+
return@withContext CleanupResult(
|
|
137
|
+
success = true,
|
|
138
|
+
filesRemoved = 0,
|
|
139
|
+
spaceFreedMB = 0.0
|
|
140
|
+
)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Need to delete oldest files until size is below threshold
|
|
144
|
+
val targetSizeBytes = (maxSizeMB / 2) * 1024 * 1024 // Clean to 50% of max
|
|
145
|
+
val files = cacheDir.walkTopDown()
|
|
146
|
+
.filter { it.isFile }
|
|
147
|
+
.sortedBy { it.lastModified() } // Oldest first
|
|
148
|
+
.toMutableList()
|
|
149
|
+
|
|
150
|
+
var spaceFreed = 0L
|
|
151
|
+
var filesRemoved = 0
|
|
152
|
+
val erroredFiles = mutableListOf<ErroredFile>()
|
|
153
|
+
|
|
154
|
+
// One-time cachePath -> mediaId lookup (see cleanupOldFiles): avoids re-reading the
|
|
155
|
+
// whole metadata store for every file deleted in the size-eviction loop.
|
|
156
|
+
val pathToMediaId: Map<String, String> = metadataStore.getAllMedia()
|
|
157
|
+
.mapNotNull { m -> m.cachePath?.let { it to m.id } }
|
|
158
|
+
.toMap()
|
|
159
|
+
|
|
160
|
+
for (file in files) {
|
|
161
|
+
if (currentSizeBytes - spaceFreed <= targetSizeBytes) break
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
spaceFreed += file.length()
|
|
165
|
+
if (file.delete()) {
|
|
166
|
+
filesRemoved++
|
|
167
|
+
try {
|
|
168
|
+
pathToMediaId[file.absolutePath]?.let { metadataStore.removeMedia(it) }
|
|
169
|
+
} catch (e: Exception) {
|
|
170
|
+
Log.w(TAG, "Failed to remove metadata for deleted file: ${file.absolutePath}", e)
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
erroredFiles.add(ErroredFile(file.absolutePath, "Failed to delete"))
|
|
174
|
+
}
|
|
175
|
+
} catch (e: Exception) {
|
|
176
|
+
erroredFiles.add(ErroredFile(file.absolutePath, e.message ?: "Unknown error"))
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
Log.i(TAG, "Size-based cleanup completed: $filesRemoved files removed, ${spaceFreed / (1024 * 1024)}MB freed")
|
|
181
|
+
|
|
182
|
+
CleanupResult(
|
|
183
|
+
success = true,
|
|
184
|
+
filesRemoved = filesRemoved,
|
|
185
|
+
spaceFreedMB = spaceFreed.toDouble() / (1024 * 1024),
|
|
186
|
+
erroredFiles = erroredFiles
|
|
187
|
+
)
|
|
188
|
+
} catch (e: Exception) {
|
|
189
|
+
Log.e(TAG, "Size-based cleanup failed", e)
|
|
190
|
+
CleanupResult(
|
|
191
|
+
success = false,
|
|
192
|
+
filesRemoved = 0,
|
|
193
|
+
spaceFreedMB = 0.0,
|
|
194
|
+
error = e.message
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Cleanup orphaned files (files without metadata)
|
|
202
|
+
*/
|
|
203
|
+
suspend fun cleanupOrphanedFiles(olderThanHours: Int = orphanFileAgeHours): CleanupResult {
|
|
204
|
+
return withContext(Dispatchers.IO) {
|
|
205
|
+
try {
|
|
206
|
+
val cacheDir = getCacheDir()
|
|
207
|
+
if (!cacheDir.exists()) {
|
|
208
|
+
return@withContext CleanupResult(
|
|
209
|
+
success = true,
|
|
210
|
+
filesRemoved = 0,
|
|
211
|
+
spaceFreedMB = 0.0
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
val allMetadata = metadataStore.getAllMedia()
|
|
216
|
+
val metadataFilePaths = allMetadata.mapNotNull { it.cachePath }.toSet()
|
|
217
|
+
val cutoffTime = System.currentTimeMillis() - TimeUnit.HOURS.toMillis(olderThanHours.toLong())
|
|
218
|
+
|
|
219
|
+
var filesRemoved = 0
|
|
220
|
+
var spaceFreed = 0L
|
|
221
|
+
val erroredFiles = mutableListOf<ErroredFile>()
|
|
222
|
+
|
|
223
|
+
cacheDir.walkTopDown().forEach { file ->
|
|
224
|
+
if (file.isFile && file.absolutePath !in metadataFilePaths && file.lastModified() < cutoffTime) {
|
|
225
|
+
try {
|
|
226
|
+
spaceFreed += file.length()
|
|
227
|
+
if (file.delete()) {
|
|
228
|
+
filesRemoved++
|
|
229
|
+
} else {
|
|
230
|
+
erroredFiles.add(ErroredFile(file.absolutePath, "Failed to delete"))
|
|
231
|
+
}
|
|
232
|
+
} catch (e: Exception) {
|
|
233
|
+
erroredFiles.add(ErroredFile(file.absolutePath, e.message ?: "Unknown error"))
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
Log.i(TAG, "Orphaned file cleanup completed: $filesRemoved files removed")
|
|
239
|
+
|
|
240
|
+
CleanupResult(
|
|
241
|
+
success = true,
|
|
242
|
+
filesRemoved = filesRemoved,
|
|
243
|
+
spaceFreedMB = spaceFreed.toDouble() / (1024 * 1024),
|
|
244
|
+
erroredFiles = erroredFiles
|
|
245
|
+
)
|
|
246
|
+
} catch (e: Exception) {
|
|
247
|
+
Log.e(TAG, "Orphaned file cleanup failed", e)
|
|
248
|
+
CleanupResult(
|
|
249
|
+
success = false,
|
|
250
|
+
filesRemoved = 0,
|
|
251
|
+
spaceFreedMB = 0.0,
|
|
252
|
+
error = e.message
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Cleanup duplicate files (keep only one)
|
|
260
|
+
*/
|
|
261
|
+
suspend fun cleanupDuplicates(): CleanupResult {
|
|
262
|
+
return withContext(Dispatchers.IO) {
|
|
263
|
+
try {
|
|
264
|
+
val duplicates = metadataStore.getDuplicateMedia()
|
|
265
|
+
|
|
266
|
+
var filesRemoved = 0
|
|
267
|
+
var spaceFreed = 0L
|
|
268
|
+
val erroredFiles = mutableListOf<ErroredFile>()
|
|
269
|
+
|
|
270
|
+
// Group by hash
|
|
271
|
+
val hashGroups = duplicates.groupBy { it.fileHash }
|
|
272
|
+
|
|
273
|
+
hashGroups.forEach { (_, group) ->
|
|
274
|
+
if (group.size > 1) {
|
|
275
|
+
// Keep the first one, delete the rest
|
|
276
|
+
group.drop(1).forEach { duplicate ->
|
|
277
|
+
try {
|
|
278
|
+
val file = File(duplicate.cachePath ?: return@forEach)
|
|
279
|
+
if (file.exists()) {
|
|
280
|
+
spaceFreed += file.length()
|
|
281
|
+
if (file.delete()) {
|
|
282
|
+
filesRemoved++
|
|
283
|
+
metadataStore.removeMedia(duplicate.id)
|
|
284
|
+
} else {
|
|
285
|
+
erroredFiles.add(ErroredFile(file.absolutePath, "Failed to delete"))
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
} catch (e: Exception) {
|
|
289
|
+
erroredFiles.add(ErroredFile(duplicate.cachePath ?: "unknown", e.message ?: "Unknown error"))
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
Log.i(TAG, "Duplicate cleanup completed: $filesRemoved duplicate files removed")
|
|
296
|
+
|
|
297
|
+
CleanupResult(
|
|
298
|
+
success = true,
|
|
299
|
+
filesRemoved = filesRemoved,
|
|
300
|
+
spaceFreedMB = spaceFreed.toDouble() / (1024 * 1024),
|
|
301
|
+
erroredFiles = erroredFiles
|
|
302
|
+
)
|
|
303
|
+
} catch (e: Exception) {
|
|
304
|
+
Log.e(TAG, "Duplicate cleanup failed", e)
|
|
305
|
+
CleanupResult(
|
|
306
|
+
success = false,
|
|
307
|
+
filesRemoved = 0,
|
|
308
|
+
spaceFreedMB = 0.0,
|
|
309
|
+
error = e.message
|
|
310
|
+
)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Perform comprehensive cleanup
|
|
317
|
+
*/
|
|
318
|
+
suspend fun performCleanup(): CleanupResult {
|
|
319
|
+
return withContext(Dispatchers.IO) {
|
|
320
|
+
try {
|
|
321
|
+
val results = mutableListOf<CleanupResult>()
|
|
322
|
+
|
|
323
|
+
// Run cleanups in sequence
|
|
324
|
+
results.add(cleanupOrphanedFiles())
|
|
325
|
+
results.add(cleanupOldFiles())
|
|
326
|
+
results.add(cleanupDuplicates())
|
|
327
|
+
|
|
328
|
+
val totalRemoved = results.sumOf { it.filesRemoved }
|
|
329
|
+
val totalSpaceFreed = results.sumOf { it.spaceFreedMB }
|
|
330
|
+
val allErrors = results.flatMap { it.erroredFiles }
|
|
331
|
+
|
|
332
|
+
CleanupResult(
|
|
333
|
+
success = true,
|
|
334
|
+
filesRemoved = totalRemoved,
|
|
335
|
+
spaceFreedMB = totalSpaceFreed,
|
|
336
|
+
erroredFiles = allErrors
|
|
337
|
+
)
|
|
338
|
+
} catch (e: Exception) {
|
|
339
|
+
Log.e(TAG, "Comprehensive cleanup failed", e)
|
|
340
|
+
CleanupResult(
|
|
341
|
+
success = false,
|
|
342
|
+
filesRemoved = 0,
|
|
343
|
+
spaceFreedMB = 0.0,
|
|
344
|
+
error = e.message
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* Configure cleanup parameters
|
|
352
|
+
*/
|
|
353
|
+
fun configure(maxCacheSizeMB: Long, cleanupAgeDays: Int, orphanFileAgeHours: Int) {
|
|
354
|
+
this.maxCacheSizeMB = maxCacheSizeMB
|
|
355
|
+
this.cleanupAgeDays = cleanupAgeDays
|
|
356
|
+
this.orphanFileAgeHours = orphanFileAgeHours
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Get cache directory
|
|
361
|
+
*/
|
|
362
|
+
private fun getCacheDir(): File {
|
|
363
|
+
return File(context.cacheDir, "media_cache").apply { mkdirs() }
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
data class CleanupResult(
|
|
367
|
+
val success: Boolean,
|
|
368
|
+
val filesRemoved: Int,
|
|
369
|
+
val spaceFreedMB: Double,
|
|
370
|
+
val erroredFiles: List<ErroredFile> = emptyList(),
|
|
371
|
+
val error: String? = null
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
data class ErroredFile(
|
|
375
|
+
val path: String,
|
|
376
|
+
val error: String
|
|
377
|
+
)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private fun File.getTotalSize(): Long {
|
|
381
|
+
return if (isDirectory) {
|
|
382
|
+
listFiles()?.sumOf { it.getTotalSize() } ?: 0L
|
|
383
|
+
} else {
|
|
384
|
+
length()
|
|
385
|
+
}
|
|
386
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
package com.customcamera
|
|
2
|
+
|
|
3
|
+
object CameraAspectRatioMode {
|
|
4
|
+
const val ORIGINAL_LABEL = "Original"
|
|
5
|
+
const val FULL_LABEL = "Full"
|
|
6
|
+
|
|
7
|
+
data class Mode(val label: String, val ratio: Float)
|
|
8
|
+
|
|
9
|
+
private const val DEFAULT_RATIO = 720f / 1165f
|
|
10
|
+
|
|
11
|
+
fun availableLabels(): List<String> = listOf(ORIGINAL_LABEL, FULL_LABEL, "4:3", "16:9", "1:1", "9:16", "4:5")
|
|
12
|
+
|
|
13
|
+
fun fromLabel(label: String?, fallbackRatio: Float = DEFAULT_RATIO): Mode? = when (label) {
|
|
14
|
+
ORIGINAL_LABEL -> Mode(ORIGINAL_LABEL, fallbackRatio)
|
|
15
|
+
FULL_LABEL -> Mode(FULL_LABEL, fallbackRatio)
|
|
16
|
+
"4:3" -> Mode("4:3", 4f / 3f)
|
|
17
|
+
"16:9" -> Mode("16:9", 16f / 9f)
|
|
18
|
+
"1:1" -> Mode("1:1", 1f)
|
|
19
|
+
"9:16" -> Mode("9:16", 9f / 16f)
|
|
20
|
+
"4:5" -> Mode("4:5", 4f / 5f)
|
|
21
|
+
else -> null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
fun resolveRatio(label: String?, screenAspectRatio: Float, fallbackRatio: Float = DEFAULT_RATIO): Float =
|
|
25
|
+
fromLabel(label, fallbackRatio)?.let { mode ->
|
|
26
|
+
if (mode.label == FULL_LABEL) screenAspectRatio else mode.ratio
|
|
27
|
+
} ?: fallbackRatio
|
|
28
|
+
|
|
29
|
+
private const val RATIO_16_9 = 16f / 9f
|
|
30
|
+
|
|
31
|
+
// Tolerance for classifying a preview-container ratio (derived from integer pixel dimensions,
|
|
32
|
+
// so it drifts a hair from the exact fraction) against a named aspect. Far tighter than the
|
|
33
|
+
// gap between any two selectable ratios (4:3 ≈ 1.333 vs 16:9 ≈ 1.778), so it never confuses
|
|
34
|
+
// one for another.
|
|
35
|
+
private const val LANDSCAPE_RATIO_MATCH_EPSILON = 0.02f
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether a LANDSCAPE capture of [ratio] (the portrait-locked viewfinder box's width / height)
|
|
39
|
+
* must have BOTH its capture crop and its editor framing INVERTED to the box's inverse aspect,
|
|
40
|
+
* so the saved image/video reproduces EXACTLY the region the viewfinder framed — true WYSIWYG,
|
|
41
|
+
* with no camera content revealed outside the capture frame.
|
|
42
|
+
*
|
|
43
|
+
* A box held sideways frames its INVERSE aspect (its on-screen width axis points vertically in
|
|
44
|
+
* the world), so:
|
|
45
|
+
* - every ratio <= 1 (9:16, 4:5, 1:1, Original, Full-on-portrait) inverts, and
|
|
46
|
+
* - 16:9 inverts too: a wide "16:9" box held sideways frames a TALL upright region, so keeping
|
|
47
|
+
* it wide pulled in extra left/right scene the viewfinder never showed.
|
|
48
|
+
* 4:3 is the sole remaining exception — it is deliberately KEPT WIDE for a landscape capture
|
|
49
|
+
* (fills the editor width), an accepted tradeoff. Portrait captures never invert (callers gate
|
|
50
|
+
* on their own rotatedToPortrait flag).
|
|
51
|
+
*
|
|
52
|
+
* This is the single source of truth shared by the capture crop (CustomCameraActivity's photo
|
|
53
|
+
* normalize + video crop-transcode) and the editor framing
|
|
54
|
+
* (MediaPreviewEditor.shouldInvertForLandscape), so the saved file and the editor box stay
|
|
55
|
+
* pixel-for-pixel consistent.
|
|
56
|
+
*/
|
|
57
|
+
fun invertsForLandscape(ratio: Float): Boolean =
|
|
58
|
+
ratio > 0f && (ratio <= 1f || Math.abs(ratio - RATIO_16_9) <= LANDSCAPE_RATIO_MATCH_EPSILON)
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* User-facing full-word label for a popup-menu row. The [ORIGINAL_LABEL] / [FULL_LABEL]
|
|
62
|
+
* constants stay as stable internal identifiers (used for matching); this maps them to
|
|
63
|
+
* their localized display text. Ratio notation ("4:3", "16:9"…) is universal and passes
|
|
64
|
+
* through unchanged.
|
|
65
|
+
*/
|
|
66
|
+
fun displayLabel(label: String?): String = when (label) {
|
|
67
|
+
ORIGINAL_LABEL, null -> Localization.getString("aspectRatioOriginal", "Original")
|
|
68
|
+
FULL_LABEL -> Localization.getString("aspectRatioFull", "Full")
|
|
69
|
+
else -> label
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fun buttonLabel(label: String?): String = when (label) {
|
|
73
|
+
ORIGINAL_LABEL, null -> Localization.getString("aspectRatioOriginalShort", "Original")
|
|
74
|
+
FULL_LABEL -> Localization.getString("aspectRatioFull", "Full")
|
|
75
|
+
else -> label
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
package com.customcamera
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import android.os.Bundle
|
|
7
|
+
import java.io.File
|
|
8
|
+
|
|
9
|
+
data class CameraAsset(
|
|
10
|
+
val id: String,
|
|
11
|
+
val uri: Uri,
|
|
12
|
+
val type: String,
|
|
13
|
+
val fileName: String?,
|
|
14
|
+
val mimeType: String?,
|
|
15
|
+
val width: Int?,
|
|
16
|
+
val height: Int?,
|
|
17
|
+
val duration: Long?,
|
|
18
|
+
val size: Long?,
|
|
19
|
+
val dateTaken: Long?,
|
|
20
|
+
val caption: String? = null,
|
|
21
|
+
val cachePath: String? = null,
|
|
22
|
+
val isCached: Boolean = false,
|
|
23
|
+
val cacheCreatedAt: Long? = null,
|
|
24
|
+
val cacheLastModified: Long? = null,
|
|
25
|
+
val originalUri: String? = null,
|
|
26
|
+
val documentUri: String? = null,
|
|
27
|
+
/**
|
|
28
|
+
* The aspect ratio (width / height) of the live camera preview at the time of capture.
|
|
29
|
+
* Used to render the captured media with identical framing as the viewfinder — true WYSIWYG.
|
|
30
|
+
* Stored as a Float (e.g. 1.33f for 4:3, 1.78f for 16:9) or null if unknown.
|
|
31
|
+
*/
|
|
32
|
+
val previewAspectRatio: Float? = null,
|
|
33
|
+
/**
|
|
34
|
+
* True when this photo was captured in landscape orientation. This helps distinguish
|
|
35
|
+
* between images natively captured in portrait (9:16 aspect ratio) versus landscape
|
|
36
|
+
* (captured while the device was held horizontally). Both are now cropped to the selected
|
|
37
|
+
* aspect ratio and stored with previewAspectRatio set, ensuring WYSIWYG behavior where
|
|
38
|
+
* the editor preview exactly matches the capture framing. This flag is provided for
|
|
39
|
+
* editor logic that may need to handle landscape and portrait captures differently.
|
|
40
|
+
*/
|
|
41
|
+
val rotatedToPortrait: Boolean = false,
|
|
42
|
+
/**
|
|
43
|
+
* For videos: a content:// (or file://) URI of the user-selected cover frame, extracted as a
|
|
44
|
+
* JPEG in the editor. Purely a poster/thumbnail — the video file itself is unchanged. Surfaced
|
|
45
|
+
* to JS as `coverUri` and preferred by [ThumbnailLoader] for the in-app strip.
|
|
46
|
+
*/
|
|
47
|
+
val coverUri: String? = null,
|
|
48
|
+
|
|
49
|
+
/** The exact container width (in pixels) used for the camera preview at capture time. */
|
|
50
|
+
val captureContainerWidth: Int? = null,
|
|
51
|
+
|
|
52
|
+
/** The exact container height (in pixels) used for the camera preview at capture time. */
|
|
53
|
+
val captureContainerHeight: Int? = null,
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* True when [size] is a DISPLAY convention rather than the exact bytes on disk. Set for a
|
|
57
|
+
* compressed video published from the editor: the file the encoder writes copies its audio and
|
|
58
|
+
* container through and so sits above the proportional figure the user picked, but the preview /
|
|
59
|
+
* Info screen must quote the SAME proportional size the compression dropdown and the completion
|
|
60
|
+
* toast quote (`original × (1 - percent/100)`). This flag tells JS ([refreshAssetMetadata]) to
|
|
61
|
+
* keep [size] as-is for that clip instead of re-statting the file and overwriting the convention.
|
|
62
|
+
* Everything without a compressed re-encode (originals, muted/trimmed clips, stills) leaves this
|
|
63
|
+
* false, so those still re-stat normally.
|
|
64
|
+
*/
|
|
65
|
+
val sizeIsDisplayEstimate: Boolean = false
|
|
66
|
+
) {
|
|
67
|
+
fun toBundle(): Bundle =
|
|
68
|
+
Bundle().apply {
|
|
69
|
+
putString("id", id)
|
|
70
|
+
putString("uri", uri.toString())
|
|
71
|
+
putString("type", type)
|
|
72
|
+
fileName?.let { putString("fileName", it) }
|
|
73
|
+
mimeType?.let { putString("mimeType", it) }
|
|
74
|
+
width?.let { putInt("width", it) }
|
|
75
|
+
height?.let { putInt("height", it) }
|
|
76
|
+
duration?.let { putDouble("duration", it.toDouble()) }
|
|
77
|
+
size?.let { putDouble("size", it.toDouble()) }
|
|
78
|
+
dateTaken?.let { putDouble("dateTaken", it.toDouble()) }
|
|
79
|
+
caption?.takeIf { it.isNotBlank() }?.let { putString("caption", it) }
|
|
80
|
+
cachePath?.let { putString("cachePath", it) }
|
|
81
|
+
putBoolean("isCached", isCached)
|
|
82
|
+
cacheCreatedAt?.let { putDouble("cacheCreatedAt", it.toDouble()) }
|
|
83
|
+
cacheLastModified?.let { putDouble("cacheLastModified", it.toDouble()) }
|
|
84
|
+
originalUri?.let { putString("originalUri", it) }
|
|
85
|
+
documentUri?.let { putString("documentUri", it) }
|
|
86
|
+
previewAspectRatio?.let { putFloat("previewAspectRatio", it) }
|
|
87
|
+
putBoolean("rotatedToPortrait", rotatedToPortrait)
|
|
88
|
+
coverUri?.let { putString("coverUri", it) }
|
|
89
|
+
captureContainerWidth?.let { putInt("captureContainerWidth", it) }
|
|
90
|
+
captureContainerHeight?.let { putInt("captureContainerHeight", it) }
|
|
91
|
+
putBoolean("sizeIsDisplayEstimate", sizeIsDisplayEstimate)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
companion object {
|
|
95
|
+
fun fromBundle(bundle: Bundle): CameraAsset {
|
|
96
|
+
return CameraAsset(
|
|
97
|
+
id = bundle.getString("id") ?: "",
|
|
98
|
+
uri = Uri.parse(bundle.getString("uri")),
|
|
99
|
+
type = bundle.getString("type") ?: "photo",
|
|
100
|
+
fileName = bundle.getString("fileName"),
|
|
101
|
+
mimeType = bundle.getString("mimeType"),
|
|
102
|
+
width = if (bundle.containsKey("width")) bundle.getInt("width") else null,
|
|
103
|
+
height = if (bundle.containsKey("height")) bundle.getInt("height") else null,
|
|
104
|
+
duration = if (bundle.containsKey("duration")) bundle.getLong("duration") else null,
|
|
105
|
+
size = if (bundle.containsKey("size")) bundle.getLong("size") else null,
|
|
106
|
+
dateTaken = if (bundle.containsKey("dateTaken")) bundle.getLong("dateTaken") else null,
|
|
107
|
+
caption = bundle.getString("caption"),
|
|
108
|
+
cachePath = bundle.getString("cachePath"),
|
|
109
|
+
isCached = bundle.getBoolean("isCached", false),
|
|
110
|
+
cacheCreatedAt = if (bundle.containsKey("cacheCreatedAt")) bundle.getLong("cacheCreatedAt") else null,
|
|
111
|
+
cacheLastModified = if (bundle.containsKey("cacheLastModified")) bundle.getLong("cacheLastModified") else null,
|
|
112
|
+
originalUri = bundle.getString("originalUri"),
|
|
113
|
+
documentUri = bundle.getString("documentUri"),
|
|
114
|
+
previewAspectRatio = if (bundle.containsKey("previewAspectRatio")) bundle.getFloat("previewAspectRatio") else null,
|
|
115
|
+
rotatedToPortrait = bundle.getBoolean("rotatedToPortrait", false),
|
|
116
|
+
coverUri = bundle.getString("coverUri"),
|
|
117
|
+
captureContainerWidth = if (bundle.containsKey("captureContainerWidth")) bundle.getInt("captureContainerWidth") else null,
|
|
118
|
+
captureContainerHeight = if (bundle.containsKey("captureContainerHeight")) bundle.getInt("captureContainerHeight") else null,
|
|
119
|
+
sizeIsDisplayEstimate = bundle.getBoolean("sizeIsDisplayEstimate", false)
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
fun fromCacheMetadata(
|
|
124
|
+
metadata: MediaCacheManager.MediaCacheMetadata,
|
|
125
|
+
publishedUri: Uri? = null,
|
|
126
|
+
context: Context? = null
|
|
127
|
+
): CameraAsset {
|
|
128
|
+
val uri = publishedUri ?: if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
129
|
+
try {
|
|
130
|
+
androidx.core.content.FileProvider.getUriForFile(
|
|
131
|
+
context,
|
|
132
|
+
"${context.packageName}.camera.fileprovider",
|
|
133
|
+
File(metadata.cachePath)
|
|
134
|
+
)
|
|
135
|
+
} catch (e: Exception) {
|
|
136
|
+
Uri.fromFile(File(metadata.cachePath))
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
Uri.fromFile(File(metadata.cachePath))
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return CameraAsset(
|
|
143
|
+
id = metadata.id,
|
|
144
|
+
uri = uri,
|
|
145
|
+
type = metadata.fileType,
|
|
146
|
+
fileName = metadata.fileName,
|
|
147
|
+
mimeType = metadata.mimeType,
|
|
148
|
+
width = metadata.width,
|
|
149
|
+
height = metadata.height,
|
|
150
|
+
duration = metadata.duration,
|
|
151
|
+
size = metadata.fileSize,
|
|
152
|
+
dateTaken = metadata.createdAt,
|
|
153
|
+
cachePath = metadata.cachePath,
|
|
154
|
+
isCached = true,
|
|
155
|
+
cacheCreatedAt = metadata.createdAt,
|
|
156
|
+
cacheLastModified = metadata.lastModified,
|
|
157
|
+
originalUri = metadata.originalUri,
|
|
158
|
+
documentUri = publishedUri?.toString(),
|
|
159
|
+
previewAspectRatio = null
|
|
160
|
+
)
|
|
161
|
+
}
|
|
162
|
+
fun fromCacheMediaResult(
|
|
163
|
+
result: MediaStorageWorkflow.CacheMediaResult,
|
|
164
|
+
fileType: String,
|
|
165
|
+
publishedUri: Uri? = null,
|
|
166
|
+
originalUri: String? = null,
|
|
167
|
+
context: Context? = null,
|
|
168
|
+
previewAspectRatio: Float? = null,
|
|
169
|
+
rotatedToPortrait: Boolean = false,
|
|
170
|
+
captureContainerWidth: Int? = null,
|
|
171
|
+
captureContainerHeight: Int? = null
|
|
172
|
+
): CameraAsset {
|
|
173
|
+
val uri = publishedUri ?: if (context != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
174
|
+
try {
|
|
175
|
+
androidx.core.content.FileProvider.getUriForFile(
|
|
176
|
+
context,
|
|
177
|
+
"${context.packageName}.camera.fileprovider",
|
|
178
|
+
File(result.cachePath)
|
|
179
|
+
)
|
|
180
|
+
} catch (e: Exception) {
|
|
181
|
+
Uri.fromFile(File(result.cachePath))
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
Uri.fromFile(File(result.cachePath))
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return CameraAsset(
|
|
188
|
+
id = result.mediaId,
|
|
189
|
+
uri = uri,
|
|
190
|
+
type = fileType,
|
|
191
|
+
fileName = result.fileName,
|
|
192
|
+
mimeType = result.mimeType,
|
|
193
|
+
width = result.width,
|
|
194
|
+
height = result.height,
|
|
195
|
+
duration = result.duration,
|
|
196
|
+
size = result.fileSize,
|
|
197
|
+
dateTaken = System.currentTimeMillis(),
|
|
198
|
+
cachePath = result.cachePath,
|
|
199
|
+
isCached = true,
|
|
200
|
+
cacheCreatedAt = System.currentTimeMillis(),
|
|
201
|
+
cacheLastModified = System.currentTimeMillis(),
|
|
202
|
+
originalUri = originalUri,
|
|
203
|
+
documentUri = publishedUri?.toString(),
|
|
204
|
+
previewAspectRatio = previewAspectRatio,
|
|
205
|
+
rotatedToPortrait = rotatedToPortrait,
|
|
206
|
+
captureContainerWidth = captureContainerWidth,
|
|
207
|
+
captureContainerHeight = captureContainerHeight
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|