Skip to content

Commit ff8bba9

Browse files
committed
More updates
1 parent f588606 commit ff8bba9

9 files changed

Lines changed: 676 additions & 39 deletions

File tree

‎app/src/main/java/com/android/camera/catalog/domain/SampleCatalog.kt‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616
package com.android.camera.catalog.domain
1717

18-
import com.android.camerax.takeaphoto.CameraXTakeAPhoto
18+
import com.android.camerax.takeaphoto.CameraXTakeAPhotoScreen
1919
import android.Manifest
2020
import androidx.annotation.DrawableRes
2121
import androidx.annotation.RequiresPermission
@@ -24,6 +24,7 @@ import androidx.compose.runtime.Composable
2424
import androidx.compose.ui.graphics.Color
2525
import com.android.ai.theme.extendedColorScheme
2626
import com.android.camera.catalog.R
27+
import com.android.camera2.takeaphoto.Camera2TakeAPhotoScreen
2728
import com.android.camera2.takeavideo.Camera2TakeAVideoScreen
2829
import com.google.firebase.ai.type.PublicPreviewAPI
2930

@@ -33,8 +34,8 @@ val sampleCatalog = listOf(
3334
SampleCatalogItem(
3435
title = R.string.camerax_takeaphoto_list_title,
3536
description = R.string.camerax_takeaphoto_list_description,
36-
route = "CameraXTakeAPhoto",
37-
sampleEntryScreen = { CameraXTakeAPhoto() },
37+
route = "CameraXTakeAPhotoScreen",
38+
sampleEntryScreen = { CameraXTakeAPhotoScreen() },
3839
type = SampleType.CAMERAX,
3940
tags = listOf(),
4041
isFeatured = true,
@@ -43,7 +44,7 @@ val sampleCatalog = listOf(
4344
title = R.string.camera2_sample_takeaphoto_list_title,
4445
description = R.string.camera2_sample_takeaphoto_list_description,
4546
route = "Camera2TakeAPhotoScreen",
46-
sampleEntryScreen = { Camera2TakeAVideoScreen() },
47+
sampleEntryScreen = { Camera2TakeAPhotoScreen() },
4748
type = SampleType.CAMERA2,
4849
tags = listOf(SampleTags.MEDIA3, SampleTags.ML_KIT),
4950
isFeatured = false,

‎gradle/libs.versions.toml‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
[versions]
22
agp = "9.1.0"
3+
cameraCore = "1.5.3"
34
coilCompose = "3.4.0"
45
firebaseBom = "34.11.0"
56
lifecycleRuntimeCompose = "2.10.0"
@@ -32,6 +33,10 @@ cameraViewfinder = "1.5.3"
3233
kotlinxCoroutinesGuava = "1.10.2"
3334

3435
[libraries]
36+
androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "cameraCore" }
37+
androidx-camera-compose = { module = "androidx.camera:camera-compose", version.ref = "cameraCore" }
38+
androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "cameraCore" }
39+
androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "cameraCore" }
3540
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
3641
androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycleRuntimeCompose" }
3742
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }

‎samples/camera2-takeaphoto/build.gradle.kts‎

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,29 +59,26 @@ kotlin {
5959
}
6060

