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,378 @@
|
|
|
1
|
+
package com.customcamera
|
|
2
|
+
|
|
3
|
+
import android.content.ContentResolver
|
|
4
|
+
import android.content.ContentUris
|
|
5
|
+
import android.database.ContentObserver
|
|
6
|
+
import android.net.Uri
|
|
7
|
+
import android.os.Build
|
|
8
|
+
import android.os.Bundle
|
|
9
|
+
import android.os.Handler
|
|
10
|
+
import android.os.Looper
|
|
11
|
+
import android.provider.MediaStore
|
|
12
|
+
|
|
13
|
+
class MediaStoreRepository(
|
|
14
|
+
private val contentResolver: ContentResolver
|
|
15
|
+
) {
|
|
16
|
+
private val mediaUri: Uri = MediaStore.Files.getContentUri("external")
|
|
17
|
+
|
|
18
|
+
fun queryRecent(
|
|
19
|
+
mediaTypes: String,
|
|
20
|
+
limit: Int,
|
|
21
|
+
offset: Int
|
|
22
|
+
): List<CameraAsset> {
|
|
23
|
+
val selection = buildSelection(mediaTypes)
|
|
24
|
+
val projection = arrayOf(
|
|
25
|
+
MediaStore.Files.FileColumns._ID,
|
|
26
|
+
MediaStore.Files.FileColumns.MEDIA_TYPE,
|
|
27
|
+
MediaStore.MediaColumns.DISPLAY_NAME,
|
|
28
|
+
MediaStore.MediaColumns.MIME_TYPE,
|
|
29
|
+
MediaStore.MediaColumns.WIDTH,
|
|
30
|
+
MediaStore.MediaColumns.HEIGHT,
|
|
31
|
+
MediaStore.MediaColumns.SIZE,
|
|
32
|
+
MediaStore.MediaColumns.DATE_TAKEN,
|
|
33
|
+
MediaStore.MediaColumns.DATE_MODIFIED,
|
|
34
|
+
MediaStore.Video.VideoColumns.DURATION
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
// MediaProvider validates sort-order SQL, and how strictly it does so varies by OEM and API
|
|
38
|
+
// level. If the COALESCE expression is ever rejected, fall back to the plain column sort
|
|
39
|
+
// rather than letting the whole media list fail to load — a degraded ordering is recoverable,
|
|
40
|
+
// an empty gallery is not.
|
|
41
|
+
val cursor = runQuery(projection, selection, SORT_ORDER, limit, offset)
|
|
42
|
+
?: runQuery(projection, selection, FALLBACK_SORT_ORDER, limit, offset)
|
|
43
|
+
|
|
44
|
+
cursor?.use {
|
|
45
|
+
val idColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID)
|
|
46
|
+
val mediaTypeColumn = it.getColumnIndexOrThrow(MediaStore.Files.FileColumns.MEDIA_TYPE)
|
|
47
|
+
val displayNameColumn = it.getColumnIndexOrThrow(MediaStore.MediaColumns.DISPLAY_NAME)
|
|
48
|
+
val mimeTypeColumn = it.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE)
|
|
49
|
+
val widthColumn = it.getColumnIndex(MediaStore.MediaColumns.WIDTH)
|
|
50
|
+
val heightColumn = it.getColumnIndex(MediaStore.MediaColumns.HEIGHT)
|
|
51
|
+
val sizeColumn = it.getColumnIndex(MediaStore.MediaColumns.SIZE)
|
|
52
|
+
val dateTakenColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_TAKEN)
|
|
53
|
+
val dateModifiedColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)
|
|
54
|
+
val durationColumn = it.getColumnIndex(MediaStore.Video.VideoColumns.DURATION)
|
|
55
|
+
val assets = mutableListOf<CameraAsset>()
|
|
56
|
+
|
|
57
|
+
while (it.moveToNext()) {
|
|
58
|
+
val id = it.getLong(idColumn)
|
|
59
|
+
val mediaType = it.getInt(mediaTypeColumn)
|
|
60
|
+
val mime = it.getNullableString(mimeTypeColumn)
|
|
61
|
+
// Map the MediaStore MEDIA_TYPE (and, for documents, the MIME type) onto the
|
|
62
|
+
// app-wide asset type vocabulary: "photo", "video", "audio", "document".
|
|
63
|
+
val type = when (mediaType) {
|
|
64
|
+
MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO -> "video"
|
|
65
|
+
MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO -> "audio"
|
|
66
|
+
MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE -> "photo"
|
|
67
|
+
// Anything else is decided by the dedicated document media type (value 6,
|
|
68
|
+
// API 30+) and the MIME family — never blindly defaulted to "photo", which
|
|
69
|
+
// is what made documents surface under the Photos filter. A non-image/
|
|
70
|
+
// video/audio file with no better signal is treated as a document.
|
|
71
|
+
else -> when {
|
|
72
|
+
mediaType == MEDIA_TYPE_DOCUMENT -> "document"
|
|
73
|
+
isDocumentMime(mime) -> "document"
|
|
74
|
+
mime != null && mime.startsWith("audio/") -> "audio"
|
|
75
|
+
mime != null && mime.startsWith("video/") -> "video"
|
|
76
|
+
ImageFormatSupport.isImageMimeType(mime) -> "photo"
|
|
77
|
+
// No usable MIME type: fall back to the file extension before giving up, which is
|
|
78
|
+
// what rescues rows scanned without a type at all (common for files another app
|
|
79
|
+
// wrote into its own Android/media directory).
|
|
80
|
+
ImageFormatSupport.fromFileName(it.getNullableString(displayNameColumn)) != null ->
|
|
81
|
+
"photo"
|
|
82
|
+
else -> "document"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Build a type-appropriate, openable content URI. The typed collections (Images/Video/
|
|
86
|
+
// Audio) are used only when MediaStore itself filed the row under that MEDIA_TYPE — an
|
|
87
|
+
// id from the Files collection is not guaranteed to resolve inside a typed collection,
|
|
88
|
+
// so a row recognised as an image purely by its MIME type keeps the generic Files URI
|
|
89
|
+
// it is known to be addressable by. Documents likewise resolve through Files.
|
|
90
|
+
val contentUri = when {
|
|
91
|
+
mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO ->
|
|
92
|
+
ContentUris.withAppendedId(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, id)
|
|
93
|
+
mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO ->
|
|
94
|
+
ContentUris.withAppendedId(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id)
|
|
95
|
+
mediaType == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE ->
|
|
96
|
+
ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
|
|
97
|
+
else -> ContentUris.withAppendedId(mediaUri, id)
|
|
98
|
+
}
|
|
99
|
+
val dateTaken = it.getNullableLong(dateTakenColumn)
|
|
100
|
+
?: (it.getNullableLong(dateModifiedColumn)?.times(1000))
|
|
101
|
+
|
|
102
|
+
assets.add(
|
|
103
|
+
CameraAsset(
|
|
104
|
+
id = id.toString(),
|
|
105
|
+
uri = contentUri,
|
|
106
|
+
type = type,
|
|
107
|
+
fileName = it.getNullableString(displayNameColumn),
|
|
108
|
+
mimeType = mime,
|
|
109
|
+
width = it.getNullableInt(widthColumn),
|
|
110
|
+
height = it.getNullableInt(heightColumn),
|
|
111
|
+
duration = it.getNullableLong(durationColumn),
|
|
112
|
+
size = it.getNullableLong(sizeColumn),
|
|
113
|
+
dateTaken = dateTaken
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return assets
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return emptyList()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Run the paged media query with [sortOrder], returning null if the provider rejects it so the
|
|
126
|
+
* caller can retry with a simpler ordering. Only a *rejected query* yields null — a query that
|
|
127
|
+
* legitimately matches nothing still returns an empty cursor, so an empty gallery never
|
|
128
|
+
* triggers a pointless second round trip.
|
|
129
|
+
*/
|
|
130
|
+
private fun runQuery(
|
|
131
|
+
projection: Array<String>,
|
|
132
|
+
selection: String,
|
|
133
|
+
sortOrder: String,
|
|
134
|
+
limit: Int,
|
|
135
|
+
offset: Int
|
|
136
|
+
): android.database.Cursor? =
|
|
137
|
+
runCatching {
|
|
138
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
139
|
+
contentResolver.query(
|
|
140
|
+
mediaUri,
|
|
141
|
+
projection,
|
|
142
|
+
Bundle().apply {
|
|
143
|
+
putString(ContentResolver.QUERY_ARG_SQL_SELECTION, selection)
|
|
144
|
+
putStringArray(ContentResolver.QUERY_ARG_SQL_SELECTION_ARGS, emptyArray<String>())
|
|
145
|
+
putString(ContentResolver.QUERY_ARG_SQL_SORT_ORDER, sortOrder)
|
|
146
|
+
putInt(ContentResolver.QUERY_ARG_LIMIT, limit)
|
|
147
|
+
putInt(ContentResolver.QUERY_ARG_OFFSET, offset)
|
|
148
|
+
},
|
|
149
|
+
null
|
|
150
|
+
)
|
|
151
|
+
} else {
|
|
152
|
+
@Suppress("DEPRECATION")
|
|
153
|
+
contentResolver.query(
|
|
154
|
+
mediaUri,
|
|
155
|
+
projection,
|
|
156
|
+
selection,
|
|
157
|
+
emptyArray<String>(),
|
|
158
|
+
"$sortOrder LIMIT $limit OFFSET $offset"
|
|
159
|
+
)
|
|
160
|
+
}
|
|
161
|
+
}.getOrNull()
|
|
162
|
+
|
|
163
|
+
fun registerObserver(onChanged: () -> Unit): ContentObserver {
|
|
164
|
+
val observer =
|
|
165
|
+
object : ContentObserver(Handler(Looper.getMainLooper())) {
|
|
166
|
+
override fun onChange(selfChange: Boolean) {
|
|
167
|
+
onChanged()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
|
171
|
+
onChanged()
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
contentResolver.registerContentObserver(
|
|
175
|
+
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
|
176
|
+
true,
|
|
177
|
+
observer
|
|
178
|
+
)
|
|
179
|
+
contentResolver.registerContentObserver(
|
|
180
|
+
MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
|
|
181
|
+
true,
|
|
182
|
+
observer
|
|
183
|
+
)
|
|
184
|
+
contentResolver.registerContentObserver(
|
|
185
|
+
MediaStore.Files.getContentUri("external"),
|
|
186
|
+
true,
|
|
187
|
+
observer
|
|
188
|
+
)
|
|
189
|
+
return observer
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fun unregisterObserver(observer: ContentObserver) {
|
|
193
|
+
contentResolver.unregisterContentObserver(observer)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
fun assetFromUri(uri: Uri, type: String): CameraAsset =
|
|
197
|
+
queryAsset(uri, type)
|
|
198
|
+
?: CameraAsset(
|
|
199
|
+
id = uri.lastPathSegment ?: uri.toString(),
|
|
200
|
+
uri = uri,
|
|
201
|
+
type = type,
|
|
202
|
+
fileName = null,
|
|
203
|
+
mimeType = null,
|
|
204
|
+
width = null,
|
|
205
|
+
height = null,
|
|
206
|
+
duration = null,
|
|
207
|
+
size = null,
|
|
208
|
+
dateTaken = System.currentTimeMillis()
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
private fun queryAsset(uri: Uri, type: String): CameraAsset? {
|
|
212
|
+
val baseProjection = arrayOf(
|
|
213
|
+
MediaStore.MediaColumns._ID,
|
|
214
|
+
MediaStore.MediaColumns.DISPLAY_NAME,
|
|
215
|
+
MediaStore.MediaColumns.MIME_TYPE,
|
|
216
|
+
MediaStore.MediaColumns.WIDTH,
|
|
217
|
+
MediaStore.MediaColumns.HEIGHT,
|
|
218
|
+
MediaStore.MediaColumns.SIZE,
|
|
219
|
+
MediaStore.MediaColumns.DATE_TAKEN,
|
|
220
|
+
MediaStore.MediaColumns.DATE_MODIFIED
|
|
221
|
+
)
|
|
222
|
+
val projection =
|
|
223
|
+
if (type == "video") {
|
|
224
|
+
baseProjection + MediaStore.Video.VideoColumns.DURATION
|
|
225
|
+
} else {
|
|
226
|
+
baseProjection
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
contentResolver.query(uri, projection, null, null, null)?.use {
|
|
230
|
+
if (!it.moveToFirst()) {
|
|
231
|
+
return null
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
val idColumn = it.getColumnIndex(MediaStore.MediaColumns._ID)
|
|
235
|
+
val displayNameColumn = it.getColumnIndex(MediaStore.MediaColumns.DISPLAY_NAME)
|
|
236
|
+
val mimeTypeColumn = it.getColumnIndex(MediaStore.MediaColumns.MIME_TYPE)
|
|
237
|
+
val widthColumn = it.getColumnIndex(MediaStore.MediaColumns.WIDTH)
|
|
238
|
+
val heightColumn = it.getColumnIndex(MediaStore.MediaColumns.HEIGHT)
|
|
239
|
+
val sizeColumn = it.getColumnIndex(MediaStore.MediaColumns.SIZE)
|
|
240
|
+
val dateTakenColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_TAKEN)
|
|
241
|
+
val dateModifiedColumn = it.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED)
|
|
242
|
+
val durationColumn = it.getColumnIndex(MediaStore.Video.VideoColumns.DURATION)
|
|
243
|
+
val id = it.getNullableLong(idColumn)?.toString() ?: (uri.lastPathSegment ?: uri.toString())
|
|
244
|
+
val dateTaken = it.getNullableLong(dateTakenColumn)
|
|
245
|
+
?: (it.getNullableLong(dateModifiedColumn)?.times(1000))
|
|
246
|
+
|
|
247
|
+
return CameraAsset(
|
|
248
|
+
id = id,
|
|
249
|
+
uri = uri,
|
|
250
|
+
type = type,
|
|
251
|
+
fileName = it.getNullableString(displayNameColumn),
|
|
252
|
+
mimeType = it.getNullableString(mimeTypeColumn),
|
|
253
|
+
width = it.getNullableInt(widthColumn),
|
|
254
|
+
height = it.getNullableInt(heightColumn),
|
|
255
|
+
duration = it.getNullableLong(durationColumn),
|
|
256
|
+
size = it.getNullableLong(sizeColumn),
|
|
257
|
+
dateTaken = dateTaken ?: System.currentTimeMillis()
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return null
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private fun buildSelection(mediaTypes: String): String {
|
|
265
|
+
val videoType = MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO
|
|
266
|
+
val audioType = MediaStore.Files.FileColumns.MEDIA_TYPE_AUDIO
|
|
267
|
+
val typeCol = MediaStore.Files.FileColumns.MEDIA_TYPE
|
|
268
|
+
return when (mediaTypes) {
|
|
269
|
+
// "image" is the gallery-filter alias for the app's internal "photo" type.
|
|
270
|
+
"photo", "image" -> buildImageSelection()
|
|
271
|
+
"video" -> "$typeCol = $videoType"
|
|
272
|
+
"audio" -> "$typeCol = $audioType"
|
|
273
|
+
"document" -> buildDocumentSelection()
|
|
274
|
+
// Default ("all"/"Recent"): images + videos, matching the prior behaviour.
|
|
275
|
+
else -> "(${buildImageSelection()} OR $typeCol = $videoType)"
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Images are matched on MEDIA_TYPE **or** an `image/…` MIME type.
|
|
281
|
+
*
|
|
282
|
+
* MEDIA_TYPE alone is not sufficient in practice. Files written by other apps into their own
|
|
283
|
+
* `Android/media/…` directory (WhatsApp being the common case) are indexed by a scan that
|
|
284
|
+
* sometimes lands them under MEDIA_TYPE_NONE while still recording the correct MIME type — so
|
|
285
|
+
* a MEDIA_TYPE-only filter silently hid every one of them. Matching the MIME family as well
|
|
286
|
+
* recovers those rows, and covers every image container the platform knows — including the
|
|
287
|
+
* HEIF and AVIF files that motivated this — without enumerating formats here.
|
|
288
|
+
*/
|
|
289
|
+
private fun buildImageSelection(): String {
|
|
290
|
+
val typeCol = MediaStore.Files.FileColumns.MEDIA_TYPE
|
|
291
|
+
val mimeCol = MediaStore.MediaColumns.MIME_TYPE
|
|
292
|
+
val imageType = MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE
|
|
293
|
+
return "($typeCol = $imageType OR $mimeCol LIKE 'image/%')"
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Documents are not a single MediaStore MEDIA_TYPE across all Android versions, so match
|
|
298
|
+
* on both the dedicated document media type (value 6, added in API 30) and a curated list
|
|
299
|
+
* of document MIME types (PDF, Word, Excel, PowerPoint, text, etc.). The literal 6 is used
|
|
300
|
+
* so the selection compiles and runs on every supported API level.
|
|
301
|
+
*/
|
|
302
|
+
private fun buildDocumentSelection(): String {
|
|
303
|
+
val typeCol = MediaStore.Files.FileColumns.MEDIA_TYPE
|
|
304
|
+
val mimeCol = MediaStore.MediaColumns.MIME_TYPE
|
|
305
|
+
val mimeList = DOCUMENT_MIME_TYPES.joinToString(",") { "'$it'" }
|
|
306
|
+
return "($typeCol = $MEDIA_TYPE_DOCUMENT OR $mimeCol IN ($mimeList))"
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
private fun isDocumentMime(mime: String?): Boolean {
|
|
310
|
+
if (mime == null) return false
|
|
311
|
+
return DOCUMENT_MIME_TYPES.contains(mime)
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
companion object {
|
|
315
|
+
// MediaStore.Files.FileColumns.MEDIA_TYPE_DOCUMENT — the dedicated document media
|
|
316
|
+
// type added in API 30. Referenced as a literal so the code compiles and the
|
|
317
|
+
// selection/classification agree on every supported API level.
|
|
318
|
+
private const val MEDIA_TYPE_DOCUMENT = 6
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Newest-first ordering that treats an undated row as being as old as its file, instead of
|
|
322
|
+
* as having no date at all.
|
|
323
|
+
*
|
|
324
|
+
* DATE_TAKEN is populated from EXIF/container metadata, so it is NULL for a large share of
|
|
325
|
+
* real-world images — anything stripped of EXIF in transit, which includes essentially every
|
|
326
|
+
* photo received through WhatsApp. Ordering by `DATE_TAKEN DESC` alone sorts those NULLs
|
|
327
|
+
* *after* every dated row in SQLite, so they were pushed past the paging window (and far
|
|
328
|
+
* past the strip's 80-item cap) and appeared nowhere at all — the reported "images don't
|
|
329
|
+
* show up" bug.
|
|
330
|
+
*
|
|
331
|
+
* COALESCE falls back to DATE_MODIFIED (stored in seconds, hence ×1000) so undated files
|
|
332
|
+
* interleave by their real recency. This mirrors the per-row `dateTaken` fallback the
|
|
333
|
+
* cursor read already performed, which is why the list and its ordering disagreed.
|
|
334
|
+
*/
|
|
335
|
+
private val SORT_ORDER =
|
|
336
|
+
"COALESCE(${MediaStore.MediaColumns.DATE_TAKEN}, " +
|
|
337
|
+
"${MediaStore.MediaColumns.DATE_MODIFIED} * 1000) DESC, " +
|
|
338
|
+
"${MediaStore.Files.FileColumns._ID} DESC"
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Ordering used only if a provider refuses [SORT_ORDER]'s COALESCE expression. DATE_MODIFIED
|
|
342
|
+
* leads because, unlike DATE_TAKEN, it is populated for every row — so undated images still
|
|
343
|
+
* appear in a sensible position instead of being banished to the end of the list.
|
|
344
|
+
*/
|
|
345
|
+
private val FALLBACK_SORT_ORDER =
|
|
346
|
+
"${MediaStore.MediaColumns.DATE_MODIFIED} DESC, " +
|
|
347
|
+
"${MediaStore.Files.FileColumns._ID} DESC"
|
|
348
|
+
|
|
349
|
+
// Supported document MIME types for the "Document" gallery filter. Kept in sync with
|
|
350
|
+
// buildDocumentSelection() so the SQL filter and the per-row type mapping agree.
|
|
351
|
+
private val DOCUMENT_MIME_TYPES = setOf(
|
|
352
|
+
"application/pdf",
|
|
353
|
+
"application/msword",
|
|
354
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
355
|
+
"application/vnd.ms-excel",
|
|
356
|
+
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
357
|
+
"application/vnd.ms-powerpoint",
|
|
358
|
+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
359
|
+
"text/plain",
|
|
360
|
+
"text/csv",
|
|
361
|
+
"application/rtf",
|
|
362
|
+
"text/rtf",
|
|
363
|
+
"application/vnd.oasis.opendocument.text",
|
|
364
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
365
|
+
"application/vnd.oasis.opendocument.presentation",
|
|
366
|
+
"application/epub+zip"
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
private fun android.database.Cursor.getNullableString(index: Int): String? =
|
|
372
|
+
if (index < 0 || isNull(index)) null else getString(index)
|
|
373
|
+
|
|
374
|
+
private fun android.database.Cursor.getNullableInt(index: Int): Int? =
|
|
375
|
+
if (index < 0 || isNull(index)) null else getInt(index)
|
|
376
|
+
|
|
377
|
+
private fun android.database.Cursor.getNullableLong(index: Int): Long? =
|
|
378
|
+
if (index < 0 || isNull(index)) null else getLong(index)
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
package com.customcamera
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.net.Uri
|
|
5
|
+
import android.widget.ImageView
|
|
6
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
7
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
8
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* ReactNative view manager for media-list thumbnails.
|
|
12
|
+
*
|
|
13
|
+
* A JS `<Image>` can only show a still that Fresco can decode: a video file renders nothing at
|
|
14
|
+
* all (there is no frame to read without an extractor), and HEIF/AVIF need the platform decoders.
|
|
15
|
+
* Both cases used to leave the row with an empty tile, so list rows fell back to a generic
|
|
16
|
+
* file-style placeholder. This view hands the work to [ThumbnailLoader] — the same loader the
|
|
17
|
+
* native camera strip and grid already use — so a row shows the media's OWN first usable frame:
|
|
18
|
+
* the user-picked cover if the clip has one, else an extracted video frame, else the decoded
|
|
19
|
+
* image, all downsampled to the tile and served from a shared bitmap cache.
|
|
20
|
+
*
|
|
21
|
+
* Prop contract mirrors [AnimatedImageViewManager]: `src` with a same-request guard so a recycled
|
|
22
|
+
* FlatList cell never keeps the previous row's frame, plus the few asset fields the loader needs
|
|
23
|
+
* to pick a decode path.
|
|
24
|
+
*/
|
|
25
|
+
class MediaThumbnailViewManager : SimpleViewManager<MediaThumbnailView>() {
|
|
26
|
+
override fun getName() = "CustomMediaThumbnail"
|
|
27
|
+
|
|
28
|
+
override fun createViewInstance(reactContext: ThemedReactContext): MediaThumbnailView =
|
|
29
|
+
MediaThumbnailView(reactContext)
|
|
30
|
+
|
|
31
|
+
override fun onDropViewInstance(view: MediaThumbnailView) {
|
|
32
|
+
view.release()
|
|
33
|
+
super.onDropViewInstance(view)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ReactNative applies props one setter at a time and in no guaranteed order, so a setter must
|
|
38
|
+
* only RECORD its value — the decode is started once here, when the whole batch has landed.
|
|
39
|
+
*
|
|
40
|
+
* Requesting from the setters instead made the request identity depend on which props happened
|
|
41
|
+
* to have arrived: a `src` that landed before `mimeType`/`fileName` pinned the decode to a
|
|
42
|
+
* format-hint-less asset, and the later hints — being absent from the request key — never
|
|
43
|
+
* triggered a retry. Video frames never noticed (MediaMetadataRetriever reads the container
|
|
44
|
+
* itself), but every still goes through [ImageFormatSupport], which answers "can this device
|
|
45
|
+
* decode it" and "does it carry alpha" from exactly those two fields.
|
|
46
|
+
*/
|
|
47
|
+
protected override fun onAfterUpdateTransaction(view: MediaThumbnailView) {
|
|
48
|
+
super.onAfterUpdateTransaction(view)
|
|
49
|
+
view.onPropsApplied()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The media's own URI (`content://`, `file://` or a plain path). */
|
|
53
|
+
@ReactProp(name = "src")
|
|
54
|
+
fun setSrc(view: MediaThumbnailView, src: String?) {
|
|
55
|
+
view.setSrc(src)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* `video` selects frame extraction; anything else is decoded as a still. Both the JS
|
|
60
|
+
* (`photo`) and database (`image`) spellings mean the same thing here.
|
|
61
|
+
*/
|
|
62
|
+
@ReactProp(name = "mediaType")
|
|
63
|
+
fun setMediaType(view: MediaThumbnailView, mediaType: String?) {
|
|
64
|
+
view.setMediaType(mediaType)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Cached copy of the file, read in preference to the URI when it still exists on disk. */
|
|
68
|
+
@ReactProp(name = "cachePath")
|
|
69
|
+
fun setCachePath(view: MediaThumbnailView, cachePath: String?) {
|
|
70
|
+
view.setCachePath(cachePath)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A video's user-selected cover frame; overrides frame extraction when present. */
|
|
74
|
+
@ReactProp(name = "coverUri")
|
|
75
|
+
fun setCoverUri(view: MediaThumbnailView, coverUri: String?) {
|
|
76
|
+
view.setCoverUri(coverUri)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Used to decide decodability and alpha handling — see ImageFormatSupport. */
|
|
80
|
+
@ReactProp(name = "mimeType")
|
|
81
|
+
fun setMimeType(view: MediaThumbnailView, mimeType: String?) {
|
|
82
|
+
view.setMimeType(mimeType)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Fallback format hint for assets that arrived without a MIME type. */
|
|
86
|
+
@ReactProp(name = "fileName")
|
|
87
|
+
fun setFileName(view: MediaThumbnailView, fileName: String?) {
|
|
88
|
+
view.setFileName(fileName)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** `cover` (default) or `contain`, matching the `resizeMode` names used on the JS side. */
|
|
92
|
+
@ReactProp(name = "resizeMode")
|
|
93
|
+
fun setResizeMode(view: MediaThumbnailView, resizeMode: String?) {
|
|
94
|
+
view.setResizeMode(resizeMode)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Decode target in pixels, for the rare case a consumer wants a sharper (or cheaper) bitmap
|
|
99
|
+
* than the tile's own measured size. 0 (the default) means "use the measured size".
|
|
100
|
+
*/
|
|
101
|
+
@ReactProp(name = "thumbnailSize", defaultInt = 0)
|
|
102
|
+
fun setThumbnailSize(view: MediaThumbnailView, thumbnailSize: Int) {
|
|
103
|
+
view.setThumbnailSize(thumbnailSize)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* An [ImageView] that paints the asset's own thumbnail, decoded off the main thread by the
|
|
109
|
+
* shared [ThumbnailLoader].
|
|
110
|
+
*
|
|
111
|
+
* Nothing here writes to the file: every path is a read-only decode.
|
|
112
|
+
*/
|
|
113
|
+
class MediaThumbnailView(context: Context) : ImageView(context) {
|
|
114
|
+
private var src: String? = null
|
|
115
|
+
private var mediaType: String? = null
|
|
116
|
+
private var cachePath: String? = null
|
|
117
|
+
private var coverUri: String? = null
|
|
118
|
+
private var mimeType: String? = null
|
|
119
|
+
private var fileName: String? = null
|
|
120
|
+
private var requestedSize = 0
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Identity of the request currently painted or in flight, so a re-layout or a prop update that
|
|
124
|
+
* changes nothing the loader reads does not re-decode. It carries every field that CAN change
|
|
125
|
+
* the decode — source, kind, cache copy, format hints and target size — because a field left
|
|
126
|
+
* out of it is a field whose later arrival looks like a repeat of the request already made.
|
|
127
|
+
*/
|
|
128
|
+
private var currentKey: String? = null
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Bumped on every request. A decode that finishes after its row was recycled compares its
|
|
132
|
+
* captured value against this and drops the bitmap instead of painting it into the wrong row.
|
|
133
|
+
*/
|
|
134
|
+
private var generation = 0
|
|
135
|
+
|
|
136
|
+
init {
|
|
137
|
+
scaleType = ScaleType.CENTER_CROP
|
|
138
|
+
adjustViewBounds = false
|
|
139
|
+
cropToPadding = true
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Every setter only records its value; the decode starts from onPropsApplied() once the whole
|
|
143
|
+
// prop batch has landed — see MediaThumbnailViewManager.onAfterUpdateTransaction.
|
|
144
|
+
fun setSrc(src: String?) {
|
|
145
|
+
this.src = src
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fun setMediaType(mediaType: String?) {
|
|
149
|
+
this.mediaType = mediaType
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fun setCachePath(cachePath: String?) {
|
|
153
|
+
this.cachePath = cachePath
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
fun setCoverUri(coverUri: String?) {
|
|
157
|
+
this.coverUri = coverUri
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
fun setMimeType(mimeType: String?) {
|
|
161
|
+
this.mimeType = mimeType
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
fun setFileName(fileName: String?) {
|
|
165
|
+
this.fileName = fileName
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
fun setResizeMode(resizeMode: String?) {
|
|
169
|
+
scaleType = if (resizeMode == "contain") ScaleType.FIT_CENTER else ScaleType.CENTER_CROP
|
|
170
|
+
adjustViewBounds = false
|
|
171
|
+
cropToPadding = true
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
fun setThumbnailSize(thumbnailSize: Int) {
|
|
175
|
+
this.requestedSize = thumbnailSize
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Every prop in this update has been applied — decode against the complete set. */
|
|
179
|
+
fun onPropsApplied() {
|
|
180
|
+
requestThumbnail()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Drop the frame and orphan any in-flight decode; called when ReactNative discards the view. */
|
|
184
|
+
fun release() {
|
|
185
|
+
generation++
|
|
186
|
+
currentKey = null
|
|
187
|
+
setImageDrawable(null)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// The tile's decode size is its own measured size, so the request usually can't be made until
|
|
191
|
+
// the first layout pass. Props set before then leave `currentKey` null and land here instead.
|
|
192
|
+
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
|
|
193
|
+
super.onSizeChanged(width, height, oldWidth, oldHeight)
|
|
194
|
+
requestThumbnail()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private fun requestThumbnail() {
|
|
198
|
+
val source = src
|
|
199
|
+
if (source.isNullOrEmpty()) {
|
|
200
|
+
generation++
|
|
201
|
+
currentKey = null
|
|
202
|
+
setImageDrawable(null)
|
|
203
|
+
return
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
val size = decodeSize()
|
|
207
|
+
if (size <= 0) return
|
|
208
|
+
|
|
209
|
+
// Parsed before the request is claimed, so an unusable URI leaves `currentKey` untouched and
|
|
210
|
+
// a later, valid prop update is not mistaken for a repeat of this one.
|
|
211
|
+
val uri = runCatching { Uri.parse(source) }.getOrNull() ?: return
|
|
212
|
+
|
|
213
|
+
// Identity of the decode, not of the file: every field the loader reads belongs here, or a
|
|
214
|
+
// later prop update that genuinely changes the result is mistaken for a repeat of this one.
|
|
215
|
+
val key = "${coverUri ?: source}|${normalizedType()}|$cachePath|$mimeType|$fileName|$size"
|
|
216
|
+
if (key == currentKey) return
|
|
217
|
+
currentKey = key
|
|
218
|
+
|
|
219
|
+
generation++
|
|
220
|
+
val token = generation
|
|
221
|
+
// Clear first: a recycled cell must never show the previous row's frame while this one
|
|
222
|
+
// decodes.
|
|
223
|
+
setImageDrawable(null)
|
|
224
|
+
|
|
225
|
+
val asset = CameraAsset(
|
|
226
|
+
id = key,
|
|
227
|
+
uri = uri,
|
|
228
|
+
type = normalizedType(),
|
|
229
|
+
fileName = fileName,
|
|
230
|
+
mimeType = mimeType,
|
|
231
|
+
width = null,
|
|
232
|
+
height = null,
|
|
233
|
+
duration = null,
|
|
234
|
+
size = null,
|
|
235
|
+
dateTaken = null,
|
|
236
|
+
cachePath = cachePath,
|
|
237
|
+
coverUri = coverUri
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
sharedLoader(context).requestBitmap(asset, size) { bitmap ->
|
|
241
|
+
if (token == generation) {
|
|
242
|
+
setImageBitmap(bitmap)
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** The tile's measured size wins; the prop is only a fallback for a not-yet-laid-out view. */
|
|
248
|
+
private fun decodeSize(): Int {
|
|
249
|
+
val measured = maxOf(width, height)
|
|
250
|
+
return if (measured > 0) measured else requestedSize
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** [ThumbnailLoader] only distinguishes `video` from everything else. */
|
|
254
|
+
private fun normalizedType(): String = if (mediaType == "video") "video" else "photo"
|
|
255
|
+
|
|
256
|
+
companion object {
|
|
257
|
+
/**
|
|
258
|
+
* One loader for every thumbnail view in the app: its LruCache is what makes scrolling back
|
|
259
|
+
* up a list free, and a per-view loader would cache nothing and start a coroutine scope per
|
|
260
|
+
* row. Sized against the heap and bounded by [ThumbnailLoader], so it is never cleared —
|
|
261
|
+
* `clear()` would cancel the scope shared by every other row.
|
|
262
|
+
*/
|
|
263
|
+
@Volatile
|
|
264
|
+
private var loader: ThumbnailLoader? = null
|
|
265
|
+
|
|
266
|
+
private fun sharedLoader(context: Context): ThumbnailLoader =
|
|
267
|
+
loader ?: synchronized(this) {
|
|
268
|
+
loader ?: ThumbnailLoader(context.applicationContext.contentResolver)
|
|
269
|
+
.also { loader = it }
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Drop any cached bitmap decoded from [source] on the shared loader.
|
|
274
|
+
*
|
|
275
|
+
* Used by [SecureMediaDeleter]: a decoded thumbnail outlives the file it came from, so a
|
|
276
|
+
* securely erased photo would keep rendering in the media list from cache — and that bitmap
|
|
277
|
+
* is a real, if downscaled, copy of media the user asked to be destroyed. A no-op when no
|
|
278
|
+
* thumbnail has been requested yet in this process (the loader is created lazily), which is
|
|
279
|
+
* correct: an unbuilt cache holds nothing to evict.
|
|
280
|
+
*/
|
|
281
|
+
fun evictFromSharedCache(source: String?) {
|
|
282
|
+
if (source.isNullOrEmpty()) return
|
|
283
|
+
loader?.evictSource(source)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|