### MediaRepository Interface and Examples Source: https://context7.com/google/jetpack-camera-app/llms.txt Handles saving, deleting, and loading media. Use this repository for post-capture workflows, interacting with MediaStore, and loading media for display. ```kotlin interface MediaRepository { val currentMedia: StateFlow suspend fun setCurrentMedia(pendingMedia: MediaDescriptor) suspend fun getLastCapturedMedia(): MediaDescriptor suspend fun deleteMedia(mediaDescriptor: MediaDescriptor.Content): Boolean suspend fun copyToUri(mediaDescriptor: MediaDescriptor.Content, destinationUri: Uri) suspend fun saveToMediaStore( mediaDescriptor: MediaDescriptor.Content, outputFilename: String? ): Uri? suspend fun load(mediaDescriptor: MediaDescriptor): Media // Media.Image(bitmap) | Media.Video(uri) } ``` ```kotlin // After cache-and-review capture, save the cached image to MediaStore: val cachedDescriptor = MediaDescriptor.Content.Image(uri = cacheUri, thumbnail = null, isCached = true) val savedUri = mediaRepository.saveToMediaStore(cachedDescriptor, outputFilename = null) ``` ```kotlin // Load for display in the review screen: when (val media = mediaRepository.load(cachedDescriptor)) { is Media.Image -> ImageBitmap(media.bitmap) is Media.Video -> VideoPlayer(media.uri) is Media.None, is Media.Error -> ShowError() } ``` ```kotlin // Delete cached media: mediaRepository.deleteMedia(cachedDescriptor) ``` -------------------------------- ### Configure MacrobenchmarkRule for Startup Latency Source: https://context7.com/google/jetpack-camera-app/llms.txt This setup uses MacrobenchmarkRule to measure cold startup latency. Ensure all necessary permissions are granted before running the benchmark. The benchmark package name and iteration count can be configured. ```kotlin @RunWith(AndroidJUnit4::class) class StartupBenchmark { @get:Rule val benchmarkRule = MacrobenchmarkRule() // Cold startup with all permissions pre-granted: @Test fun startupColdNoPermissionRequest() { benchmarkRule.measureRepeated( packageName = "com.google.jetpackcamera", // JCA_PACKAGE_NAME constant metrics = listOf(StartupTimingMetric()), iterations = 5, // DEFAULT_TEST_ITERATIONS startupMode = StartupMode.COLD, setupBlock = { allowAllRequiredPerms(APP_REQUIRED_PERMISSIONS.toTypedArray()) } ) { pressHome() startActivityAndWait() } } } ``` -------------------------------- ### QuickSettingsController Interface and Examples Source: https://context7.com/google/jetpack-camera-app/llms.txt Interface for controlling quick settings within the camera viewfinder. Use these methods to immediately change settings like dynamic range, lens facing, and capture format. ```kotlin interface QuickSettingsController { fun toggleQuickSettings() fun setFocusedSetting(focusedQuickSetting: FocusedQuickSetting) fun setLensFacing(lensFace: LensFacing) // flip between FRONT / BACK fun setFlash(flashMode: FlashMode) fun setAspectRatio(aspectRatio: AspectRatio) fun setStreamConfig(streamConfig: StreamConfig) fun setDynamicRange(dynamicRange: DynamicRange) fun setImageFormat(imageOutputFormat: ImageOutputFormat) fun setConcurrentCameraMode(concurrentCameraMode: ConcurrentCameraMode) fun setCaptureMode(captureMode: CaptureMode) } ``` ```kotlin // Toggle HDR on: quickSettingsController.setDynamicRange(DynamicRange.HLG10) ``` ```kotlin // Switch to front camera: quickSettingsController.setLensFacing(LensFacing.FRONT) ``` ```kotlin // Enable dual concurrent camera (front + back simultaneously): quickSettingsController.setConcurrentCameraMode(ConcurrentCameraMode.DUAL) ``` ```kotlin // Switch to Ultra HDR image capture: quickSettingsController.setImageFormat(ImageOutputFormat.JPEG_ULTRA_HDR) ``` -------------------------------- ### ZoomController Interface and Examples Source: https://context7.com/google/jetpack-camera-app/llms.txt Interface for managing zoom ratios and animation states. Use `setZoomRatio` for pinch-to-zoom and `setZoomAnimationState` to control the zoom UI. ```kotlin interface ZoomController { fun setZoomRatio(zoomRatio: CameraZoomRatio) fun setZoomAnimationState(targetValue: Float?) // null to dismiss zoom bar UI } ``` ```kotlin // Pinch-to-zoom gesture handler in a Composable: Modifier.pointerInput(Unit) { detectTransformGestures { _, _, zoomChange, _ -> zoomController.setZoomRatio( CameraZoomRatio(ZoomStrategy.Scale(value = zoomChange)) ) } } ``` -------------------------------- ### Observe Camera State in ViewModel Source: https://context7.com/google/jetpack-camera-app/llms.txt Example of observing the camera state within a ViewModel using coroutines and `collect`. ```kotlin // Observing camera state in a ViewModel/coroutine: viewModelScope.launch { cameraSystem.getCurrentCameraState().collect { state -> val isRecording = state.videoRecordingState is VideoRecordingState.Active val zoomLevel = state.zoomRatios[LensFacing.BACK] ?: 1f val focusRunning = state.focusState is FocusState.Specified && (state.focusState as FocusState.Specified).status == FocusState.Status.RUNNING } } ``` -------------------------------- ### Apply External Capture Mode Override Source: https://context7.com/google/jetpack-camera-app/llms.txt Example of applying an external capture mode override, such as from an intent, to a settings object. ```kotlin // Apply an ExternalCaptureMode override (e.g. from an intent) to a settings object val base = DEFAULT_CAMERA_APP_SETTINGS // = CameraAppSettings() val settings = base.applyExternalCaptureMode(ExternalCaptureMode.ImageCapture) // settings.captureMode == CaptureMode.IMAGE_ONLY ``` -------------------------------- ### CameraController Interface for Lifecycle and Focus Control Source: https://context7.com/google/jetpack-camera-app/llms.txt This interface manages camera lifecycle and focus interactions. Use it to start and stop the camera, and to implement tap-to-focus functionality based on screen coordinates. ```kotlin interface CameraController { fun startCamera() fun stopCamera() fun tapToFocus(x: Float, y: Float) // normalized surface coordinates fun setDisplayRotation(deviceRotation: DeviceRotation) // Natural, Rotation90, etc. } ``` ```kotlin // In a Composable wired to touch events on the preview surface: Box( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { detectTapGestures { offset -> cameraController.tapToFocus(offset.x, offset.y) } } ) ``` ```kotlin // Start/stop camera alongside the Compose lifecycle: DisposableEffect(Unit) { cameraController.startCamera() onDispose { cameraController.stopCamera() } } ``` -------------------------------- ### CameraController Source: https://context7.com/google/jetpack-camera-app/llms.txt UI-facing controller for camera lifecycle management and focus interactions. It allows starting and stopping the camera, setting display rotation, and handling tap-to-focus gestures. ```APIDOC ## CameraController — Lifecycle and focus control UI-facing controller for camera lifecycle management and focus interactions. ```kotlin interface CameraController { fun startCamera() fun stopCamera() fun tapToFocus(x: Float, y: Float) // normalized surface coordinates fun setDisplayRotation(deviceRotation: DeviceRotation) // Natural, Rotation90, etc. } // In a Composable wired to touch events on the preview surface: Box( modifier = Modifier .fillMaxSize() .pointerInput(Unit) { detectTapGestures { offset -> cameraController.tapToFocus(offset.x, offset.y) } } ) // Start/stop camera alongside the Compose lifecycle: DisposableEffect(Unit) { cameraController.startCamera() onDispose { cameraController.stopCamera() } } ``` ``` -------------------------------- ### ExternalCaptureMode Enum and Intent Examples Source: https://context7.com/google/jetpack-camera-app/llms.txt Enum defining different intent-driven launch modes for capture. Use these to launch the camera app for specific capture types like single image, video, or with debug options. ```kotlin enum class ExternalCaptureMode { Standard, // default app launch, free photo/video toggle ImageCapture, // ACTION_IMAGE_CAPTURE — single photo, returns URI VideoCapture, // ACTION_VIDEO_CAPTURE — single video, returns URI MultipleImageCapture // INTENT_ACTION_STILL_IMAGE_CAMERA — sequential photos } ``` ```kotlin // Launching JCA for single image capture from another app: val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply { putExtra(MediaStore.EXTRA_OUTPUT, outputUri) // where to save the image } startActivityForResult(intent, REQUEST_IMAGE_CAPTURE) ``` ```kotlin // Launching for video capture: val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE).apply { putExtra(MediaStore.EXTRA_OUTPUT, outputUri) } startActivityForResult(intent, REQUEST_VIDEO_CAPTURE) ``` ```kotlin // Launching with debug mode and single-lens restriction: val intent = Intent(context, MainActivity::class.java).apply { putExtra("KEY_DEBUG_MODE", true) putExtra("KEY_DEBUG_SINGLE_LENS_MODE", "back") // "back" or "front" } startActivity(intent) ``` ```kotlin // Launching with cache-and-review workflow: val intent = Intent(context, MainActivity::class.java).apply { putExtra("KEY_REVIEW_AFTER_CAPTURE", true) } startActivity(intent) ``` -------------------------------- ### QuickSettingsController Source: https://context7.com/google/jetpack-camera-app/llms.txt Interface for controlling quick settings on the camera preview screen. Changes are applied immediately to the live camera session. ```APIDOC ## QuickSettingsController ### Description Controller interface for the quick-settings panel (expanded from the downward-arrow overlay on the preview screen). Changes apply immediately to the live camera session. ### Methods - `toggleQuickSettings()`: Toggles the visibility of the quick settings panel. - `setFocusedSetting(focusedQuickSetting: FocusedQuickSetting)`: Sets the currently focused quick setting. - `setLensFacing(lensFace: LensFacing)`: Sets the camera lens to use (FRONT or BACK). - `setFlash(flashMode: FlashMode)`: Sets the flash mode. - `setAspectRatio(aspectRatio: AspectRatio)`: Sets the aspect ratio for the camera preview. - `setStreamConfig(streamConfig: StreamConfig)`: Configures the camera stream. - `setDynamicRange(dynamicRange: DynamicRange)`: Sets the dynamic range for the camera. - `setImageFormat(imageOutputFormat: ImageOutputFormat)`: Sets the output format for captured images. - `setConcurrentCameraMode(concurrentCameraMode: ConcurrentCameraMode)`: Sets the concurrent camera mode. - `setCaptureMode(captureMode: CaptureMode)`: Sets the capture mode. ### Usage Examples ```kotlin // Toggle HDR on: quickSettingsController.setDynamicRange(DynamicRange.HLG10) // Switch to front camera: quickSettingsController.setLensFacing(LensFacing.FRONT) // Enable dual concurrent camera (front + back simultaneously): quickSettingsController.setConcurrentCameraMode(ConcurrentCameraMode.DUAL) // Switch to Ultra HDR image capture: quickSettingsController.setImageFormat(ImageOutputFormat.JPEG_ULTRA_HDR) ``` ``` -------------------------------- ### Use FakeCameraSystem for In-Process Testing Source: https://context7.com/google/jetpack-camera-app/llms.txt A controllable CameraSystem implementation for unit and instrumentation tests. Instantiate with custom initial settings and use its methods to simulate camera operations and assert states. ```kotlin val fakeCameraSystem = FakeCameraSystem( defaultCameraSettings = CameraAppSettings( cameraLensFacing = LensFacing.BACK, flashMode = FlashMode.AUTO ) ) ``` ```kotlin fakeCameraSystem.initialize(CameraAppSettings()) { /* no-op */ } testScope.launch { fakeCameraSystem.runCamera() } ``` ```kotlin assertTrue(fakeCameraSystem.previewStarted) assertEquals(0, fakeCameraSystem.numPicturesTaken) ``` ```kotlin fakeCameraSystem.takePicture() assertEquals(1, fakeCameraSystem.numPicturesTaken) ``` ```kotlin fakeCameraSystem.emitScreenFlashEvent( CameraSystem.ScreenFlashEvent(CameraSystem.ScreenFlashEvent.Type.APPLY_UI) {} ) ``` ```kotlin fakeCameraSystem.startVideoRecording(SaveLocation.Default) {} assertTrue(fakeCameraSystem.recordingInProgress) fakeCameraSystem.pauseVideoRecording() assertTrue(fakeCameraSystem.isRecordingPaused) fakeCameraSystem.stopVideoRecording() assertFalse(fakeCameraSystem.recordingInProgress) ``` -------------------------------- ### SettingsRepository Source: https://context7.com/google/jetpack-camera-app/llms.txt Interface backed by DataStore (protobuf) in LocalSettingsRepository. Exposes defaultCameraAppSettings as a Flow and suspend update functions for each individual setting. ```APIDOC ## SettingsRepository Interface backed by DataStore (protobuf) in `LocalSettingsRepository`. Exposes `defaultCameraAppSettings` as a `Flow` and `suspend` update functions for each individual setting. ### Properties - **`defaultCameraAppSettings: Flow`**: A flow emitting the current camera application settings. ### Methods - **`getCurrentDefaultCameraAppSettings(): CameraAppSettings`**: Retrieves the current camera application settings. - **`updateDefaultLensFacing(lensFacing: LensFacing)`**: Updates the default lens facing. - **`updateDarkModeStatus(darkMode: DarkMode)`**: Updates the dark mode status (DarkMode.DARK / LIGHT / SYSTEM). - **`updateFlashModeStatus(flashMode: FlashMode)`**: Updates the flash mode status. - **`updateAspectRatio(aspectRatio: AspectRatio)`**: Updates the aspect ratio. - **`updateStreamConfig(streamConfig: StreamConfig)`**: Updates the stream configuration. - **`updateLowLightBoostPriority(lowLightBoostPriority: LowLightBoostPriority)`**: Updates the low light boost priority. - **`updateStabilizationMode(stabilizationMode: StabilizationMode)`**: Updates the stabilization mode. - **`updateDynamicRange(dynamicRange: DynamicRange)`**: Updates the dynamic range. - **`updateTargetFrameRate(targetFrameRate: Int)`**: Updates the target frame rate (0 for AUTO). - **`updateImageFormat(imageFormat: ImageOutputFormat)`**: Updates the image format. - **`updateMaxVideoDuration(durationMillis: Long)`**: Updates the maximum video duration in milliseconds. - **`updateVideoQuality(videoQuality: VideoQuality)`**: Updates the video quality. - **`updateAudioEnabled(isAudioEnabled: Boolean)`**: Updates whether audio is enabled. ### Examples ```kotlin // Reading the settings stream: settingsRepository.defaultCameraAppSettings .collect { settings -> val fps = settings.targetFrameRate // 0 = AUTO val quality = settings.videoQuality // UNSPECIFIED, SD, HD, FHD, UHD } // Writing a setting: viewModelScope.launch { settingsRepository.updateFlashModeStatus(FlashMode.AUTO) settingsRepository.updateTargetFrameRate(TARGET_FPS_30) // 30 settingsRepository.updateStabilizationMode(StabilizationMode.HIGH_QUALITY) settingsRepository.updateMaxVideoDuration(60_000L) // 60 seconds settingsRepository.updateImageFormat(ImageOutputFormat.JPEG_ULTRA_HDR) } ``` ``` -------------------------------- ### Configure Git Pre-push Hook Source: https://github.com/google/jetpack-camera-app/blob/main/README.md Run this command once from the repository root to enable the pre-push hook for automatic code formatting checks. ```bash git config core.hooksPath scripts/git-hooks ``` -------------------------------- ### Find and Link Libraries Source: https://github.com/google/jetpack-camera-app/blob/main/core/camera/src/main/cpp/CMakeLists.txt Locates the 'log' and 'GLESv3' libraries and links them to the 'opengl_debug_lib'. ```cmake find_library(log-lib log) find_library(opengles3-lib GLESv3) target_link_libraries(opengl_debug_lib PRIVATE ${log-lib} ${opengles3-lib}) ``` -------------------------------- ### CaptureController Interface for Image and Video Capture Source: https://context7.com/google/jetpack-camera-app/llms.txt This interface provides commands for capturing images and starting/stopping video recordings. It also allows controlling recording lock, pause state, and audio. Use this controller to issue capture commands from your UI. ```kotlin interface CaptureController { val captureEvents: ReceiveChannel fun captureImage(contentResolver: ContentResolver) fun startVideoRecording() fun stopVideoRecording() fun setLockedRecording(isLocked: Boolean) // hands-free recording lock fun setPaused(shouldBePaused: Boolean) fun setAudioEnabled(shouldEnableAudio: Boolean) } ``` ```kotlin // Example: trigger a photo capture from a button click handler Button(onClick = { captureController.captureImage(context.contentResolver) }) { Text("Take Photo") } ``` ```kotlin // Example: start / stop video with audio toggle captureController.setAudioEnabled(true) captureController.startVideoRecording() // ... user taps stop ... captureController.stopVideoRecording() ``` ```kotlin // Collect results asynchronously: LaunchedEffect(Unit) { for (event in captureController.captureEvents) { if (event is VideoCaptureEvent.VideoSaved) { Log.d("JCA", "Video saved to ${event.savedUri}") } } } ``` -------------------------------- ### CameraSystem Interface Source: https://context7.com/google/jetpack-camera-app/llms.txt The primary interface for all camera operations, including initialization, capture, recording, and runtime controls. ```APIDOC ## CameraSystem Interface ### Description `CameraSystem` is the central interface for all camera operations. It lives in `:core:camera` and is implemented by `CameraXCameraSystem`. Every camera operation (capture, recording, zoom, focus, settings) is dispatched through this interface. ### Initialization and lifecycle - **`suspend fun initialize(cameraAppSettings: CameraAppSettings, cameraPropertiesJSONCallback: (result: String) -> Unit)`** Must be called before `runCamera()`. Populates initial settings and returns camera properties JSON. - **`suspend fun runCamera()`** Starts the camera preview loop; suspends until the calling coroutine is cancelled. Frames won't stream until a `SurfaceRequest` from `getSurfaceRequest()` is fulfilled. ### Image capture - **`suspend fun takePicture(onCaptureStarted: (() -> Unit) = {})`** Takes a picture with default save location. - **`suspend fun takePicture(contentResolver: ContentResolver, saveLocation: SaveLocation, onCaptureStarted: (() -> Unit) = {}): ImageCapture.OutputFileResults`** Takes a picture and saves it to the specified location. - `saveLocation`: `SaveLocation.Default` or `SaveLocation.Explicit(uri)` ### Video capture - **`suspend fun startVideoRecording(saveLocation: SaveLocation, onVideoRecord: (OnVideoRecordEvent) -> Unit)`** Starts video recording. - `saveLocation`: Specifies where to save the video. - `onVideoRecord`: Callback for `OnVideoRecorded(uri)` or `OnVideoRecordError(t)`. - **`suspend fun pauseVideoRecording()`** Pauses the current video recording. - **`suspend fun resumeVideoRecording()`** Resumes the current video recording. - **`suspend fun stopVideoRecording()`** Stops the current video recording. ### State flows - **`fun getCurrentCameraState(): StateFlow`** Provides the current camera state, including zoom, recording status, focus, and low-light boost. - **`fun getSystemConstraints(): StateFlow`** Provides the system constraints for the camera. - **`fun getSurfaceRequest(): StateFlow`** Provides the `SurfaceRequest` to be wired to a Compose `PreviewView`. - **`fun getScreenFlashEvents(): ReceiveChannel`** Provides a channel for screen flash events, used by the front-camera flash overlay. - **`fun getCurrentSettings(): StateFlow`** Provides the current camera application settings. ### Runtime controls - **`fun changeZoomRatio(newZoomState: CameraZoomRatio)`** Changes the zoom ratio of the camera. - **`fun setFlashMode(flashMode: FlashMode)`** Sets the flash mode. Possible values: `OFF`, `ON`, `AUTO`, `LOW_LIGHT_BOOST`. - **`fun setDeviceRotation(deviceRotation: DeviceRotation)`** Sets the device rotation. - **`suspend fun setLensFacing(lensFacing: LensFacing)`** Sets the lens facing direction. Possible values: `FRONT`, `BACK`. - **`suspend fun tapToFocus(x: Float, y: Float)`** Initiates tap-to-focus at the specified coordinates. - **`suspend fun setAspectRatio(aspectRatio: AspectRatio)`** Sets the aspect ratio of the camera preview. Possible values: `THREE_FOUR`, `NINE_SIXTEEN`, `ONE_ONE`. - **`suspend fun setStreamConfig(streamConfig: StreamConfig)`** Sets the stream configuration. Possible values: `SINGLE_STREAM`, `MULTI_STREAM`. - **`suspend fun setDynamicRange(dynamicRange: DynamicRange)`** Sets the dynamic range. Possible values: `SDR`, `HLG10`. - **`suspend fun setImageFormat(imageFormat: ImageOutputFormat)`** Sets the image output format. Possible values: `JPEG`, `JPEG_ULTRA_HDR`. - **`suspend fun setVideoQuality(videoQuality: VideoQuality)`** Sets the video quality. Possible values: `UNSPECIFIED`, `SD`, `HD`, `FHD`, `UHD`. - **`suspend fun setStabilizationMode(stabilizationMode: StabilizationMode)`** Sets the video stabilization mode. - **`suspend fun setTargetFrameRate(targetFrameRate: Int)`** Sets the target frame rate for video recording. `0` means `AUTO`. - **`suspend fun setMaxVideoDuration(durationInMillis: Long)`** Sets the maximum video duration in milliseconds. `0L` means unlimited. - **`suspend fun setAudioEnabled(isAudioEnabled: Boolean)`** Enables or disables audio recording. - **`suspend fun setConcurrentCameraMode(concurrentCameraMode: ConcurrentCameraMode)`** Enables or disables concurrent camera mode. - **`suspend fun setLowLightBoostPriority(lowLightBoostPriority: LowLightBoostPriority)`** Sets the priority for low-light boost. - **`suspend fun setCaptureMode(captureMode: CaptureMode)`** Sets the capture mode. Possible values: `STANDARD`, `IMAGE_ONLY`, `VIDEO_ONLY`. ### Data Types - **`ScreenFlashEvent(type: Type, onComplete: () -> Unit)`** Represents a screen flash event for the front camera. - `Type`: `APPLY_UI` or `CLEAR_UI`. - `onComplete`: Callback to be invoked upon completion. ``` -------------------------------- ### Implement ConstraintsRepository for Runtime Capability Propagation Source: https://context7.com/google/jetpack-camera-app/llms.txt This interface defines how camera system constraints are exposed as a StateFlow. The SettableConstraintsRepository allows updating these constraints. Use this to bridge camera system capabilities to the UI settings layer. ```kotlin interface ConstraintsRepository { val systemConstraints: StateFlow } interface SettableConstraintsRepository : ConstraintsRepository { fun updateSystemConstraints(systemConstraints: CameraSystemConstraints) } ``` ```kotlin viewModelScope.launch { cameraSystemRepository.cameraSystem.getSystemConstraints() .filterNotNull() .collect { constraintsRepository.updateSystemConstraints(it) } } ``` ```kotlin combine( settingsRepository.defaultCameraAppSettings, constraintsRepository.systemConstraints.filterNotNull() ) { settings, constraints -> val backConstraints = constraints.perLensConstraints[LensFacing.BACK] val supportsHDR = backConstraints?.supportedDynamicRanges?.contains(DynamicRange.HLG10) == true // build SettingsUiState.Enabled(...) } ``` -------------------------------- ### Observe MediaStore Insertions in Instrumentation Tests Source: https://context7.com/google/jetpack-camera-app/llms.txt Use this helper in device tests to observe newly written files in the Android MediaStore as a reactive Flow. It can filter by file name prefix. ```kotlin import androidx.camera.testing.impl.CameraXResetRule import androidx.compose.ui.test.junit4.createAndroidComposeRule import androidx.compose.ui.test.performClick import androidx.compose.ui.test.hasTestTag import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Rule import org.junit.Test import android.provider.MediaStore import androidx.camera.integration.app.R import androidx.camera.integration.app.util.mediaStoreInsertedFlow import kotlin.time.Duration.Companion.seconds import kotlin.time.ExperimentalTime // In an instrumentation test: wait for a captured image to appear in DCIM/Camera @OptIn(ExperimentalCoroutinesApi::class, ExperimentalTime::class) @Test fun imageCaptureWritesToMediaStore() = runTest { val mediaFlow = mediaStoreInsertedFlow( mediaUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI, instrumentation = InstrumentationRegistry.getInstrumentation(), filePrefix = "JCA_" // optional: filter by file name prefix ) // Trigger capture in the UI... device.findObject(By.res("capture_button")).click() // Assert that a new file arrives within timeout: val (displayName, uri) = withTimeout(10_000) { mediaFlow.first() } assertTrue(displayName.startsWith("JCA_")) assertNotNull(uri) } ``` -------------------------------- ### Define Camera App Settings Data Class Source: https://context7.com/google/jetpack-camera-app/llms.txt Represents the full persisted state of the camera. Passed to `CameraSystem.initialize()` and tracked via `SettingsRepository.defaultCameraAppSettings`. ```kotlin data class CameraAppSettings( val captureMode: CaptureMode = CaptureMode.STANDARD, val cameraLensFacing: LensFacing = LensFacing.BACK, val darkMode: DarkMode = DarkMode.DARK, // DARK, LIGHT, SYSTEM val flashMode: FlashMode = FlashMode.OFF, val streamConfig: StreamConfig = StreamConfig.MULTI_STREAM, val aspectRatio: AspectRatio = AspectRatio.NINE_SIXTEEN, val stabilizationMode: StabilizationMode = StabilizationMode.AUTO, val dynamicRange: DynamicRange = DynamicRange.SDR, val videoQuality: VideoQuality = VideoQuality.UNSPECIFIED, val defaultZoomRatios: Map = mapOf(), val targetFrameRate: Int = TARGET_FPS_AUTO, // 0 = auto val imageFormat: ImageOutputFormat = ImageOutputFormat.JPEG, val audioEnabled: Boolean = true, val deviceRotation: DeviceRotation = DeviceRotation.Natural, val concurrentCameraMode: ConcurrentCameraMode = ConcurrentCameraMode.OFF, val maxVideoDurationMillis: Long = UNLIMITED_VIDEO_DURATION, // 0L = unlimited val lowLightBoostPriority: LowLightBoostPriority = LowLightBoostPriority.PRIORITIZE_AE_MODE, val debugSettings: DebugSettings = DebugSettings() ) ``` -------------------------------- ### Run Instrumentation Tests on Pixel 2 Emulator Source: https://github.com/google/jetpack-camera-app/blob/main/README.md Execute instrumentation tests on the Pixel 2 emulator (API 28) using this Gradle command. ```bash ./gradlew pixel2Api28StableDebugAndroidTest ``` -------------------------------- ### Apply Camera Settings Diffs Efficiently Source: https://context7.com/google/jetpack-camera-app/llms.txt This extension function on CameraAppSettings applies only changed fields to a CameraSystem, preventing redundant reconfigurations. It's useful for reacting to DataStore settings emissions by updating the live camera system with minimal overhead. ```kotlin // Defined in CameraSystem.Companion: suspend fun CameraAppSettings.applyDiffs( new: CameraAppSettings, cameraSystem: CameraSystem ) ``` ```kotlin var oldCameraAppSettings: CameraAppSettings? = null settingsRepository.defaultCameraAppSettings.collect { new -> oldCameraAppSettings?.applyDiffs(new, cameraSystemRepository.cameraSystem) oldCameraAppSettings = new } ``` -------------------------------- ### Set Linker Options Source: https://github.com/google/jetpack-camera-app/blob/main/core/camera/src/main/cpp/CMakeLists.txt Applies specific linker options to the 'opengl_debug_lib', such as maximum page size. ```cmake target_link_options( opengl_debug_lib PRIVATE "-Wl,-z,max-page-size=16384" ) ``` -------------------------------- ### CameraSystem Interface Definition Source: https://context7.com/google/jetpack-camera-app/llms.txt Defines the primary interface for all camera operations, including initialization, capture, recording, and runtime controls. It is implemented by CameraXCameraSystem. ```kotlin interface CameraSystem { // Must be called before runCamera(). Populates initial settings and returns camera properties JSON. suspend fun initialize( cameraAppSettings: CameraAppSettings, cameraPropertiesJSONCallback: (result: String) -> Unit ) // Starts the camera preview loop; suspends until the calling coroutine is cancelled. // Frames won't stream until a SurfaceRequest from getSurfaceRequest() is fulfilled. suspend fun runCamera() // ------- Image capture ------- suspend fun takePicture(onCaptureStarted: (() -> Unit) = {}) suspend fun takePicture( contentResolver: ContentResolver, saveLocation: SaveLocation, // SaveLocation.Default or SaveLocation.Explicit(uri) onCaptureStarted: (() -> Unit) = {} ): ImageCapture.OutputFileResults // ------- Video capture ------- suspend fun startVideoRecording( saveLocation: SaveLocation, onVideoRecord: (OnVideoRecordEvent) -> Unit // OnVideoRecorded(uri) or OnVideoRecordError(t) ) suspend fun pauseVideoRecording() suspend fun resumeVideoRecording() suspend fun stopVideoRecording() // ------- State flows ------- fun getCurrentCameraState(): StateFlow // zoom, recording, focus, LLB, etc. fun getSystemConstraints(): StateFlow fun getSurfaceRequest(): StateFlow // wire to Compose PreviewView fun getScreenFlashEvents(): ReceiveChannel fun getCurrentSettings(): StateFlow // ------- Runtime controls ------- fun changeZoomRatio(newZoomState: CameraZoomRatio) fun setFlashMode(flashMode: FlashMode) // OFF, ON, AUTO, LOW_LIGHT_BOOST fun setDeviceRotation(deviceRotation: DeviceRotation) suspend fun setLensFacing(lensFacing: LensFacing) // FRONT, BACK suspend fun tapToFocus(x: Float, y: Float) suspend fun setAspectRatio(aspectRatio: AspectRatio) // THREE_FOUR, NINE_SIXTEEN, ONE_ONE suspend fun setStreamConfig(streamConfig: StreamConfig) // SINGLE_STREAM, MULTI_STREAM suspend fun setDynamicRange(dynamicRange: DynamicRange) // SDR, HLG10 suspend fun setImageFormat(imageFormat: ImageOutputFormat) // JPEG, JPEG_ULTRA_HDR suspend fun setVideoQuality(videoQuality: VideoQuality) // UNSPECIFIED, SD, HD, FHD, UHD suspend fun setStabilizationMode(stabilizationMode: StabilizationMode) suspend fun setTargetFrameRate(targetFrameRate: Int) // 0=AUTO, 15, 30, 60 suspend fun setMaxVideoDuration(durationInMillis: Long) // 0L = unlimited suspend fun setAudioEnabled(isAudioEnabled: Boolean) suspend fun setConcurrentCameraMode(concurrentCameraMode: ConcurrentCameraMode) suspend fun setLowLightBoostPriority(lowLightBoostPriority: LowLightBoostPriority) suspend fun setCaptureMode(captureMode: CaptureMode) // STANDARD, IMAGE_ONLY, VIDEO_ONLY } ``` ```kotlin // Screen flash event used by the front-camera flash overlay data class ScreenFlashEvent(val type: Type, val onComplete: () -> Unit) { enum class Type { APPLY_UI, CLEAR_UI } } ``` -------------------------------- ### Camera Zoom Control Models Source: https://context7.com/google/jetpack-camera-app/llms.txt Wraps ZoomStrategy to control camera zoom via CameraSystem or ZoomController. Supports absolute, scale, and incremental zoom adjustments. ```kotlin // Absolute: set the zoom ratio to exactly 2.0x on the primary lens cameraSystem.changeZoomRatio( CameraZoomRatio(ZoomStrategy.Absolute(value = 2.0f, lensToZoom = LensToZoom.PRIMARY)) ) ``` ```kotlin // Scale: multiply current zoom by 1.1x (e.g., on pinch gesture update) zoomController.setZoomRatio( CameraZoomRatio(ZoomStrategy.Scale(value = 1.1f)) ) ``` ```kotlin // Increment: add 0.5x to current zoom zoomController.setZoomRatio( CameraZoomRatio(ZoomStrategy.Increment(value = 0.5f, lensToZoom = LensToZoom.PRIMARY)) ) ``` ```kotlin // Zoom animation state (show/hide zoom bar UI) zoomController.setZoomAnimationState(targetValue = 2.0f) // show bar targeting 2x zoomController.setZoomAnimationState(targetValue = null) // hide bar ``` -------------------------------- ### CameraZoomRatio / ZoomStrategy Source: https://context7.com/google/jetpack-camera-app/llms.txt CameraZoomRatio wraps a ZoomStrategy to express absolute, relative, or incremental zoom changes applied via CameraSystem.changeZoomRatio() or ZoomController.setZoomRatio(). ```APIDOC ## CameraZoomRatio / ZoomStrategy `CameraZoomRatio` wraps a `ZoomStrategy` to express absolute, relative, or incremental zoom changes applied via `CameraSystem.changeZoomRatio()` or `ZoomController.setZoomRatio()`. ### Zoom Strategies - **Absolute**: Set the zoom ratio to exactly a specified value. - **Scale**: Multiply current zoom by a specified factor. - **Increment**: Add a specified value to the current zoom. ### Methods - **`CameraSystem.changeZoomRatio(zoomRatio: CameraZoomRatio)`**: Applies a zoom change using `CameraZoomRatio`. - **`ZoomController.setZoomRatio(zoomRatio: CameraZoomRatio)`**: Applies a zoom change using `CameraZoomRatio`. - **`ZoomController.setZoomAnimationState(targetValue: Float?)`**: Controls zoom animation state (show/hide zoom bar UI). ### Examples ```kotlin // Absolute zoom cameraSystem.changeZoomRatio( CameraZoomRatio(ZoomStrategy.Absolute(value = 2.0f, lensToZoom = LensToZoom.PRIMARY)) ) // Scale zoom zoomController.setZoomRatio( CameraZoomRatio(ZoomStrategy.Scale(value = 1.1f)) ) // Increment zoom zoomController.setZoomRatio( CameraZoomRatio(ZoomStrategy.Increment(value = 0.5f, lensToZoom = LensToZoom.PRIMARY)) ) // Zoom animation zoomController.setZoomAnimationState(targetValue = 2.0f) // show bar targeting 2x zoomController.setZoomAnimationState(targetValue = null) // hide bar ``` ``` -------------------------------- ### Run Instrumentation Tests on Pixel 8 Emulator Source: https://github.com/google/jetpack-camera-app/blob/main/README.md Execute instrumentation tests on the Pixel 8 emulator (API 34) using this Gradle command. ```bash ./gradlew pixel8Api34StableDebugAndroidTest ``` -------------------------------- ### Persistent Settings CRUD Interface Source: https://context7.com/google/jetpack-camera-app/llms.txt Interface for managing persistent camera settings using DataStore. Exposes settings as a Flow and provides suspend functions for updates. ```kotlin interface SettingsRepository { val defaultCameraAppSettings: Flow suspend fun getCurrentDefaultCameraAppSettings(): CameraAppSettings suspend fun updateDefaultLensFacing(lensFacing: LensFacing) suspend fun updateDarkModeStatus(darkMode: DarkMode) // DarkMode.DARK / LIGHT / SYSTEM suspend fun updateFlashModeStatus(flashMode: FlashMode) suspend fun updateAspectRatio(aspectRatio: AspectRatio) suspend fun updateStreamConfig(streamConfig: StreamConfig) suspend fun updateLowLightBoostPriority(lowLightBoostPriority: LowLightBoostPriority) suspend fun updateStabilizationMode(stabilizationMode: StabilizationMode) suspend fun updateDynamicRange(dynamicRange: DynamicRange) suspend fun updateTargetFrameRate(targetFrameRate: Int) suspend fun updateImageFormat(imageFormat: ImageOutputFormat) suspend fun updateMaxVideoDuration(durationMillis: Long) suspend fun updateVideoQuality(videoQuality: VideoQuality) suspend fun updateAudioEnabled(isAudioEnabled: Boolean) } ``` ```kotlin // Reading the settings stream: settingsRepository.defaultCameraAppSettings .collect { settings -> val fps = settings.targetFrameRate // 0 = AUTO val quality = settings.videoQuality // UNSPECIFIED, SD, HD, FHD, UHD } ``` ```kotlin // Writing a setting (from a ViewModel scope): viewModelScope.launch { settingsRepository.updateFlashModeStatus(FlashMode.AUTO) settingsRepository.updateTargetFrameRate(TARGET_FPS_30) // 30 settingsRepository.updateStabilizationMode(StabilizationMode.HIGH_QUALITY) settingsRepository.updateMaxVideoDuration(60_000L) // 60 seconds settingsRepository.updateImageFormat(ImageOutputFormat.JPEG_ULTRA_HDR) } ``` -------------------------------- ### ZoomController Source: https://context7.com/google/jetpack-camera-app/llms.txt Manages zoom ratio and animation state for the camera. ```APIDOC ## ZoomController ### Description Manages zoom ratio and animation state for the camera. ### Methods - `setZoomRatio(zoomRatio: CameraZoomRatio)`: Sets the zoom ratio for the camera. - `setZoomAnimationState(targetValue: Float?)`: Sets the zoom animation state. Pass `null` to dismiss the zoom bar UI. ### Usage Examples ```kotlin // Pinch-to-zoom gesture handler in a Composable: Modifier.pointerInput(Unit) { detectTransformGestures { _, _, zoomChange, _ -> zoomController.setZoomRatio( CameraZoomRatio(ZoomStrategy.Scale(value = zoomChange)) ) } } ``` ``` -------------------------------- ### MediaRepository Source: https://context7.com/google/jetpack-camera-app/llms.txt Handles saving, deleting, and loading captured media (images and videos) in the post-capture workflow. ```APIDOC ## MediaRepository ### Description Handles saving, deleting, and loading captured images and videos in the post-capture workflow. ### Properties - `currentMedia: StateFlow`: A StateFlow emitting the currently selected media descriptor. ### Methods - `setCurrentMedia(pendingMedia: MediaDescriptor)`: Sets the current media descriptor. - `getLastCapturedMedia(): MediaDescriptor`: Retrieves the last captured media descriptor. - `deleteMedia(mediaDescriptor: MediaDescriptor.Content): Boolean`: Deletes the specified media content. Returns true if deletion was successful. - `copyToUri(mediaDescriptor: MediaDescriptor.Content, destinationUri: Uri)`: Copies the media content to a specified destination URI. - `saveToMediaStore(mediaDescriptor: MediaDescriptor.Content, outputFilename: String?): Uri?`: Saves the media content to the MediaStore. Returns the Uri of the saved media or null if saving failed. - `load(mediaDescriptor: MediaDescriptor): Media`: Loads the media content. Returns `Media.Image(bitmap)` or `Media.Video(uri)`. ### Usage Examples ```kotlin // After cache-and-review capture, save the cached image to MediaStore: val cachedDescriptor = MediaDescriptor.Content.Image(uri = cacheUri, thumbnail = null, isCached = true) val savedUri = mediaRepository.saveToMediaStore(cachedDescriptor, outputFilename = null) // Load for display in the review screen: when (val media = mediaRepository.load(cachedDescriptor)) { is Media.Image -> ImageBitmap(media.bitmap) is Media.Video -> VideoPlayer(media.uri) is Media.None, is Media.Error -> ShowError() } // Delete cached media: mediaRepository.deleteMedia(cachedDescriptor) ``` ``` -------------------------------- ### Bypass Git Pre-push Hook Source: https://github.com/google/jetpack-camera-app/blob/main/README.md Use the --no-verify flag with your push command to bypass the pre-push hook if necessary. ```bash git push origin --no-verify ``` -------------------------------- ### CameraSystemConstraints / CameraConstraints Source: https://context7.com/google/jetpack-camera-app/llms.txt Provides access to device camera hardware capabilities. It is populated after initialization and can be retrieved to query supported features like lenses, stabilization modes, frame rates, dynamic ranges, and zoom capabilities. ```APIDOC ## CameraSystemConstraints / CameraConstraints — Device capability queries `CameraSystemConstraints` describes what the device's camera hardware supports. It is populated by `CameraXCameraSystem` after initialization and distributed via `CameraSystem.getSystemConstraints()`. ```kotlin data class CameraSystemConstraints( val availableLenses: List, val concurrentCamerasSupported: Boolean, val perLensConstraints: Map ) data class CameraConstraints( val supportedStabilizationModes: Set, val supportedFixedFrameRates: Set, // e.g. {15, 30, 60} val supportedDynamicRanges: Set, // e.g. {SDR, HLG10} val supportedVideoQualitiesMap: Map>, val supportedImageFormatsMap: Map>, val supportedIlluminants: Set, // FLASH_UNIT, SCREEN val supportedFlashModes: Set, val supportedZoomRange: Range?, // e.g. Range(0.5f, 10f) val unsupportedStabilizationFpsMap: Map>, val supportedTestPatterns: Set ) // Usage: check if the back camera supports Ultra HDR in multi-stream mode val constraints = cameraSystem.getSystemConstraints().value val backConstraints = constraints?.perLensConstraints[LensFacing.BACK] val supportsUltraHdr = backConstraints ?.supportedImageFormatsMap[StreamConfig.MULTI_STREAM] ?.contains(ImageOutputFormat.JPEG_ULTRA_HDR) == true // Check MIME types per lens (useful for intent filter responses): val mimeTypes = constraints?.getSupportedMimeTypes() // e.g. { LensFacing.BACK: {"image/jpeg", "video/mp4"}, LensFacing.FRONT: {"image/jpeg"} } // Typical constraints for unit tests: val testConstraints = TYPICAL_SYSTEM_CONSTRAINTS // availableLenses=[FRONT,BACK], zoom=Range(0.5f,10f), fps={15,30}, flash={OFF,ON,AUTO} ``` ``` -------------------------------- ### CameraSystemConstraints and CameraConstraints Data Structures Source: https://context7.com/google/jetpack-camera-app/llms.txt These data classes describe the capabilities of the device's camera hardware. Use them to check for supported features like stabilization modes, dynamic ranges, and zoom capabilities. ```kotlin data class CameraSystemConstraints( val availableLenses: List, val concurrentCamerasSupported: Boolean, val perLensConstraints: Map ) data class CameraConstraints( val supportedStabilizationModes: Set, val supportedFixedFrameRates: Set, val supportedDynamicRanges: Set, val supportedVideoQualitiesMap: Map>, val supportedImageFormatsMap: Map>, val supportedIlluminants: Set, val supportedFlashModes: Set, val supportedZoomRange: Range?, val unsupportedStabilizationFpsMap: Map>, val supportedTestPatterns: Set ) ``` ```kotlin // Usage: check if the back camera supports Ultra HDR in multi-stream mode val constraints = cameraSystem.getSystemConstraints().value val backConstraints = constraints?.perLensConstraints[LensFacing.BACK] val supportsUltraHdr = backConstraints ?.supportedImageFormatsMap[StreamConfig.MULTI_STREAM] ?.contains(ImageOutputFormat.JPEG_ULTRA_HDR) == true ``` ```kotlin // Check MIME types per lens (useful for intent filter responses): val mimeTypes = constraints?.getSupportedMimeTypes() // e.g. { LensFacing.BACK: {"image/jpeg", "video/mp4"}, LensFacing.FRONT: {"image/jpeg"} } ``` ```kotlin // Typical constraints for unit tests: val testConstraints = TYPICAL_SYSTEM_CONSTRAINTS // availableLenses=[FRONT,BACK], zoom=Range(0.5f,10f), fps={15,30}, flash={OFF,ON,AUTO} ``` -------------------------------- ### Add Core Camera JNI Library Source: https://github.com/google/jetpack-camera-app/blob/main/core/camera/src/main/cpp/CMakeLists.txt Defines the shared library 'opengl_debug_lib' and specifies its source files. ```cmake add_library( opengl_debug_lib SHARED opengl_debug_jni.cpp jni_hooks.cpp ) ``` -------------------------------- ### CaptureController Source: https://context7.com/google/jetpack-camera-app/llms.txt UI-facing controller interface for issuing image and video capture commands. It provides an event channel for capture results and methods to control the capture process, including starting/stopping recordings and pausing. ```APIDOC ## CaptureController — Image and video capture commands UI-facing controller interface (implemented by `CaptureControllerImpl` in `:ui:controller:impl`) for issuing capture commands from the Compose preview screen. ```kotlin interface CaptureController { val captureEvents: ReceiveChannel fun captureImage(contentResolver: ContentResolver) fun startVideoRecording() fun stopVideoRecording() fun setLockedRecording(isLocked: Boolean) // hands-free recording lock fun setPaused(shouldBePaused: Boolean) fun setAudioEnabled(shouldEnableAudio: Boolean) } // Example: trigger a photo capture from a button click handler Button(onClick = { captureController.captureImage(context.contentResolver) }) { Text("Take Photo") } // Example: start / stop video with audio toggle captureController.setAudioEnabled(true) captureController.startVideoRecording() // ... user taps stop ... captureController.stopVideoRecording() // Collect results asynchronously: LaunchedEffect(Unit) { for (event in captureController.captureEvents) { if (event is VideoCaptureEvent.VideoSaved) { Log.d("JCA", "Video saved to ${event.savedUri}") } } } ``` ``` -------------------------------- ### ExternalCaptureMode Source: https://context7.com/google/jetpack-camera-app/llms.txt Enum representing different intent-driven launch modes for the camera app. ```APIDOC ## ExternalCaptureMode ### Description Enum parsed from the launch `Intent` action in `MainActivity`, controlling the UX flow and capture constraints. ### Enum Values - `Standard`: Default app launch, free photo/video toggle. - `ImageCapture`: `ACTION_IMAGE_CAPTURE` - single photo, returns URI. - `VideoCapture`: `ACTION_VIDEO_CAPTURE` - single video, returns URI. - `MultipleImageCapture`: `INTENT_ACTION_STILL_IMAGE_CAMERA` - sequential photos. ### Usage Examples ```kotlin // Launching JCA for single image capture from another app: val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE).apply { putExtra(MediaStore.EXTRA_OUTPUT, outputUri) // where to save the image } startActivityForResult(intent, REQUEST_IMAGE_CAPTURE) // Launching for video capture: val intent = Intent(MediaStore.ACTION_VIDEO_CAPTURE).apply { putExtra(MediaStore.EXTRA_OUTPUT, outputUri) } startActivityForResult(intent, REQUEST_VIDEO_CAPTURE) // Launching with debug mode and single-lens restriction: val intent = Intent(context, MainActivity::class.java).apply { putExtra("KEY_DEBUG_MODE", true) putExtra("KEY_DEBUG_SINGLE_LENS_MODE", "back") // "back" or "front" } startActivity(intent) // Launching with cache-and-review workflow: val intent = Intent(context, MainActivity::class.java).apply { putExtra("KEY_REVIEW_AFTER_CAPTURE", true) } startActivity(intent) ``` ``` -------------------------------- ### CameraXResetRule for CameraX State Reset Source: https://context7.com/google/jetpack-camera-app/llms.txt This rule resets CameraX state between tests that call configureInstance(). Ensure your test class includes this rule. ```kotlin // CameraXResetRule: reset CameraX state between tests that call configureInstance(): class MyTest { @get:Rule val cameraXResetRule = CameraXResetRule() @Test fun myTest() { /* CameraX is freshly initialized before this test */ } } ```