1 /*
<lambda>null2 * Copyright (C) 2024 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 * http://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.google.jetpackcamera.feature.preview.ui
17
18 import android.content.ContentResolver
19 import android.net.Uri
20 import androidx.compose.animation.AnimatedVisibility
21 import androidx.compose.animation.core.tween
22 import androidx.compose.animation.fadeIn
23 import androidx.compose.animation.fadeOut
24 import androidx.compose.foundation.layout.Arrangement
25 import androidx.compose.foundation.layout.Box
26 import androidx.compose.foundation.layout.Column
27 import androidx.compose.foundation.layout.IntrinsicSize
28 import androidx.compose.foundation.layout.Row
29 import androidx.compose.foundation.layout.fillMaxSize
30 import androidx.compose.foundation.layout.fillMaxWidth
31 import androidx.compose.foundation.layout.height
32 import androidx.compose.foundation.layout.padding
33 import androidx.compose.foundation.layout.safeDrawingPadding
34 import androidx.compose.material.icons.Icons
35 import androidx.compose.material.icons.filled.CameraAlt
36 import androidx.compose.material.icons.filled.Videocam
37 import androidx.compose.material.icons.outlined.CameraAlt
38 import androidx.compose.material.icons.outlined.Videocam
39 import androidx.compose.material3.LocalContentColor
40 import androidx.compose.material3.LocalTextStyle
41 import androidx.compose.runtime.Composable
42 import androidx.compose.runtime.CompositionLocalProvider
43 import androidx.compose.runtime.LaunchedEffect
44 import androidx.compose.runtime.getValue
45 import androidx.compose.runtime.mutableStateOf
46 import androidx.compose.runtime.remember
47 import androidx.compose.runtime.setValue
48 import androidx.compose.ui.Alignment
49 import androidx.compose.ui.Modifier
50 import androidx.compose.ui.graphics.Color
51 import androidx.compose.ui.graphics.vector.rememberVectorPainter
52 import androidx.compose.ui.platform.LocalContext
53 import androidx.compose.ui.platform.testTag
54 import androidx.compose.ui.res.stringResource
55 import androidx.compose.ui.tooling.preview.Preview
56 import androidx.compose.ui.unit.dp
57 import androidx.compose.ui.unit.sp
58 import com.google.jetpackcamera.core.camera.VideoRecordingState
59 import com.google.jetpackcamera.feature.preview.CaptureButtonUiState
60 import com.google.jetpackcamera.feature.preview.CaptureModeToggleUiState
61 import com.google.jetpackcamera.feature.preview.DEFAULT_CAPTURE_BUTTON_STATE
62 import com.google.jetpackcamera.feature.preview.ElapsedTimeUiState
63 import com.google.jetpackcamera.feature.preview.FlashModeUiState
64 import com.google.jetpackcamera.feature.preview.MultipleEventsCutter
65 import com.google.jetpackcamera.feature.preview.PreviewMode
66 import com.google.jetpackcamera.feature.preview.PreviewUiState
67 import com.google.jetpackcamera.feature.preview.PreviewViewModel
68 import com.google.jetpackcamera.feature.preview.R
69 import com.google.jetpackcamera.feature.preview.StabilizationUiState
70 import com.google.jetpackcamera.feature.preview.quicksettings.ui.QuickSettingsIndicators
71 import com.google.jetpackcamera.feature.preview.quicksettings.ui.ToggleQuickSettingsButton
72 import com.google.jetpackcamera.feature.preview.ui.debug.DebugOverlayToggleButton
73 import com.google.jetpackcamera.settings.model.FlashMode
74 import com.google.jetpackcamera.settings.model.ImageOutputFormat
75 import com.google.jetpackcamera.settings.model.LensFacing
76 import com.google.jetpackcamera.settings.model.StabilizationMode
77 import com.google.jetpackcamera.settings.model.SystemConstraints
78 import com.google.jetpackcamera.settings.model.TYPICAL_SYSTEM_CONSTRAINTS
79 import com.google.jetpackcamera.settings.model.VideoQuality
80 import kotlinx.coroutines.delay
81
82 class ZoomLevelDisplayState(private val alwaysDisplay: Boolean = false) {
83 private var _showZoomLevel = mutableStateOf(alwaysDisplay)
84 val showZoomLevel: Boolean get() = _showZoomLevel.value
85
86 suspend fun showZoomLevel() {
87 if (!alwaysDisplay) {
88 _showZoomLevel.value = true
89 delay(3000)
90 _showZoomLevel.value = false
91 }
92 }
93 }
94
95 @Composable
CameraControlsOverlaynull96 fun CameraControlsOverlay(
97 previewUiState: PreviewUiState.Ready,
98 modifier: Modifier = Modifier,
99 zoomLevelDisplayState: ZoomLevelDisplayState = remember { ZoomLevelDisplayState() },
<lambda>null100 onNavigateToSettings: () -> Unit = {},
<lambda>null101 onFlipCamera: () -> Unit = {},
<lambda>null102 onChangeFlash: (FlashMode) -> Unit = {},
<lambda>null103 onChangeImageFormat: (ImageOutputFormat) -> Unit = {},
<lambda>null104 onToggleWhenDisabled: (CaptureModeToggleUiState.DisabledReason) -> Unit = {},
<lambda>null105 onToggleQuickSettings: () -> Unit = {},
<lambda>null106 onToggleDebugOverlay: () -> Unit = {},
<lambda>null107 onToggleAudio: () -> Unit = {},
<lambda>null108 onSetPause: (Boolean) -> Unit = {},
109 onCaptureImageWithUri: (
110 ContentResolver,
111 Uri?,
112 Boolean,
113 (PreviewViewModel.ImageCaptureEvent, Int) -> Unit
_null114 ) -> Unit = { _, _, _, _ -> },
115 onStartVideoRecording: (
116 Uri?,
117 Boolean,
118 (PreviewViewModel.VideoCaptureEvent) -> Unit
_null119 ) -> Unit = { _, _, _ -> },
<lambda>null120 onStopVideoRecording: () -> Unit = {},
<lambda>null121 onImageWellClick: (uri: Uri?) -> Unit = {},
122 onLockVideoRecording: (Boolean) -> Unit
123 ) {
124 // Show the current zoom level for a short period of time, only when the level changes.
<lambda>null125 var firstRun by remember { mutableStateOf(true) }
<lambda>null126 LaunchedEffect(previewUiState.zoomScale) {
127 if (firstRun) {
128 firstRun = false
129 } else {
130 zoomLevelDisplayState.showZoomLevel()
131 }
132 }
133
<lambda>null134 CompositionLocalProvider(LocalContentColor provides Color.White) {
135 Box(
136 modifier
137 .safeDrawingPadding()
138 .fillMaxSize()
139 ) {
140 if (previewUiState.videoRecordingState is VideoRecordingState.Inactive) {
141 ControlsTop(
142 modifier = Modifier
143 .fillMaxWidth()
144 .align(Alignment.TopCenter),
145 isQuickSettingsOpen = previewUiState.quickSettingsIsOpen,
146 isDebugMode = previewUiState.debugUiState.isDebugMode,
147 onNavigateToSettings = onNavigateToSettings,
148 onChangeFlash = onChangeFlash,
149 onToggleQuickSettings = onToggleQuickSettings,
150 onToggleDebugOverlay = onToggleDebugOverlay,
151 stabilizationUiState = previewUiState.stabilizationUiState,
152 videoQuality = previewUiState.videoQuality,
153 flashModeUiState = previewUiState.flashModeUiState
154 )
155 }
156
157 ControlsBottom(
158 modifier = Modifier
159 // padding to avoid snackbar
160 .padding(bottom = 60.dp)
161 .fillMaxWidth()
162 .align(Alignment.BottomCenter),
163 previewUiState = previewUiState,
164 zoomLevel = previewUiState.zoomScale,
165 physicalCameraId = previewUiState.currentPhysicalCameraId,
166 logicalCameraId = previewUiState.currentLogicalCameraId,
167 showZoomLevel = zoomLevelDisplayState.showZoomLevel,
168 isQuickSettingsOpen = previewUiState.quickSettingsIsOpen,
169 systemConstraints = previewUiState.systemConstraints,
170 videoRecordingState = previewUiState.videoRecordingState,
171 onFlipCamera = onFlipCamera,
172 onCaptureImageWithUri = onCaptureImageWithUri,
173 onToggleQuickSettings = onToggleQuickSettings,
174 onToggleAudio = onToggleAudio,
175 onSetPause = onSetPause,
176 onChangeImageFormat = onChangeImageFormat,
177 onToggleWhenDisabled = onToggleWhenDisabled,
178 onStartVideoRecording = onStartVideoRecording,
179 onStopVideoRecording = onStopVideoRecording,
180 onImageWellClick = onImageWellClick,
181 onLockVideoRecording = onLockVideoRecording
182 )
183 }
184 }
185 }
186
187 @Composable
ControlsTopnull188 private fun ControlsTop(
189 isQuickSettingsOpen: Boolean,
190 modifier: Modifier = Modifier,
191 isDebugMode: Boolean = false,
192 onNavigateToSettings: () -> Unit = {},
<lambda>null193 onChangeFlash: (FlashMode) -> Unit = {},
<lambda>null194 onToggleQuickSettings: () -> Unit = {},
<lambda>null195 onToggleDebugOverlay: () -> Unit = {},
196 stabilizationUiState: StabilizationUiState = StabilizationUiState.Disabled,
197 videoQuality: VideoQuality = VideoQuality.UNSPECIFIED,
198 flashModeUiState: FlashModeUiState = FlashModeUiState.Unavailable
199 ) {
<lambda>null200 Column(modifier) {
201 Row(modifier, verticalAlignment = Alignment.CenterVertically) {
202 Row(Modifier.weight(1f), verticalAlignment = Alignment.CenterVertically) {
203 // button to open default settings page
204 SettingsNavButton(
205 modifier = Modifier
206 .padding(12.dp)
207 .testTag(SETTINGS_BUTTON),
208 onNavigateToSettings = onNavigateToSettings
209 )
210 AnimatedVisibility(
211 visible = !isQuickSettingsOpen,
212 enter = fadeIn(),
213 exit = fadeOut()
214 ) {
215 QuickSettingsIndicators(
216 flashModeUiState = flashModeUiState,
217 onFlashModeClick = onChangeFlash
218 )
219 }
220 }
221
222 // quick settings button
223 ToggleQuickSettingsButton(
224 toggleDropDown = onToggleQuickSettings,
225 isOpen = isQuickSettingsOpen
226 )
227
228 Row(
229 Modifier.weight(1f),
230 verticalAlignment = Alignment.CenterVertically,
231 horizontalArrangement = Arrangement.SpaceEvenly
232 ) {
233 var visibleStabilizationUiState: StabilizationUiState by remember {
234 mutableStateOf(StabilizationUiState.Disabled)
235 }
236 if (stabilizationUiState is StabilizationUiState.Enabled) {
237 // Only save StabilizationUiState.Set so exit transition can happen properly
238 visibleStabilizationUiState = stabilizationUiState
239 }
240 AnimatedVisibility(
241 visible = stabilizationUiState is StabilizationUiState.Enabled,
242 enter = fadeIn(),
243 exit = fadeOut()
244 ) {
245 (visibleStabilizationUiState as? StabilizationUiState.Enabled)?.let {
246 StabilizationIcon(stabilizationUiState = it)
247 }
248 }
249 VideoQualityIcon(videoQuality, Modifier.testTag(VIDEO_QUALITY_TAG))
250 }
251 }
252 if (isDebugMode) {
253 DebugOverlayToggleButton(toggleIsOpen = onToggleDebugOverlay)
254 }
255 }
256 }
257
258 @Composable
ControlsBottomnull259 private fun ControlsBottom(
260 modifier: Modifier = Modifier,
261 previewUiState: PreviewUiState.Ready,
262 physicalCameraId: String? = null,
263 logicalCameraId: String? = null,
264 zoomLevel: Float,
265 showZoomLevel: Boolean,
266 isQuickSettingsOpen: Boolean,
267 systemConstraints: SystemConstraints,
268 videoRecordingState: VideoRecordingState,
269 onFlipCamera: () -> Unit = {},
270 onCaptureImageWithUri: (
271 ContentResolver,
272 Uri?,
273 Boolean,
274 (PreviewViewModel.ImageCaptureEvent, Int) -> Unit
_null275 ) -> Unit = { _, _, _, _ -> },
<lambda>null276 onToggleQuickSettings: () -> Unit = {},
<lambda>null277 onToggleAudio: () -> Unit = {},
<lambda>null278 onSetPause: (Boolean) -> Unit = {},
<lambda>null279 onChangeImageFormat: (ImageOutputFormat) -> Unit = {},
<lambda>null280 onToggleWhenDisabled: (CaptureModeToggleUiState.DisabledReason) -> Unit = {},
281 onStartVideoRecording: (
282 Uri?,
283 Boolean,
284 (PreviewViewModel.VideoCaptureEvent) -> Unit
_null285 ) -> Unit = { _, _, _ -> },
<lambda>null286 onStopVideoRecording: () -> Unit = {},
<lambda>null287 onImageWellClick: (uri: Uri?) -> Unit = {},
<lambda>null288 onLockVideoRecording: (Boolean) -> Unit = {}
289 ) {
290 Column(
291 modifier = modifier,
292 horizontalAlignment = Alignment.CenterHorizontally
293 ) {
294 CompositionLocalProvider(
295 LocalTextStyle provides LocalTextStyle.current.copy(fontSize = 20.sp)
<lambda>null296 ) {
297 Column(horizontalAlignment = Alignment.CenterHorizontally) {
298 if (showZoomLevel) {
299 ZoomScaleText(zoomLevel)
300 }
301 if (previewUiState.debugUiState.isDebugMode) {
302 CurrentCameraIdText(physicalCameraId, logicalCameraId)
303 }
304 if (previewUiState.elapsedTimeUiState is ElapsedTimeUiState.Enabled) {
305 AnimatedVisibility(
306 visible = (
307 previewUiState.videoRecordingState is
308 VideoRecordingState.Active
309 ),
310 enter = fadeIn(),
311 exit = fadeOut(animationSpec = tween(delayMillis = 1_500))
312 ) {
313 ElapsedTimeText(
314 modifier = Modifier.testTag(ELAPSED_TIME_TAG),
315 elapsedTimeUiState = previewUiState.elapsedTimeUiState
316 )
317 }
318 }
319 }
320 }
321
322 Column {
323 if (!isQuickSettingsOpen &&
324 previewUiState.captureModeToggleUiState
325 is CaptureModeToggleUiState.Visible
326 ) {
327 // TODO(yasith): Align to end of ImageWell based on alignment lines
328 Box(
329 Modifier.align(Alignment.End).padding(end = 12.dp)
<lambda>null330 ) {
331 CaptureModeToggleButton(
332 uiState = previewUiState.captureModeToggleUiState,
333 onChangeImageFormat = onChangeImageFormat,
334 onToggleWhenDisabled = onToggleWhenDisabled,
335 modifier = Modifier.testTag(CAPTURE_MODE_TOGGLE_BUTTON)
336 )
337 }
338 }
339
340 Row(
341 Modifier
342 .fillMaxWidth()
343 .height(IntrinsicSize.Max),
344 verticalAlignment = Alignment.CenterVertically
<lambda>null345 ) {
346 // Row that holds flip camera, capture button, and audio
347 Row(Modifier.weight(1f), horizontalArrangement = Arrangement.SpaceEvenly) {
348 // animation fades in/out this component based on quick settings
349 AnimatedVisibility(
350 visible = !isQuickSettingsOpen,
351 enter = fadeIn(),
352 exit = fadeOut()
353 ) {
354 if (videoRecordingState is VideoRecordingState.Inactive) {
355 FlipCameraButton(
356 modifier = Modifier.testTag(FLIP_CAMERA_BUTTON),
357 onClick = onFlipCamera,
358 lensFacing = previewUiState.currentCameraSettings.cameraLensFacing,
359 // enable only when phone has front and rear camera
360 enabledCondition = systemConstraints.availableLenses.size > 1
361 )
362 } else if (videoRecordingState is VideoRecordingState.Active
363 ) {
364 PauseResumeToggleButton(
365 onSetPause = onSetPause,
366 currentRecordingState = videoRecordingState
367 )
368 }
369 }
370 }
371 CaptureButton(
372 captureButtonUiState = previewUiState.captureButtonUiState,
373 previewMode = previewUiState.previewMode,
374 isQuickSettingsOpen = isQuickSettingsOpen,
375 onCaptureImageWithUri = onCaptureImageWithUri,
376 onToggleQuickSettings = onToggleQuickSettings,
377 onStartVideoRecording = onStartVideoRecording,
378 onStopVideoRecording = onStopVideoRecording,
379 onLockVideoRecording = onLockVideoRecording
380 )
381 Row(Modifier.weight(1f), horizontalArrangement = Arrangement.SpaceEvenly) {
382 if (videoRecordingState is VideoRecordingState.Active) {
383 AmplitudeVisualizer(
384 modifier = Modifier
385 .weight(1f)
386 .fillMaxSize(),
387 onToggleAudio = onToggleAudio,
388 audioUiState = previewUiState.audioUiState
389 )
390 } else {
391 Column {
392 if (!isQuickSettingsOpen &&
393 previewUiState.previewMode is PreviewMode.StandardMode
394 ) {
395 ImageWell(
396 modifier = Modifier.weight(1f),
397 imageWellUiState = previewUiState.imageWellUiState,
398 onClick = onImageWellClick
399 )
400 }
401 }
402 }
403 }
404 }
405 }
406 }
407 }
408
409 @Composable
CaptureButtonnull410 private fun CaptureButton(
411 modifier: Modifier = Modifier,
412 captureButtonUiState: CaptureButtonUiState,
413 isQuickSettingsOpen: Boolean,
414 previewMode: PreviewMode,
415 onToggleQuickSettings: () -> Unit = {},
416 onCaptureImageWithUri: (
417 ContentResolver,
418 Uri?,
419 Boolean,
420 (PreviewViewModel.ImageCaptureEvent, Int) -> Unit
_null421 ) -> Unit = { _, _, _, _ -> },
422 onStartVideoRecording: (
423 Uri?,
424 Boolean,
425 (PreviewViewModel.VideoCaptureEvent) -> Unit
_null426 ) -> Unit = { _, _, _ -> },
<lambda>null427 onStopVideoRecording: () -> Unit = {},
<lambda>null428 onLockVideoRecording: (Boolean) -> Unit = {}
429 ) {
<lambda>null430 val multipleEventsCutter = remember { MultipleEventsCutter() }
431 val context = LocalContext.current
432
433 CaptureButton(
434 modifier = modifier.testTag(CAPTURE_BUTTON),
<lambda>null435 onCaptureImage = {
436 if (captureButtonUiState is CaptureButtonUiState.Enabled) {
437 multipleEventsCutter.processEvent {
438 when (previewMode) {
439 is PreviewMode.StandardMode -> {
440 onCaptureImageWithUri(
441 context.contentResolver,
442 null,
443 true
444 ) { event: PreviewViewModel.ImageCaptureEvent, _: Int ->
445 previewMode.onImageCapture(event)
446 }
447 }
448
449 is PreviewMode.ExternalImageCaptureMode -> {
450 onCaptureImageWithUri(
451 context.contentResolver,
452 previewMode.imageCaptureUri,
453 false
454 ) { event: PreviewViewModel.ImageCaptureEvent, _: Int ->
455 previewMode.onImageCapture(event)
456 }
457 }
458
459 is PreviewMode.ExternalMultipleImageCaptureMode -> {
460 val ignoreUri =
461 previewMode.imageCaptureUris.isNullOrEmpty()
462 onCaptureImageWithUri(
463 context.contentResolver,
464 null,
465 previewMode.imageCaptureUris.isNullOrEmpty() ||
466 ignoreUri,
467 previewMode.onImageCapture
468 )
469 }
470
471 else -> {
472 onCaptureImageWithUri(
473 context.contentResolver,
474 null,
475 false
476 ) { _: PreviewViewModel.ImageCaptureEvent, _: Int -> }
477 }
478 }
479 }
480 }
481 if (isQuickSettingsOpen) {
482 onToggleQuickSettings()
483 }
484 },
<lambda>null485 onStartVideoRecording = {
486 if (captureButtonUiState is CaptureButtonUiState.Enabled) {
487 when (previewMode) {
488 is PreviewMode.StandardMode -> {
489 onStartVideoRecording(null, false) {}
490 }
491
492 is PreviewMode.ExternalVideoCaptureMode -> {
493 onStartVideoRecording(
494 previewMode.videoCaptureUri,
495 true,
496 previewMode.onVideoCapture
497 )
498 }
499
500 else -> {
501 onStartVideoRecording(null, false) {}
502 }
503 }
504 if (isQuickSettingsOpen) {
505 onToggleQuickSettings()
506 }
507 }
508 },
<lambda>null509 onStopVideoRecording = {
510 onStopVideoRecording()
511 },
512 captureButtonUiState = captureButtonUiState,
513 onLockVideoRecording = onLockVideoRecording
514 )
515 }
516
517 @Composable
CaptureModeToggleButtonnull518 private fun CaptureModeToggleButton(
519 uiState: CaptureModeToggleUiState.Visible,
520 onChangeImageFormat: (ImageOutputFormat) -> Unit,
521 onToggleWhenDisabled: (CaptureModeToggleUiState.DisabledReason) -> Unit,
522 modifier: Modifier = Modifier
523 ) {
524 // Captures hdr image (left) when output format is UltraHdr, else captures hdr video (right).
525 val initialState =
526 when (uiState.currentMode) {
527 CaptureModeToggleUiState.ToggleMode.CAPTURE_TOGGLE_IMAGE -> ToggleState.Left
528 CaptureModeToggleUiState.ToggleMode.CAPTURE_TOGGLE_VIDEO -> ToggleState.Right
529 }
530 ToggleButton(
531 leftIcon = if (uiState.currentMode ==
532 CaptureModeToggleUiState.ToggleMode.CAPTURE_TOGGLE_IMAGE
533 ) {
534 rememberVectorPainter(image = Icons.Filled.CameraAlt)
535 } else {
536 rememberVectorPainter(image = Icons.Outlined.CameraAlt)
537 },
538 rightIcon = if (uiState.currentMode ==
539 CaptureModeToggleUiState.ToggleMode.CAPTURE_TOGGLE_VIDEO
540 ) {
541 rememberVectorPainter(image = Icons.Filled.Videocam)
542 } else {
543 rememberVectorPainter(image = Icons.Outlined.Videocam)
544 },
545 initialState = initialState,
546 onToggleStateChanged = {
547 val imageFormat = when (it) {
548 ToggleState.Left -> ImageOutputFormat.JPEG_ULTRA_HDR
549 ToggleState.Right -> ImageOutputFormat.JPEG
550 }
551 onChangeImageFormat(imageFormat)
552 },
553 onToggleWhenDisabled = {
554 check(uiState is CaptureModeToggleUiState.Disabled)
555 onToggleWhenDisabled(uiState.disabledReason)
556 },
557 enabled = uiState is CaptureModeToggleUiState.Enabled,
558 leftIconDescription =
559 stringResource(id = R.string.capture_mode_image_capture_content_description),
560 rightIconDescription =
561 stringResource(id = R.string.capture_mode_video_recording_content_description),
562 modifier = modifier
563 )
564 }
565
566 @Preview(backgroundColor = 0xFF000000, showBackground = true)
567 @Composable
Preview_ControlsTop_QuickSettingsOpennull568 private fun Preview_ControlsTop_QuickSettingsOpen() {
569 CompositionLocalProvider(LocalContentColor provides Color.White) {
570 ControlsTop(
571 isQuickSettingsOpen = true
572 )
573 }
574 }
575
576 @Preview(backgroundColor = 0xFF000000, showBackground = true)
577 @Composable
Preview_ControlsTop_QuickSettingsClosednull578 private fun Preview_ControlsTop_QuickSettingsClosed() {
579 CompositionLocalProvider(LocalContentColor provides Color.White) {
580 ControlsTop(
581 isQuickSettingsOpen = false
582 )
583 }
584 }
585
586 @Preview(backgroundColor = 0xFF000000, showBackground = true)
587 @Composable
Preview_ControlsTop_FlashModeOnnull588 private fun Preview_ControlsTop_FlashModeOn() {
589 CompositionLocalProvider(LocalContentColor provides Color.White) {
590 ControlsTop(
591 isQuickSettingsOpen = false,
592 flashModeUiState = FlashModeUiState.Available(
593 selectedFlashMode = FlashMode.ON,
594 availableFlashModes = listOf(FlashMode.OFF, FlashMode.ON),
595 isActive = false
596 )
597 )
598 }
599 }
600
601 @Preview(backgroundColor = 0xFF000000, showBackground = true)
602 @Composable
Preview_ControlsTop_FlashModeAutonull603 private fun Preview_ControlsTop_FlashModeAuto() {
604 CompositionLocalProvider(LocalContentColor provides Color.White) {
605 ControlsTop(
606 isQuickSettingsOpen = false,
607 flashModeUiState = FlashModeUiState.Available(
608 selectedFlashMode = FlashMode.AUTO,
609 availableFlashModes = listOf(FlashMode.OFF, FlashMode.ON, FlashMode.AUTO),
610 isActive = false
611 )
612 )
613 }
614 }
615
616 @Preview(backgroundColor = 0xFF000000, showBackground = true)
617 @Composable
Preview_ControlsTop_WithStabilizationnull618 private fun Preview_ControlsTop_WithStabilization() {
619 CompositionLocalProvider(LocalContentColor provides Color.White) {
620 ControlsTop(
621 isQuickSettingsOpen = false,
622 stabilizationUiState = StabilizationUiState.Specific(
623 stabilizationMode = StabilizationMode.ON
624 )
625 )
626 }
627 }
628
629 @Preview(backgroundColor = 0xFF000000, showBackground = true)
630 @Composable
Preview_ControlsTop_WithStabilizationAutonull631 private fun Preview_ControlsTop_WithStabilizationAuto() {
632 CompositionLocalProvider(LocalContentColor provides Color.White) {
633 ControlsTop(
634 isQuickSettingsOpen = false,
635 stabilizationUiState = StabilizationUiState.Auto(
636 stabilizationMode = StabilizationMode.OPTICAL
637 )
638 )
639 }
640 }
641
642 @Preview(backgroundColor = 0xFF000000, showBackground = true)
643 @Composable
Preview_ControlsBottomnull644 private fun Preview_ControlsBottom() {
645 CompositionLocalProvider(LocalContentColor provides Color.White) {
646 ControlsBottom(
647 previewUiState = PreviewUiState.Ready(
648 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
649 previewMode = PreviewMode.StandardMode {},
650 captureModeToggleUiState = CaptureModeToggleUiState.Invisible,
651 videoRecordingState = VideoRecordingState.Inactive(),
652 captureButtonUiState = DEFAULT_CAPTURE_BUTTON_STATE
653 ),
654 zoomLevel = 1.3f,
655 showZoomLevel = true,
656 isQuickSettingsOpen = false,
657 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
658 videoRecordingState = VideoRecordingState.Inactive()
659 )
660 }
661 }
662
663 @Preview(backgroundColor = 0xFF000000, showBackground = true)
664 @Composable
Preview_ControlsBottom_NoZoomLevelnull665 private fun Preview_ControlsBottom_NoZoomLevel() {
666 CompositionLocalProvider(LocalContentColor provides Color.White) {
667 ControlsBottom(
668 previewUiState = PreviewUiState.Ready(
669 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
670 previewMode = PreviewMode.StandardMode {},
671 captureModeToggleUiState = CaptureModeToggleUiState.Invisible,
672 videoRecordingState = VideoRecordingState.Inactive(),
673 captureButtonUiState = DEFAULT_CAPTURE_BUTTON_STATE
674 ),
675 zoomLevel = 1.3f,
676 showZoomLevel = false,
677 isQuickSettingsOpen = false,
678 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
679 videoRecordingState = VideoRecordingState.Inactive()
680 )
681 }
682 }
683
684 @Preview(backgroundColor = 0xFF000000, showBackground = true)
685 @Composable
Preview_ControlsBottom_QuickSettingsOpennull686 private fun Preview_ControlsBottom_QuickSettingsOpen() {
687 CompositionLocalProvider(LocalContentColor provides Color.White) {
688 ControlsBottom(
689 previewUiState = PreviewUiState.Ready(
690 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
691 previewMode = PreviewMode.StandardMode {},
692 captureModeToggleUiState = CaptureModeToggleUiState.Invisible,
693 videoRecordingState = VideoRecordingState.Inactive(),
694 captureButtonUiState = DEFAULT_CAPTURE_BUTTON_STATE
695 ),
696 zoomLevel = 1.3f,
697 showZoomLevel = true,
698 isQuickSettingsOpen = true,
699 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
700 videoRecordingState = VideoRecordingState.Inactive()
701 )
702 }
703 }
704
705 @Preview(backgroundColor = 0xFF000000, showBackground = true)
706 @Composable
Preview_ControlsBottom_NoFlippableCameranull707 private fun Preview_ControlsBottom_NoFlippableCamera() {
708 CompositionLocalProvider(LocalContentColor provides Color.White) {
709 ControlsBottom(
710 previewUiState = PreviewUiState.Ready(
711 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
712 previewMode = PreviewMode.StandardMode {},
713 captureModeToggleUiState = CaptureModeToggleUiState.Invisible,
714 videoRecordingState = VideoRecordingState.Inactive(),
715 captureButtonUiState = DEFAULT_CAPTURE_BUTTON_STATE
716 ),
717 zoomLevel = 1.3f,
718 showZoomLevel = true,
719 isQuickSettingsOpen = false,
720 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS.copy(
721 availableLenses = listOf(LensFacing.FRONT),
722 perLensConstraints = mapOf(
723 LensFacing.FRONT to
724 TYPICAL_SYSTEM_CONSTRAINTS.perLensConstraints[LensFacing.FRONT]!!
725 )
726 ),
727 videoRecordingState = VideoRecordingState.Inactive()
728 )
729 }
730 }
731
732 @Preview(backgroundColor = 0xFF000000, showBackground = true)
733 @Composable
Preview_ControlsBottom_Recordingnull734 private fun Preview_ControlsBottom_Recording() {
735 CompositionLocalProvider(LocalContentColor provides Color.White) {
736 ControlsBottom(
737 previewUiState = PreviewUiState.Ready(
738 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
739 previewMode = PreviewMode.StandardMode {},
740 captureModeToggleUiState = CaptureModeToggleUiState.Invisible,
741 videoRecordingState = VideoRecordingState.Active.Recording(0L, .9, 1_000_000_000),
742 captureButtonUiState = DEFAULT_CAPTURE_BUTTON_STATE
743 ),
744 zoomLevel = 1.3f,
745 showZoomLevel = true,
746 isQuickSettingsOpen = false,
747 systemConstraints = TYPICAL_SYSTEM_CONSTRAINTS,
748 videoRecordingState = VideoRecordingState.Active.Recording(0L, .9, 1_000_000_000)
749 )
750 }
751 }
752