6161
dependencies {
62-
62+
// Kotlin & Core
6363
implementation(libs.androidx.core.ktx)
64-
implementation(libs.androidx.appcompat)
65-
implementation(libs.androidx.material3)
66-
implementation(libs.androidx.activity.compose)
64+
implementation(libs.kotlinx.coroutines.guava)
65+
66+
// Compose core components
6767
implementation(platform(libs.androidx.compose.bom))
68+
implementation(libs.androidx.activity.compose)
69+
implementation(libs.androidx.material3)
6870
implementation(libs.androidx.material.icons.extended)
69-
implementation(platform(libs.firebase.bom))
70-
implementation(libs.firebase.ai)
71-
implementation(libs.hilt.android)
72-
implementation(libs.hilt.navigation.compose)
73-
implementation(libs.coil.compose)
74-
implementation(libs.androidx.runtime.livedata)
75-
implementation(libs.androidx.material3.window)
71+
72+
// Camera Viewfinder dependencies
7673
implementation(libs.androidx.camera.viewfinder.compose)
7774
implementation(libs.androidx.camera.viewfinder.core)
7875
implementation(libs.androidx.camera.viewfinder.view)
79-
implementation(libs.kotlinx.coroutines.guava)
80-
81-
debugImplementation(libs.ui.tooling)
76+
77+
// Hilt / Architecture
78+
implementation(libs.hilt.android)
79+
implementation(libs.hilt.navigation.compose)
8280
ksp(libs.hilt.compiler)
81+
82+
// UI Component
8383
implementation(project(":ui-component"))
84-
testImplementation(libs.junit)
85-
androidTestImplementation(libs.androidx.junit)
86-
androidTestImplementation(libs.androidx.espresso.core)
87-
}
84+
}

