Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
29b087e
Refactor test fakes into dedicated testing modules
temcguir Apr 20, 2026
184da5e
Correct copyright year in new build files
temcguir Apr 20, 2026
cc85dcf
Correct copyright year to 2026 in new build files
temcguir Apr 20, 2026
3a9fc87
Implement fake controllers with unit tests in dedicated testing module
temcguir Apr 21, 2026
08e9084
Address code review comments: add explicit test runners and simplify …
temcguir Apr 21, 2026
b931f60
Standardize build toolchain to jvmToolchain(17) in postcapture modules
temcguir Apr 21, 2026
eceb1d0
Implement high-fidelity camera startup emulation in FakeCameraSystem
temcguir Apr 21, 2026
2fb597e
Address code review comments for FakeCaptureController and FakeSnackB…
temcguir Apr 22, 2026
7f5a834
Merge branch 'temcguir/fake_controllers' into temcguir/fake_camera_sy…
temcguir Apr 22, 2026
5e99e5b
Fix CameraXCameraSystemTest build error after PreviewSurfaceRequest c…
temcguir Apr 22, 2026
9d43e46
Address code review: Make PreviewSurfaceRequest.CameraX a data class
temcguir Apr 22, 2026
03cccdf
Address code review: Make PreviewSurfaceRequest.Viewfinder a data class
temcguir Apr 22, 2026
70f407d
Address code review: Derive FakeCameraSystem surface resolution from …
temcguir Apr 22, 2026
ab983c7
Address code review: Set isCameraRunning to false while waiting for s…
temcguir Apr 22, 2026
f935fa1
Address code review: Extract preview gestures into a reusable Modifier
temcguir Apr 22, 2026
cf572fa
Merge branch 'main' into temcguir/fake_controllers
temcguir Apr 23, 2026
efa6b4d
Merge branch 'main' into temcguir/fake_controllers
temcguir Apr 29, 2026
b681e01
Merge branch 'temcguir/fake_controllers' into temcguir/fake_camera_sy…
temcguir Apr 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/camera/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ dependencies {
androidTestImplementation(libs.androidx.rules)
androidTestImplementation(libs.kotlinx.coroutines.test)
androidTestImplementation(libs.truth)
androidTestImplementation(project(":core:common:testing"))
testImplementation(project(":core:camera:testing"))

// Futures
implementation(libs.futures.ktx)
Expand All @@ -124,6 +126,7 @@ dependencies {
// CameraX
implementation(libs.camera.core)
implementation(libs.camera.camera2)
implementation(libs.camera.viewfinder.core)
implementation(libs.camera.lifecycle)
implementation(libs.camera.video)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import com.google.jetpackcamera.core.camera.postprocess.ImagePostProcessorFeatur
import com.google.jetpackcamera.core.camera.postprocess.PostProcessModule.Companion.provideImagePostProcessorMap
import com.google.jetpackcamera.core.camera.utils.APP_REQUIRED_PERMISSIONS
import com.google.jetpackcamera.core.camera.utils.provideUpdatingSurface
import com.google.jetpackcamera.core.common.FakeFilePathGenerator
import com.google.jetpackcamera.core.common.testing.FakeFilePathGenerator
import com.google.jetpackcamera.model.CaptureMode
import com.google.jetpackcamera.model.FlashMode
import com.google.jetpackcamera.model.Illuminant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ private fun createPreviewUseCase(
}.build()
.apply {
setSurfaceProvider { surfaceRequest ->
surfaceRequests.update { surfaceRequest }
surfaceRequests.update { PreviewSurfaceRequest.CameraX(surfaceRequest) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package com.google.jetpackcamera.core.camera

import android.content.Context
import androidx.camera.core.SurfaceRequest
import androidx.camera.lifecycle.ProcessCameraProvider
import com.google.jetpackcamera.core.camera.lowlight.LowLightBoostEffectProvider
import com.google.jetpackcamera.core.common.FilePathGenerator
Expand All @@ -41,7 +40,7 @@ internal data class CameraSessionContext(
val focusMeteringEvents: Channel<CameraEvent.FocusMeteringEvent>,
val videoCaptureControlEvents: Channel<VideoCaptureControlEvent>,
val currentCameraState: MutableStateFlow<CameraState>,
val surfaceRequests: MutableStateFlow<SurfaceRequest?>,
val surfaceRequests: MutableStateFlow<PreviewSurfaceRequest?>,
val transientSettings: StateFlow<TransientSessionSettings?>,
val lowLightBoostEffectProvider: LowLightBoostEffectProvider? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,12 @@ interface CameraSystem {

fun getCurrentCameraState(): StateFlow<CameraState>

fun getSurfaceRequest(): StateFlow<SurfaceRequest?>
/**
* Returns the current [PreviewSurfaceRequest].
*
* This will be null if no surface is currently requested.
*/
fun getSurfaceRequest(): StateFlow<PreviewSurfaceRequest?>

fun getScreenFlashEvents(): ReceiveChannel<ScreenFlashEvent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import androidx.camera.core.CameraXConfig
import androidx.camera.core.DynamicRange as CXDynamicRange
import androidx.camera.core.ImageCapture
import androidx.camera.core.ImageCapture.OutputFileOptions
import androidx.camera.core.SurfaceRequest
import androidx.camera.core.takePicture
import androidx.camera.lifecycle.ExperimentalCameraProviderConfiguration
import androidx.camera.lifecycle.ProcessCameraProvider
Expand Down Expand Up @@ -135,9 +134,10 @@ constructor(
private var currentCameraState = MutableStateFlow(CameraState())
override fun getCurrentCameraState(): StateFlow<CameraState> = currentCameraState.asStateFlow()

private val _surfaceRequest = MutableStateFlow<SurfaceRequest?>(null)
private val _surfaceRequest = MutableStateFlow<PreviewSurfaceRequest?>(null)

override fun getSurfaceRequest(): StateFlow<SurfaceRequest?> = _surfaceRequest.asStateFlow()
override fun getSurfaceRequest(): StateFlow<PreviewSurfaceRequest?> =
_surfaceRequest.asStateFlow()

private val lowLightBoostAvailabilityChecker: LowLightBoostAvailabilityChecker?
private val lowLightBoostEffectProvider: LowLightBoostEffectProvider?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ internal suspend fun CameraSessionContext.processFocusMeteringEvents(
cameraInfo: CameraInfo,
cameraControl: CameraControl
) {
surfaceRequests.flatMapLatest { surfaceRequest ->
surfaceRequests.flatMapLatest { previewSurfaceRequest ->
val surfaceRequest =
(previewSurfaceRequest as? PreviewSurfaceRequest.CameraX)?.surfaceRequest
surfaceRequest?.let { request ->
Log.d(
TAG,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.jetpackcamera.core.camera

import android.view.Surface
import androidx.camera.core.SurfaceRequest
import androidx.camera.viewfinder.core.ViewfinderSurfaceRequest
import kotlinx.coroutines.CompletableDeferred

/**
* A sealed interface representing a request for a preview surface, abstracting the specific
* viewfinder implementation required.
*/
sealed interface PreviewSurfaceRequest {
/**
* Wraps a CameraX [SurfaceRequest] for the production CameraXViewfinder.
*
* @property surfaceRequest The CameraX [SurfaceRequest].
*/
class CameraX(val surfaceRequest: SurfaceRequest) : PreviewSurfaceRequest
Comment thread
temcguir marked this conversation as resolved.
Outdated

/**
* Wraps a [ViewfinderSurfaceRequest] for the standalone Viewfinder composable.
*
* @property surfaceRequest The standalone [ViewfinderSurfaceRequest].
* @property surfaceDeferred A [CompletableDeferred] that will be completed with the [Surface]
* once provided by the UI.
*/
class Viewfinder(
val surfaceRequest: ViewfinderSurfaceRequest,
val surfaceDeferred: CompletableDeferred<Surface> = CompletableDeferred()
) : PreviewSurfaceRequest
Comment thread
temcguir marked this conversation as resolved.
Outdated
}

This file was deleted.

64 changes: 64 additions & 0 deletions core/camera/testing/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (C) 2026 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

plugins {
alias(libs.plugins.android.library)
alias(libs.plugins.kotlin.android)
}

android {
namespace = "com.google.jetpackcamera.core.camera.testing"
compileSdk = libs.versions.compileSdk.get().toInt()

defaultConfig {
minSdk = libs.versions.minSdk.get().toInt()
testOptions.targetSdk = libs.versions.targetSdk.get().toInt()
lint.targetSdk = libs.versions.targetSdk.get().toInt()

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}

flavorDimensions += "flavor"
productFlavors {
create("stable") {
dimension = "flavor"
isDefault = true
}
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlin {
jvmToolchain(17)
}
}

dependencies {
implementation(project(":core:camera"))
implementation(project(":core:model"))
implementation(project(":data:settings"))

implementation(libs.camera.core)
implementation(libs.camera.viewfinder.core)
implementation(libs.kotlinx.coroutines.core)
// Testing
testImplementation(libs.junit)
testImplementation(libs.truth)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.robolectric)
}
Loading
Loading