From 28ea0e1eb0fcb5c05e6c6ed604f7a97ec9b79ca4 Mon Sep 17 00:00:00 2001 From: Kimberly Crevecoeur Date: Mon, 22 Jan 2024 17:42:40 -0800 Subject: Fix issue where UiAutomator cannot find components (#100) * Fix issue where UiAutomator cannot find components * spotless --- app/build.gradle.kts | 1 - .../jetpackcamera/feature/preview/PreviewScreen.kt | 374 +++++++++++---------- .../feature/quicksettings/QuickSettingsScreen.kt | 7 +- 3 files changed, 192 insertions(+), 190 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 274e616..1c27e4d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -73,7 +73,6 @@ android { } dependencies { - implementation("androidx.test.ext:junit-ktx:1.1.5") // Compose val composeBom = platform(libs.compose.bom) implementation(composeBom) 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 5aeb5c0..d8d90b2 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 @@ -22,6 +22,7 @@ import android.util.Log import androidx.camera.core.Preview.SurfaceProvider import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row @@ -128,217 +129,224 @@ fun PreviewScreen( Text(text = stringResource(R.string.camera_not_ready), color = Color.White) } } else if (previewUiState.cameraState == CameraState.READY) { - // display camera feed. this stays behind everything else - PreviewDisplay( - onFlipCamera = viewModel::flipCamera, - onTapToFocus = viewModel::tapToFocus, - onZoomChange = { zoomChange: Float -> - viewModel.setZoomScale(zoomChange) - zoomScaleShow = true - zoomHandler.postDelayed({ zoomScaleShow = false }, ZOOM_SCALE_SHOW_TIMEOUT_MS) - }, - aspectRatio = previewUiState.currentCameraSettings.aspectRatio, - deferredSurfaceProvider = deferredSurfaceProvider - ) - - QuickSettingsScreenOverlay( - modifier = Modifier, - isOpen = previewUiState.quickSettingsIsOpen, - toggleIsOpen = { viewModel.toggleQuickSettings() }, - currentCameraSettings = previewUiState.currentCameraSettings, - onLensFaceClick = viewModel::flipCamera, - onFlashModeClick = viewModel::setFlash, - onAspectRatioClick = { - viewModel.setAspectRatio(it) + Box( + modifier = Modifier.semantics { + testTagsAsResourceId = true } - // onTimerClick = {}/*TODO*/ - ) - // relative-grid style overlay on top of preview display - Column( - modifier = Modifier - .semantics { - testTagsAsResourceId = true - } - .fillMaxSize() ) { - // hide settings, quickSettings, and quick capture mode button - when (previewUiState.videoRecordingState) { - VideoRecordingState.ACTIVE -> {} - VideoRecordingState.INACTIVE -> { - // 3-segmented row to keep quick settings button centered - Row( - modifier = Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) - ) { - // row to left of quick settings button + // display camera feed. this stays behind everything else + PreviewDisplay( + onFlipCamera = viewModel::flipCamera, + onTapToFocus = viewModel::tapToFocus, + onZoomChange = { zoomChange: Float -> + viewModel.setZoomScale(zoomChange) + zoomScaleShow = true + zoomHandler.postDelayed({ zoomScaleShow = false }, ZOOM_SCALE_SHOW_TIMEOUT_MS) + }, + aspectRatio = previewUiState.currentCameraSettings.aspectRatio, + deferredSurfaceProvider = deferredSurfaceProvider + ) + + QuickSettingsScreenOverlay( + modifier = Modifier, + isOpen = previewUiState.quickSettingsIsOpen, + toggleIsOpen = { viewModel.toggleQuickSettings() }, + currentCameraSettings = previewUiState.currentCameraSettings, + onLensFaceClick = viewModel::flipCamera, + onFlashModeClick = viewModel::setFlash, + onAspectRatioClick = { + viewModel.setAspectRatio(it) + } + // onTimerClick = {}/*TODO*/ + ) + // relative-grid style overlay on top of preview display + Column( + modifier = Modifier + .fillMaxSize() + ) { + // hide settings, quickSettings, and quick capture mode button + when (previewUiState.videoRecordingState) { + VideoRecordingState.ACTIVE -> {} + VideoRecordingState.INACTIVE -> { + // 3-segmented row to keep quick settings button centered Row( modifier = Modifier - .weight(1f), - horizontalArrangement = Arrangement.Start, - verticalAlignment = Alignment.CenterVertically + .fillMaxWidth() + .height(IntrinsicSize.Min) ) { - // button to open default settings page - SettingsNavButton( + // row to left of quick settings button + Row( modifier = Modifier - .padding(12.dp), - onNavigateToSettings = onNavigateToSettings - ) - if (!previewUiState.quickSettingsIsOpen) { - QuickSettingsIndicators( - currentCameraSettings = previewUiState.currentCameraSettings, - onFlashModeClick = viewModel::setFlash + .weight(1f), + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.CenterVertically + ) { + // button to open default settings page + SettingsNavButton( + modifier = Modifier + .padding(12.dp), + onNavigateToSettings = onNavigateToSettings ) + if (!previewUiState.quickSettingsIsOpen) { + QuickSettingsIndicators( + currentCameraSettings = previewUiState + .currentCameraSettings, + onFlashModeClick = viewModel::setFlash + ) + } } - } - // quick settings button - ToggleQuickSettingsButton( - toggleDropDown = { viewModel.toggleQuickSettings() }, - isOpen = previewUiState.quickSettingsIsOpen - ) + // quick settings button + ToggleQuickSettingsButton( + toggleDropDown = { viewModel.toggleQuickSettings() }, + isOpen = previewUiState.quickSettingsIsOpen + ) - // Row to right of quick settings - Row( - modifier = Modifier - .weight(1f) - .fillMaxHeight(), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - TestingButton( + // Row to right of quick settings + Row( modifier = Modifier - .testTag("ToggleCaptureMode"), - onClick = { viewModel.toggleCaptureMode() }, - text = stringResource( - when (previewUiState.currentCameraSettings.captureMode) { - CaptureMode.SINGLE_STREAM -> - R.string.capture_mode_single_stream + .weight(1f) + .fillMaxHeight(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + TestingButton( + modifier = Modifier + .testTag("ToggleCaptureMode"), + onClick = { viewModel.toggleCaptureMode() }, + text = stringResource( + when (previewUiState.currentCameraSettings.captureMode) { + CaptureMode.SINGLE_STREAM -> + R.string.capture_mode_single_stream - CaptureMode.MULTI_STREAM -> - R.string.capture_mode_multi_stream - } + CaptureMode.MULTI_STREAM -> + R.string.capture_mode_multi_stream + } + ) ) - ) + } } } } - } - // this component places a gap in the center of the column that will push out the top - // and bottom edges. This will also allow the addition of vertical button bars on the - // sides of the screen - Row( - modifier = Modifier - .weight(1f) - .fillMaxWidth() - ) {} + // this component places a gap in the center of the column that will push out the top + // and bottom edges. This will also allow the addition of vertical button bars on the + // sides of the screen + Row( + modifier = Modifier + .weight(1f) + .fillMaxWidth() + ) {} - if (zoomScaleShow) { - ZoomScaleText(zoomScale = zoomScale) - } + if (zoomScaleShow) { + ZoomScaleText(zoomScale = zoomScale) + } - // 3-segmented row to keep capture button centered - Row( - modifier = - Modifier - .fillMaxWidth() - .height(IntrinsicSize.Min) - ) { - when (previewUiState.videoRecordingState) { - // hide first segment while recording in progress - VideoRecordingState.ACTIVE -> { - Spacer( - modifier = Modifier - .fillMaxHeight() - .weight(1f) - ) - } - // show first segment when not recording - VideoRecordingState.INACTIVE -> { - Row( - modifier = Modifier - .weight(1f) - .fillMaxHeight(), - horizontalArrangement = Arrangement.Center, - verticalAlignment = Alignment.CenterVertically - ) { - if (!previewUiState.quickSettingsIsOpen) { - FlipCameraButton( - onClick = { viewModel.flipCamera() }, - // enable only when phone has front and rear camera - enabledCondition = - previewUiState.currentCameraSettings.isBackCameraAvailable && - previewUiState.currentCameraSettings - .isFrontCameraAvailable - ) + // 3-segmented row to keep capture button centered + Row( + modifier = + Modifier + .fillMaxWidth() + .height(IntrinsicSize.Min) + ) { + when (previewUiState.videoRecordingState) { + // hide first segment while recording in progress + VideoRecordingState.ACTIVE -> { + Spacer( + modifier = Modifier + .fillMaxHeight() + .weight(1f) + ) + } + // show first segment when not recording + VideoRecordingState.INACTIVE -> { + Row( + modifier = Modifier + .weight(1f) + .fillMaxHeight(), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + if (!previewUiState.quickSettingsIsOpen) { + FlipCameraButton( + onClick = { viewModel.flipCamera() }, + // enable only when phone has front and rear camera + enabledCondition = + previewUiState + .currentCameraSettings + .isBackCameraAvailable && + previewUiState + .currentCameraSettings + .isFrontCameraAvailable + ) + } } } } - } - val multipleEventsCutter = remember { MultipleEventsCutter() } - val context = LocalContext.current - CaptureButton( - modifier = Modifier - .testTag(CAPTURE_BUTTON), - onClick = { - multipleEventsCutter.processEvent { - when (previewMode) { - is PreviewMode.StandardMode -> { - viewModel.captureImage() - } + val multipleEventsCutter = remember { MultipleEventsCutter() } + val context = LocalContext.current + CaptureButton( + modifier = Modifier + .testTag(CAPTURE_BUTTON), + onClick = { + multipleEventsCutter.processEvent { + when (previewMode) { + is PreviewMode.StandardMode -> { + viewModel.captureImage() + } - is PreviewMode.ExternalImageCaptureMode -> { - viewModel.captureImage( - context.contentResolver, - previewMode.imageCaptureUri, - previewMode.onImageCapture - ) + is PreviewMode.ExternalImageCaptureMode -> { + viewModel.captureImage( + context.contentResolver, + previewMode.imageCaptureUri, + previewMode.onImageCapture + ) + } } } - } - if (previewUiState.quickSettingsIsOpen) { - viewModel.toggleQuickSettings() - } - }, - onLongPress = { - viewModel.startVideoRecording() - if (previewUiState.quickSettingsIsOpen) { - viewModel.toggleQuickSettings() - } - }, - onRelease = { viewModel.stopVideoRecording() }, - videoRecordingState = previewUiState.videoRecordingState - ) - // You can replace this row so long as the weight of the component is 1f to - // ensure the capture button remains centered. - Row( - modifier = Modifier - .fillMaxHeight() - .weight(1f) - ) { - /*TODO("Place other components here") */ + if (previewUiState.quickSettingsIsOpen) { + viewModel.toggleQuickSettings() + } + }, + onLongPress = { + viewModel.startVideoRecording() + if (previewUiState.quickSettingsIsOpen) { + viewModel.toggleQuickSettings() + } + }, + onRelease = { viewModel.stopVideoRecording() }, + videoRecordingState = previewUiState.videoRecordingState + ) + // You can replace this row so long as the weight of the component is 1f to + // ensure the capture button remains centered. + Row( + modifier = Modifier + .fillMaxHeight() + .weight(1f) + ) { + /*TODO("Place other components here") */ + } } } - } - // displays toast when there is a message to show - if (previewUiState.toastMessageToShow != null) { - ShowTestableToast( - modifier = Modifier - .testTag(previewUiState.toastMessageToShow!!.testTag), - toastMessage = previewUiState.toastMessageToShow!!, - onToastShown = viewModel::onToastShown + // displays toast when there is a message to show + if (previewUiState.toastMessageToShow != null) { + ShowTestableToast( + modifier = Modifier + .testTag(previewUiState.toastMessageToShow!!.testTag), + toastMessage = previewUiState.toastMessageToShow!!, + onToastShown = viewModel::onToastShown + ) + } + + // Screen flash overlay that stays on top of everything but invisible normally. This should + // not be enabled based on whether screen flash is enabled because a previous image capture + // may still be running after flash mode change and clear actions (e.g. brightness restore) + // may need to be handled later. Compose smart recomposition should be able to optimize this + // if the relevant states are no longer changing. + ScreenFlashScreen( + screenFlashUiState = screenFlashUiState, + onInitialBrightnessCalculated = viewModel.screenFlash::setClearUiScreenBrightness ) } - - // Screen flash overlay that stays on top of everything but invisible normally. This should - // not be enabled based on whether screen flash is enabled because a previous image capture - // may still be running after flash mode change and clear actions (e.g. brightness restore) - // may need to be handled later. Compose smart recomposition should be able to optimize this - // if the relevant states are no longer changing. - ScreenFlashScreen( - screenFlashUiState = screenFlashUiState, - onInitialBrightnessCalculated = viewModel.screenFlash::setClearUiScreenBrightness - ) } } diff --git a/feature/quicksettings/src/main/java/com/google/jetpackcamera/feature/quicksettings/QuickSettingsScreen.kt b/feature/quicksettings/src/main/java/com/google/jetpackcamera/feature/quicksettings/QuickSettingsScreen.kt index e6b2ff9..8a8a60f 100644 --- a/feature/quicksettings/src/main/java/com/google/jetpackcamera/feature/quicksettings/QuickSettingsScreen.kt +++ b/feature/quicksettings/src/main/java/com/google/jetpackcamera/feature/quicksettings/QuickSettingsScreen.kt @@ -36,8 +36,6 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.testTagsAsResourceId import com.google.jetpackcamera.feature.quicksettings.ui.ExpandedQuickSetRatio import com.google.jetpackcamera.feature.quicksettings.ui.QuickFlipCamera import com.google.jetpackcamera.feature.quicksettings.ui.QuickSetFlash @@ -82,13 +80,10 @@ fun QuickSettingsScreenOverlay( if (isOpen) { Column( modifier = - Modifier + modifier .fillMaxSize() .background(color = backgroundColor.value) .alpha(alpha = contentAlpha.value) - .semantics { - testTagsAsResourceId = true - } .clickable { // if a setting is expanded, click on the background to close it. // if no other settings are expanded, then close the popup -- cgit v1.2.3