‎samples/camerax-takeaphoto/build.gradle.kts‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,23 @@
1+
/*
2+
* Copyright 2026 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
117
plugins {
218
alias(libs.plugins.android.library)
319
alias(libs.plugins.jetbrains.kotlin.android)
20+
alias(libs.plugins.ksp)
421
alias(libs.plugins.compose.compiler)
522
}
623

@@ -42,10 +59,19 @@ kotlin {
4259

4360
dependencies {
4461
implementation(libs.androidx.core.ktx)
45-
implementation(libs.androidx.appcompat)
4662
implementation(libs.androidx.material3)
4763
implementation(libs.androidx.activity.compose)
4864
implementation(platform(libs.androidx.compose.bom))
4965
implementation(libs.androidx.material.icons.extended)
66+
implementation(libs.hilt.android)
67+
implementation(libs.hilt.navigation.compose)
68+
implementation(libs.androidx.camera.core)
69+
implementation(libs.androidx.camera.camera2)
70+
implementation(libs.androidx.camera.lifecycle)
71+
implementation(libs.androidx.camera.compose)
72+
implementation(libs.kotlinx.coroutines.guava)
73+
74+
ksp(libs.hilt.compiler)
75+
5076
implementation(project(":ui-component"))
51-
}
77+
}

‎samples/camerax-takeaphoto/src/main/java/com/android/camerax/takeaphoto/CameraXTakeAPhoto.kt‎

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2026 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.android.camerax.takeaphoto
17+
18+
import android.content.Context
19+
import android.util.Log
20+
import androidx.camera.core.CameraControl
21+
import androidx.camera.core.CameraSelector
22+
import androidx.camera.core.FocusMeteringAction
23+
import androidx.camera.core.ImageCapture
24+
import androidx.camera.core.ImageCaptureException
25+
import androidx.camera.core.ImageProxy
26+
import androidx.camera.core.Preview
27+
import androidx.camera.core.SurfaceOrientedMeteringPointFactory
28+
import androidx.camera.core.SurfaceRequest
29+
import androidx.camera.lifecycle.ProcessCameraProvider
30+
import androidx.compose.runtime.Composable
31+
import androidx.compose.runtime.Stable
32+
import androidx.compose.runtime.getValue
33+
import androidx.compose.runtime.mutableStateOf
34+
import androidx.compose.runtime.remember
35+
import androidx.compose.runtime.rememberCoroutineScope
36+
import androidx.compose.runtime.setValue
37+
import androidx.compose.ui.geometry.Offset
38+
import androidx.core.content.ContextCompat
39+
import androidx.lifecycle.LifecycleOwner
40+
import kotlinx.coroutines.CoroutineScope
41+
import kotlinx.coroutines.launch
42+
import java.util.concurrent.ExecutorService
43+
import java.util.concurrent.Executors
44+
45+
private const val TAG = "CameraXController"
46+
47+
@Composable
48+
fun rememberCameraXTakeAPhotoController(
49+
context: Context,
50+
lifecycleOwner: LifecycleOwner,
51+
isFrontCamera: Boolean,
52+
onPhotoCaptured: (ImageProxy) -> Unit
53+
): CameraXTakeAPhotoController {
54+
val coroutineScope = rememberCoroutineScope()
55+
return remember(context, isFrontCamera, onPhotoCaptured) {
56+
CameraXTakeAPhotoController(context, lifecycleOwner, isFrontCamera, onPhotoCaptured, coroutineScope)
57+
}
58+
}
59+
60+
@Stable
61+
class CameraXTakeAPhotoController(
62+
private val context: Context,
63+
private val lifecycleOwner: LifecycleOwner,
64+
val isFrontCamera: Boolean,
65+
private val onPhotoCaptured: (ImageProxy) -> Unit,
66+
private val coroutineScope: CoroutineScope
67+
) {
68+
var surfaceRequest: SurfaceRequest? by mutableStateOf(null)
69+
private set
70+
71+
private var cameraControl: CameraControl? = null
72+
private var cameraProvider: ProcessCameraProvider? = null
73+
74+
private val cameraExecutor: ExecutorService = Executors.newSingleThreadExecutor()
75+
76+
private val imageCapture = ImageCapture.Builder()
77+
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
78+
.build()
79+
80+
private val preview = Preview.Builder().build().apply {
81+
setSurfaceProvider { request ->
82+
surfaceRequest = request
83+
}
84+
}
85+
86+
fun openCamera() {
87+
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
88+
cameraProviderFuture.addListener({
89+
val provider = cameraProviderFuture.get()
90+
cameraProvider = provider
91+
92+
val lensFacing = if (isFrontCamera) {
93+
CameraSelector.LENS_FACING_FRONT
94+
} else {
95+
CameraSelector.LENS_FACING_BACK
96+
}
97+
98+
val cameraSelector = CameraSelector.Builder()
99+
.requireLensFacing(lensFacing)
100+
.build()
101+
102+
try {
103+
provider.unbindAll()
104+
val camera = provider.bindToLifecycle(
105+
lifecycleOwner, cameraSelector, preview, imageCapture
106+
)
107+
cameraControl = camera.cameraControl
108+
} catch (exc: Exception) {
109+
Log.e(TAG, "Use case binding failed", exc)
110+
}
111+
}, ContextCompat.getMainExecutor(context))
112+
}
113+
114+
fun updateTargetRotation(rotation: Int) {
115+
imageCapture.targetRotation = rotation
116+
preview.targetRotation = rotation
117+
}
118+
119+
fun takePicture() {
120+
imageCapture.takePicture(
121+
cameraExecutor,
122+
object : ImageCapture.OnImageCapturedCallback() {
123+
override fun onCaptureSuccess(image: ImageProxy) {
124+
coroutineScope.launch {
125+
onPhotoCaptured(image)
126+
}
127+
}
128+
129+
override fun onError(exception: ImageCaptureException) {
130+
Log.e(TAG, "Photo capture failed: \${exception.message}", exception)
131+
}
132+
}
133+
)
134+
}
135+
136+
fun focus(surfaceCoords: Offset, width: Float, height: Float) {
137+
val factory = SurfaceOrientedMeteringPointFactory(width, height)
138+
val point = factory.createPoint(surfaceCoords.x, surfaceCoords.y)
139+
val action = FocusMeteringAction.Builder(point, FocusMeteringAction.FLAG_AF).build()
140+
cameraControl?.startFocusAndMetering(action)
141+
}
142+
143+
fun closeCamera() {
144+
cameraProvider?.unbindAll()
145+
}
146+
147+
fun release() {
148+
closeCamera()
149+
cameraExecutor.shutdown()
150+
}
151+
}

0 commit comments

Comments
 (0)