Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug Overlay #282

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class CameraXCameraUseCaseTest {
Dispatchers.Default,
constraintsRepository
).apply {
initialize(appSettings, CameraUseCase.UseCaseMode.STANDARD)
initialize(appSettings, CameraUseCase.UseCaseMode.STANDARD) {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to omit the empty bracket for the callback here and other places?
What about set null as default value?

providePreviewSurface()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,9 @@ private fun Preview.Builder.updateCameraStateWithCaptureResults(
if (old.debugInfo.logicalCameraId != logicalCameraId ||
old.debugInfo.physicalCameraId != physicalCameraId
) {
old.copy(debugInfo = DebugInfo(logicalCameraId, physicalCameraId))
old.copy(
debugInfo = DebugInfo(logicalCameraId, physicalCameraId)
)
} else {
old
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ interface CameraUseCase {
suspend fun initialize(
cameraAppSettings: CameraAppSettings,
useCaseMode: UseCaseMode,
isDebugMode: Boolean = false
isDebugMode: Boolean = false,
cameraPropertiesJSONCallback: (result: String) -> Unit
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any way to omit the empty bracket for this callback?
https://github.com/google/jetpack-camera-app/pull/282/files#diff-ddcefa1c42c7568a303536cd50adbf7c72115bb8ba254759ae5fd88f251e8167R156
What about set null as default value?

)

/**
Expand Down Expand Up @@ -184,4 +185,7 @@ data class CameraState(
val debugInfo: DebugInfo = DebugInfo(null, null)
)

data class DebugInfo(val logicalCameraId: String?, val physicalCameraId: String?)
data class DebugInfo(
val logicalCameraId: String?,
val physicalCameraId: String?
)
Original file line number Diff line number Diff line change
Expand Up @@ -113,16 +113,18 @@ constructor(
private val currentSettings = MutableStateFlow<CameraAppSettings?>(null)

// Could be improved by setting initial value only when camera is initialized
private val _currentCameraState = MutableStateFlow(CameraState())
private var _currentCameraState = MutableStateFlow(CameraState())
override fun getCurrentCameraState(): StateFlow<CameraState> = _currentCameraState.asStateFlow()

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

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

override suspend fun initialize(
cameraAppSettings: CameraAppSettings,
useCaseMode: CameraUseCase.UseCaseMode,
isDebugMode: Boolean
isDebugMode: Boolean,
cameraPropertiesJSONCallback: (result: String) -> Unit
) {
this.useCaseMode = useCaseMode
cameraProvider = ProcessCameraProvider.awaitInstance(application)
Expand Down Expand Up @@ -204,16 +206,17 @@ constructor(
.tryApplyConcurrentCameraModeConstraints()
if (isDebugMode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
withContext(iODispatcher) {
val cameraProperties =
val cameraPropertiesJSON =
getAllCamerasPropertiesJSONArray(cameraProvider.availableCameraInfos).toString()
val fileDir = File(application.getExternalFilesDir(null), "Debug")
fileDir.mkdirs()
val file = File(
fileDir,
"JCACameraProperties.json"
)
writeFileExternalStorage(file, cameraProperties)
Log.d(TAG, "JCACameraProperties written to ${file.path}. \n$cameraProperties")
writeFileExternalStorage(file, cameraPropertiesJSON)
cameraPropertiesJSONCallback.invoke(cameraPropertiesJSON)
Log.d(TAG, "JCACameraProperties written to ${file.path}. \n$cameraPropertiesJSON")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class FakeCameraUseCase(
override suspend fun initialize(
cameraAppSettings: CameraAppSettings,
useCaseMode: CameraUseCase.UseCaseMode,
isDebugMode: Boolean
isDebugMode: Boolean,
cameraPropertiesJSONCallback: (result: String) -> Unit
) {
initialized = true
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class FakeCameraUseCaseTest {
cameraUseCase.initialize(
cameraAppSettings = DEFAULT_CAMERA_APP_SETTINGS,
useCaseMode = CameraUseCase.UseCaseMode.STANDARD
)
) {}
}

@Test
Expand Down Expand Up @@ -152,7 +152,7 @@ class FakeCameraUseCaseTest {
cameraUseCase.initialize(
cameraAppSettings = DEFAULT_CAMERA_APP_SETTINGS,
useCaseMode = CameraUseCase.UseCaseMode.STANDARD
)
) {}
cameraUseCase.runCamera()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import com.google.jetpackcamera.feature.preview.ui.TestableSnackbar
import com.google.jetpackcamera.feature.preview.ui.TestableToast
import com.google.jetpackcamera.feature.preview.ui.ZoomLevelDisplayState
import com.google.jetpackcamera.feature.preview.ui.debouncedOrientationFlow
import com.google.jetpackcamera.feature.preview.ui.debug.DebugOverlayComponent
import com.google.jetpackcamera.settings.model.AspectRatio
import com.google.jetpackcamera.settings.model.CaptureMode
import com.google.jetpackcamera.settings.model.ConcurrentCameraMode
Expand Down Expand Up @@ -148,6 +149,7 @@ fun PreviewScreen(
onChangeImageFormat = viewModel::setImageFormat,
onToggleWhenDisabled = viewModel::showSnackBarForDisabledHdrToggle,
onToggleQuickSettings = viewModel::toggleQuickSettings,
onToggleDebugOverlay = viewModel::toggleDebugOverlay,
onMuteAudio = viewModel::setAudioMuted,
onCaptureImage = viewModel::captureImage,
onCaptureImageWithUri = viewModel::captureImageWithUri,
Expand Down Expand Up @@ -182,6 +184,7 @@ private fun ContentScreen(
onChangeImageFormat: (ImageOutputFormat) -> Unit = {},
onToggleWhenDisabled: (CaptureModeToggleUiState.DisabledReason) -> Unit = {},
onToggleQuickSettings: () -> Unit = {},
onToggleDebugOverlay: () -> Unit = {},
onMuteAudio: (Boolean) -> Unit = {},
onCaptureImage: () -> Unit = {},
onCaptureImageWithUri: (
Expand Down Expand Up @@ -254,6 +257,7 @@ private fun ContentScreen(
onChangeFlash = onChangeFlash,
onMuteAudio = onToggleMuteAudio,
onToggleQuickSettings = onToggleQuickSettings,
onToggleDebugOverlay = onToggleDebugOverlay,
onChangeImageFormat = onChangeImageFormat,
onToggleWhenDisabled = onToggleWhenDisabled,
onCaptureImage = onCaptureImage,
Expand All @@ -262,6 +266,13 @@ private fun ContentScreen(
onStopVideoRecording = onStopVideoRecording,
zoomLevelDisplayState = remember { ZoomLevelDisplayState(isDebugMode) }
)

DebugOverlayComponent(
toggleIsOpen = onToggleDebugOverlay,
previewUiState = previewUiState,
onChangeZoomScale = onChangeZoomScale
)

// displays toast when there is a message to show
if (previewUiState.toastMessageToShow != null) {
TestableToast(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,18 @@ sealed interface PreviewUiState {
val sessionFirstFrameTimestamp: Long = 0L,
val currentPhysicalCameraId: String? = null,
val currentLogicalCameraId: String? = null,
val isDebugMode: Boolean = false,
val debugUiState: DebugUiState = DebugUiState(),
val stabilizationUiState: StabilizationUiState = StabilizationUiState.Disabled
) : PreviewUiState
}
// todo(kc): add ElapsedTimeUiState class

data class DebugUiState(
val cameraPropertiesJSON: String = "",
val isDebugMode: Boolean = false,
val isDebugOverlayOpen: Boolean = false
)

sealed interface StabilizationUiState {
data object Disabled : StabilizationUiState

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ class PreviewViewModel @AssistedInject constructor(

private var externalUriIndex: Int = 0

private var cameraPropertiesJSON = ""

val screenFlash = ScreenFlash(cameraUseCase, viewModelScope)

private val snackBarCount = atomic(0)
Expand All @@ -113,7 +115,7 @@ class PreviewViewModel @AssistedInject constructor(
cameraAppSettings = settingsRepository.defaultCameraAppSettings.first(),
previewMode.toUseCaseMode(),
isDebugMode
)
) { cameraPropertiesJSON = it }
}

init {
Expand Down Expand Up @@ -141,10 +143,7 @@ class PreviewViewModel @AssistedInject constructor(
when (old) {
is PreviewUiState.Ready -> old
is PreviewUiState.NotReady ->
PreviewUiState.Ready(
isDebugMode = isDebugMode,
previewMode = previewMode
)
PreviewUiState.Ready(previewMode = previewMode)
}.copy(
currentCameraSettings = cameraAppSettings,
systemConstraints = systemConstraints,
Expand All @@ -157,6 +156,10 @@ class PreviewViewModel @AssistedInject constructor(
),
currentLogicalCameraId = cameraState.debugInfo.logicalCameraId,
currentPhysicalCameraId = cameraState.debugInfo.physicalCameraId,
debugUiState = DebugUiState(
cameraPropertiesJSON,
isDebugMode
),
stabilizationUiState = stabilizationUiState
// TODO(kc): set elapsed time UI state once VideoRecordingState
// refactor is complete.
Expand Down Expand Up @@ -762,6 +765,20 @@ class PreviewViewModel @AssistedInject constructor(
}
}

fun toggleDebugOverlay() {
viewModelScope.launch {
_previewUiState.update { old ->
(old as? PreviewUiState.Ready)?.copy(
debugUiState = DebugUiState(
old.debugUiState.cameraPropertiesJSON,
old.debugUiState.isDebugMode,
!old.debugUiState.isDebugOverlayOpen
)
) ?: old
}
}
}

fun tapToFocus(x: Float, y: Float) {
Log.d(TAG, "tapToFocus")
viewModelScope.launch {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import com.google.jetpackcamera.feature.preview.R
import com.google.jetpackcamera.feature.preview.StabilizationUiState
import com.google.jetpackcamera.feature.preview.quicksettings.ui.QuickSettingsIndicators
import com.google.jetpackcamera.feature.preview.quicksettings.ui.ToggleQuickSettingsButton
import com.google.jetpackcamera.feature.preview.ui.debug.DebugOverlayToggleButton
import com.google.jetpackcamera.settings.model.CameraAppSettings
import com.google.jetpackcamera.settings.model.FlashMode
import com.google.jetpackcamera.settings.model.ImageOutputFormat
Expand Down Expand Up @@ -96,6 +97,7 @@ fun CameraControlsOverlay(
onChangeImageFormat: (ImageOutputFormat) -> Unit = {},
onToggleWhenDisabled: (CaptureModeToggleUiState.DisabledReason) -> Unit = {},
onToggleQuickSettings: () -> Unit = {},
onToggleDebugOverlay: () -> Unit = {},
onMuteAudio: () -> Unit = {},
onCaptureImage: () -> Unit = {},
onCaptureImageWithUri: (
Expand Down Expand Up @@ -129,10 +131,12 @@ fun CameraControlsOverlay(
.fillMaxWidth()
.align(Alignment.TopCenter),
isQuickSettingsOpen = previewUiState.quickSettingsIsOpen,
isDebugMode = previewUiState.debugUiState.isDebugMode,
currentCameraSettings = previewUiState.currentCameraSettings,
onNavigateToSettings = onNavigateToSettings,
onChangeFlash = onChangeFlash,
onToggleQuickSettings = onToggleQuickSettings,
onToggleDebugOverlay = onToggleDebugOverlay,
stabilizationUiState = previewUiState.stabilizationUiState
)
}
Expand Down Expand Up @@ -168,57 +172,67 @@ fun CameraControlsOverlay(
private fun ControlsTop(
isQuickSettingsOpen: Boolean,
currentCameraSettings: CameraAppSettings,
isDebugMode: Boolean = false,
modifier: Modifier = Modifier,
onNavigateToSettings: () -> Unit = {},
onChangeFlash: (FlashMode) -> Unit = {},
onToggleQuickSettings: () -> Unit = {},
onToggleDebugOverlay: () -> Unit = {},
stabilizationUiState: StabilizationUiState = StabilizationUiState.Disabled
) {
Row(modifier, verticalAlignment = Alignment.CenterVertically) {
Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
// button to open default settings page
SettingsNavButton(
modifier = Modifier
.padding(12.dp)
.testTag(SETTINGS_BUTTON),
onNavigateToSettings = onNavigateToSettings
)
if (!isQuickSettingsOpen) {
QuickSettingsIndicators(
currentFlashMode = currentCameraSettings.flashMode,
onFlashModeClick = onChangeFlash
Column(modifier) {
Row(verticalAlignment = Alignment.CenterVertically) {
Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
// button to open default settings page
SettingsNavButton(
modifier = Modifier
.padding(12.dp)
.testTag(SETTINGS_BUTTON),
onNavigateToSettings = onNavigateToSettings
)
if (!isQuickSettingsOpen) {
QuickSettingsIndicators(
currentFlashMode = currentCameraSettings.flashMode,
onFlashModeClick = onChangeFlash
)
}
}
}

// quick settings button
ToggleQuickSettingsButton(onToggleQuickSettings, isQuickSettingsOpen)
// quick settings button
ToggleQuickSettingsButton(
toggleDropDown = onToggleQuickSettings,
isOpen = isQuickSettingsOpen
)

Row(
Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly
) {
var visibleStabilizationUiState: StabilizationUiState by remember {
mutableStateOf(StabilizationUiState.Disabled)
}
if (stabilizationUiState is StabilizationUiState.Set) {
// Only save StabilizationUiState.Set so exit transition can happen properly
visibleStabilizationUiState = stabilizationUiState
}
AnimatedVisibility(
visible = stabilizationUiState is StabilizationUiState.Set,
enter = fadeIn(),
exit = fadeOut()
Row(
Modifier.weight(1f),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly
) {
(visibleStabilizationUiState as? StabilizationUiState.Set)?.let {
StabilizationIcon(
stabilizationMode = it.stabilizationMode,
active = it.active
)
var visibleStabilizationUiState: StabilizationUiState by remember {
mutableStateOf(StabilizationUiState.Disabled)
}
if (stabilizationUiState is StabilizationUiState.Set) {
// Only save StabilizationUiState.Set so exit transition can happen properly
visibleStabilizationUiState = stabilizationUiState
}
AnimatedVisibility(
visible = stabilizationUiState is StabilizationUiState.Set,
enter = fadeIn(),
exit = fadeOut()
) {
(visibleStabilizationUiState as? StabilizationUiState.Set)?.let {
StabilizationIcon(
stabilizationMode = it.stabilizationMode,
active = it.active
)
}
}
}
}
if (isDebugMode) {
DebugOverlayToggleButton(toggleIsOpen = onToggleDebugOverlay)
}
}
}

Expand Down Expand Up @@ -261,7 +275,7 @@ private fun ControlsBottom(
if (showZoomLevel) {
ZoomScaleText(zoomLevel)
}
if (previewUiState.isDebugMode) {
if (previewUiState.debugUiState.isDebugMode) {
CurrentCameraIdText(physicalCameraId, logicalCameraId)
}
ElapsedTimeText(
Expand Down
Loading
Loading