From fa22c04845d1bb8edca97f7a9d0d4f3424f9b3d4 Mon Sep 17 00:00:00 2001 From: Kimberly Crevecoeur Date: Wed, 19 Jul 2023 18:58:22 +0000 Subject: [PATCH] implement tap to focus --- camera-viewfinder-compose/build.gradle | 3 +++ .../jetpackcamera/viewfinder/CameraPreview.kt | 13 +++++++--- .../viewfinder/surface/CombinedSurface.kt | 7 ++++- .../viewfinder/surface/Texture.kt | 8 ++++-- .../domain/camera/CameraUseCase.kt | 4 +++ .../domain/camera/CameraXCameraUseCase.kt | 26 +++++++++++++++++++ .../domain/camera/test/FakeCameraUseCase.kt | 11 ++++++++ .../feature/preview/PreviewScreen.kt | 23 +++++++++++++++- .../feature/preview/PreviewViewModel.kt | 11 ++++++++ 9 files changed, 98 insertions(+), 8 deletions(-) diff --git a/camera-viewfinder-compose/build.gradle b/camera-viewfinder-compose/build.gradle index 55c45b27f..69bd09c98 100644 --- a/camera-viewfinder-compose/build.gradle +++ b/camera-viewfinder-compose/build.gradle @@ -42,6 +42,9 @@ dependencies { implementation composeBom androidTestImplementation composeBom + // Compose - Material Design 3 + implementation 'androidx.compose.material3:material3' + // Compose - Testing androidTestImplementation "androidx.compose.ui:ui-test-junit4" diff --git a/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/CameraPreview.kt b/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/CameraPreview.kt index a55bdb9c0..32ee98e6f 100644 --- a/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/CameraPreview.kt +++ b/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/CameraPreview.kt @@ -19,9 +19,7 @@ package com.google.jetpackcamera.viewfinder import android.graphics.Bitmap import android.util.Log import android.view.Surface -import com.google.jetpackcamera.viewfinder.surface.CombinedSurface -import com.google.jetpackcamera.viewfinder.surface.CombinedSurfaceEvent -import com.google.jetpackcamera.viewfinder.surface.SurfaceType +import android.view.View import androidx.camera.core.Preview.SurfaceProvider import androidx.camera.core.SurfaceRequest import androidx.camera.view.PreviewView.ImplementationMode @@ -34,6 +32,9 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier +import com.google.jetpackcamera.viewfinder.surface.CombinedSurface +import com.google.jetpackcamera.viewfinder.surface.CombinedSurfaceEvent +import com.google.jetpackcamera.viewfinder.surface.SurfaceType import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor import kotlinx.coroutines.flow.mapNotNull @@ -45,7 +46,8 @@ fun CameraPreview( modifier: Modifier, implementationMode: ImplementationMode = ImplementationMode.COMPATIBLE, onSurfaceProviderReady: (SurfaceProvider) -> Unit = {}, - onRequestBitmapReady: (() -> Bitmap?) -> Unit + onRequestBitmapReady: (() -> Bitmap?) -> Unit, + setSurfaceView: (View) -> Unit ) { Log.d(TAG, "CameraPreview") @@ -58,6 +60,7 @@ fun CameraPreview( PreviewSurface( surfaceRequest = surfaceRequest, + setView = setSurfaceView ) } @@ -68,6 +71,7 @@ fun PreviewSurface( // onRequestBitmapReady: (() -> Bitmap?) -> Unit, type: SurfaceType = SurfaceType.TEXTURE_VIEW, implementationMode: ImplementationMode = ImplementationMode.COMPATIBLE, + setView: (View) -> Unit ) { Log.d(TAG, "PreviewSurface") @@ -89,6 +93,7 @@ fun PreviewSurface( when (implementationMode) { ImplementationMode.PERFORMANCE -> TODO() ImplementationMode.COMPATIBLE -> CombinedSurface( + setView = setView, onSurfaceEvent = { event -> surface = when (event) { is CombinedSurfaceEvent.SurfaceAvailable -> { diff --git a/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/CombinedSurface.kt b/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/CombinedSurface.kt index 5df4366b6..6ff15bb6e 100644 --- a/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/CombinedSurface.kt +++ b/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/CombinedSurface.kt @@ -19,6 +19,7 @@ package com.google.jetpackcamera.viewfinder.surface import android.graphics.Bitmap import android.util.Log import android.view.Surface +import android.view.View import androidx.compose.runtime.Composable import com.google.jetpackcamera.viewfinder.surface.SurfaceType.* @@ -29,6 +30,7 @@ fun CombinedSurface( onSurfaceEvent: (CombinedSurfaceEvent) -> Unit, onRequestBitmapReady: (() -> Bitmap?) -> Unit = {}, type: SurfaceType = TEXTURE_VIEW, + setView: (View) -> Unit ) { Log.d(TAG, "PreviewTexture") @@ -46,10 +48,12 @@ fun CombinedSurface( is SurfaceHolderEvent.SurfaceChanged -> { // TODO(yasith@) } + } } TEXTURE_VIEW -> Texture( + { when (it) { is SurfaceTextureEvent.SurfaceTextureAvailable -> { @@ -70,7 +74,8 @@ fun CombinedSurface( } true }, - onRequestBitmapReady + onRequestBitmapReady, + setView = setView ) } } diff --git a/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/Texture.kt b/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/Texture.kt index ab6c4ab5e..9741c0307 100644 --- a/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/Texture.kt +++ b/camera-viewfinder-compose/src/main/java/com/google/jetpackcamera/viewfinder/surface/Texture.kt @@ -20,6 +20,7 @@ import android.graphics.Bitmap import android.graphics.SurfaceTexture import android.util.Log import android.view.TextureView +import android.view.View import android.view.ViewGroup import android.widget.LinearLayout import androidx.compose.runtime.Composable @@ -34,8 +35,9 @@ private const val TAG = "Texture" @Composable fun Texture( onSurfaceTextureEvent: (SurfaceTextureEvent) -> Boolean = { _ -> true }, - onRequestBitmapReady: (() -> Bitmap?) -> Unit -) { + onRequestBitmapReady: (() -> Bitmap?) -> Unit, + setView: (View) -> Unit, + ) { Log.d(TAG, "Texture") var textureView: TextureView? by remember { mutableStateOf(null) } @@ -91,9 +93,11 @@ fun Texture( } }, update = { textureView = it + setView(textureView!!) onRequestBitmapReady { -> it.bitmap } } ) + } sealed interface SurfaceTextureEvent { diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraUseCase.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraUseCase.kt index 86184c862..04cdf9908 100644 --- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraUseCase.kt +++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraUseCase.kt @@ -16,6 +16,7 @@ package com.google.jetpackcamera.domain.camera +import android.view.Display import androidx.camera.core.Preview import com.google.jetpackcamera.settings.model.CameraAppSettings import com.google.jetpackcamera.settings.model.FlashModeStatus @@ -51,8 +52,11 @@ interface CameraUseCase { fun setZoomScale(scale: Float): Float fun setFlashMode(flashModeStatus: FlashModeStatus) + suspend fun flipCamera(isFrontFacing: Boolean) + fun tapToFocus(display: Display, surfaceWidth: Int, surfaceHeight: Int, x:Float, y:Float) + companion object { const val INVALID_ZOOM_SCALE = -1f } diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt index 2bbe4fed8..8ee3a0ae1 100644 --- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt +++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/CameraXCameraUseCase.kt @@ -21,9 +21,12 @@ import android.content.ContentValues import android.provider.MediaStore import android.util.Log import android.util.Rational +import android.view.Display import androidx.camera.core.Camera import androidx.camera.core.CameraSelector import androidx.camera.core.CameraSelector.LensFacing +import androidx.camera.core.DisplayOrientedMeteringPointFactory +import androidx.camera.core.FocusMeteringAction import androidx.camera.core.ImageCapture import androidx.camera.core.ImageCaptureException import androidx.camera.core.ImageProxy @@ -189,6 +192,29 @@ class CameraXCameraUseCase @Inject constructor( ) } + override fun tapToFocus( + display: Display, + surfaceWidth: Int, + surfaceHeight: Int, + x: Float, + y: Float + ) { + if (camera != null) { + val meteringPoint = DisplayOrientedMeteringPointFactory( + display, + camera!!.cameraInfo, + surfaceWidth.toFloat(), + surfaceHeight.toFloat() + ) + .createPoint(x, y); + + val action = FocusMeteringAction.Builder(meteringPoint).build() + + camera!!.cameraControl.startFocusAndMetering(action) + Log.d(TAG, "Tap to focus on: $meteringPoint") + } + } + override fun setFlashMode(flashModeStatus: FlashModeStatus) { imageCaptureUseCase.flashMode = when (flashModeStatus) { FlashModeStatus.OFF -> ImageCapture.FLASH_MODE_OFF // 2 diff --git a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/test/FakeCameraUseCase.kt b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/test/FakeCameraUseCase.kt index 87d543a4e..60d656e25 100644 --- a/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/test/FakeCameraUseCase.kt +++ b/domain/camera/src/main/java/com/google/jetpackcamera/domain/camera/test/FakeCameraUseCase.kt @@ -16,6 +16,7 @@ package com.google.jetpackcamera.domain.camera.test +import android.view.Display import androidx.camera.core.CameraSelector import androidx.camera.core.Preview import com.google.jetpackcamera.domain.camera.CameraUseCase @@ -89,4 +90,14 @@ class FakeCameraUseCase : CameraUseCase { override suspend fun flipCamera(isFrontFacing: Boolean) { isLensFacingFront = isFrontFacing } + + override fun tapToFocus( + display: Display, + surfaceWidth: Int, + surfaceHeight: Int, + x: Float, + y: Float + ) { + TODO("Not yet implemented") + } } \ No newline at end of file diff --git a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewScreen.kt b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewScreen.kt index 5ea4fadba..def1a058e 100644 --- a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewScreen.kt +++ b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewScreen.kt @@ -19,6 +19,7 @@ package com.google.jetpackcamera.feature.preview import android.os.Handler import android.os.Looper import android.util.Log +import android.view.View import androidx.camera.core.Preview.SurfaceProvider import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween @@ -48,6 +49,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color @@ -69,6 +71,7 @@ private const val TAG = "PreviewScreen" /** * Screen used for the Preview feature. */ +@OptIn(ExperimentalComposeUiApi::class) @Composable fun PreviewScreen( onNavigateToSettings: () -> Unit, @@ -85,6 +88,7 @@ fun PreviewScreen( Log.d(TAG, "onSurfaceProviderReady") deferredSurfaceProvider.complete(it) } + lateinit var viewInfo: View var zoomScale by remember { mutableStateOf(1f) } var zoomScaleShow by remember { mutableStateOf(false) } val zoomHandler = Handler(Looper.getMainLooper()) @@ -118,9 +122,23 @@ fun PreviewScreen( onDoubleTap = { offset -> Log.d(TAG, "onDoubleTap $offset") viewModel.flipCamera() + }, + onTap = { offset -> + try { + viewModel.tapToFocus( + viewInfo.display, + viewInfo.width, + viewInfo.height, + offset.x, offset.y + ) + Log.d(TAG, "onTap $offset") + } catch (e: UninitializedPropertyAccessException) { + Log.d(TAG, "onTap $offset") + e.printStackTrace() + } } ) - } + }, ) { CameraPreview( modifier = Modifier @@ -128,6 +146,9 @@ fun PreviewScreen( onSurfaceProviderReady = onSurfaceProviderReady, onRequestBitmapReady = { val bitmap = it.invoke() + }, + setSurfaceView = { s: View -> + viewInfo = s } ) diff --git a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewViewModel.kt b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewViewModel.kt index 641d04f5e..f6aab7011 100644 --- a/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewViewModel.kt +++ b/feature/preview/src/main/java/com/google/jetpackcamera/feature/preview/PreviewViewModel.kt @@ -17,6 +17,7 @@ package com.google.jetpackcamera.feature.preview import android.util.Log +import android.view.Display import androidx.camera.core.ImageCaptureException import androidx.camera.core.Preview.SurfaceProvider import androidx.lifecycle.ViewModel @@ -208,4 +209,14 @@ class PreviewViewModel @Inject constructor( ) } } + + fun tapToFocus(display: Display, surfaceWidth: Int, surfaceHeight: Int, x: Float, y: Float) { + cameraUseCase.tapToFocus( + display = display, + surfaceWidth = surfaceWidth, + surfaceHeight = surfaceHeight, + x = x, + y = y + ) + } } \ No newline at end of file