@@ -32,19 +32,13 @@ import androidx.compose.animation.core.animateFloatAsState
3232import androidx.compose.animation.core.spring
3333import androidx.compose.animation.core.tween
3434import androidx.compose.foundation.background
35- import androidx.compose.foundation.clickable
3635import androidx.compose.foundation.gestures.detectTapGestures
3736import androidx.compose.foundation.gestures.rememberTransformableState
3837import androidx.compose.foundation.gestures.transformable
39- import androidx.compose.foundation.layout.Arrangement
4038import androidx.compose.foundation.layout.Box
4139import androidx.compose.foundation.layout.BoxWithConstraints
42- import androidx.compose.foundation.layout.Row
43- import androidx.compose.foundation.layout.aspectRatio
44- import androidx.compose.foundation.layout.fillMaxHeight
4540import androidx.compose.foundation.layout.fillMaxSize
4641import androidx.compose.foundation.layout.height
47- import androidx.compose.foundation.layout.padding
4842import androidx.compose.foundation.layout.size
4943import androidx.compose.foundation.layout.width
5044import androidx.compose.foundation.shape.RoundedCornerShape
@@ -66,10 +60,8 @@ import androidx.compose.material3.Icon
6660import androidx.compose.material3.IconButton
6761import androidx.compose.material3.IconButtonDefaults
6862import androidx.compose.material3.LocalContentColor
69- import androidx.compose.material3.MaterialTheme
7063import androidx.compose.material3.SnackbarHostState
7164import androidx.compose.material3.SnackbarResult
72- import androidx.compose.material3.Surface
7365import androidx.compose.material3.Text
7466import androidx.compose.runtime.Composable
7567import androidx.compose.runtime.CompositionLocalProvider
@@ -88,17 +80,12 @@ import androidx.compose.ui.draw.clip
8880import androidx.compose.ui.draw.drawBehind
8981import androidx.compose.ui.draw.rotate
9082import androidx.compose.ui.graphics.Color
91- import androidx.compose.ui.graphics.painter.Painter
9283import androidx.compose.ui.graphics.vector.rememberVectorPainter
9384import androidx.compose.ui.input.pointer.pointerInput
94- import androidx.compose.ui.layout.layout
9585import androidx.compose.ui.platform.LocalContext
9686import androidx.compose.ui.platform.testTag
9787import androidx.compose.ui.res.painterResource
9888import androidx.compose.ui.res.stringResource
99- import androidx.compose.ui.semantics.Role
100- import androidx.compose.ui.semantics.semantics
101- import androidx.compose.ui.semantics.stateDescription
10289import androidx.compose.ui.text.style.TextAlign
10390import androidx.compose.ui.unit.Dp
10491import androidx.compose.ui.unit.dp
@@ -237,40 +224,25 @@ fun CaptureModeToggleButton(
237224 onToggleWhenDisabled : (DisableRationale ) -> Unit ,
238225 modifier : Modifier = Modifier
239226) {
240- // Captures hdr image (left) when output format is UltraHdr , else captures hdr video (right).
227+ // Captures image (left), else captures video (right).
241228 val toggleState = remember(uiState.selectedCaptureMode) {
242229 when (uiState.selectedCaptureMode) {
243- CaptureMode .IMAGE_ONLY , CaptureMode .STANDARD -> ToggleState . Left
244- CaptureMode .VIDEO_ONLY -> ToggleState . Right
230+ CaptureMode .IMAGE_ONLY , CaptureMode .STANDARD -> false
231+ CaptureMode .VIDEO_ONLY -> true
245232 }
246233 }
234+
247235 val enabled =
248236 uiState.isCaptureModeSelectable(CaptureMode .VIDEO_ONLY ) &&
249237 uiState.isCaptureModeSelectable(
250238 CaptureMode .IMAGE_ONLY
251239 ) && uiState.selectedCaptureMode != CaptureMode .STANDARD
252240
253- ToggleButton (
254- leftIcon = if (uiState.selectedCaptureMode ==
255- CaptureMode .IMAGE_ONLY
256- ) {
257- rememberVectorPainter(image = Icons .Filled .CameraAlt )
258- } else {
259- rememberVectorPainter(image = Icons .Outlined .CameraAlt )
260- },
261- rightIcon = if (uiState.selectedCaptureMode ==
262- CaptureMode .VIDEO_ONLY
263- ) {
264- rememberVectorPainter(image = Icons .Filled .Videocam )
265- } else {
266- rememberVectorPainter(image = Icons .Outlined .Videocam )
267- },
268- toggleState = toggleState,
269- onToggle = {
270- val newCaptureMode = when (toggleState) {
271- ToggleState .Right -> CaptureMode .IMAGE_ONLY
272- ToggleState .Left -> CaptureMode .VIDEO_ONLY
273- }
241+ ToggleSwitch (
242+ modifier = modifier.testTag(CAPTURE_MODE_TOGGLE_BUTTON ),
243+ checked = toggleState,
244+ onCheckedChange = { isChecked ->
245+ val newCaptureMode = if (isChecked) CaptureMode .VIDEO_ONLY else CaptureMode .IMAGE_ONLY
274246 onChangeCaptureMode(newCaptureMode)
275247 },
276248 onToggleWhenDisabled = {
@@ -286,144 +258,32 @@ fun CaptureModeToggleButton(
286258 ?.disabledReason
287259 disabledReason?.let { onToggleWhenDisabled(it) }
288260 },
289- // toggle only enabled when both capture modes are available
290261 enabled = enabled,
291- leftIconDescription =
292- if (enabled) {
293- stringResource(id = R .string.capture_mode_image_capture_content_description)
262+ leftIcon = if (uiState.selectedCaptureMode ==
263+ CaptureMode .IMAGE_ONLY
264+ ) {
265+ Icons .Filled .CameraAlt
294266 } else {
295- stringResource(
296- id = R .string.capture_mode_image_capture_content_description_disabled
297- )
267+ Icons .Outlined .CameraAlt
298268 },
299- rightIconDescription =
300- if (enabled) {
301- stringResource(id = R .string.capture_mode_video_recording_content_description)
269+ rightIcon = if (uiState.selectedCaptureMode ==
270+ CaptureMode .VIDEO_ONLY
271+ ) {
272+ Icons .Filled .Videocam
302273 } else {
303- stringResource(
304- id = R .string.capture_mode_video_recording_content_description_disabled
305- )
274+ Icons .Outlined .Videocam
306275 },
307- modifier = modifier
308- )
309- }
310-
311- enum class ToggleState {
312- Left ,
313- Right
314- }
315-
316- // todo(kc): need to recreate image toggle button to be scalable and support drag
317- @Composable
318- fun ToggleButton (
319- leftIcon : Painter ,
320- rightIcon : Painter ,
321- modifier : Modifier = Modifier ,
322- toggleState : ToggleState ? = null,
323- onToggle : () -> Unit = {},
324- onToggleWhenDisabled : () -> Unit = {},
325- enabled : Boolean = true,
326- leftIconDescription : String = "leftIcon",
327- rightIconDescription : String = "rightIcon",
328- iconPadding : Dp = 8.dp
329- ) {
330- val backgroundColor = MaterialTheme .colorScheme.surfaceContainerHighest
331- val disableColor = MaterialTheme .colorScheme.onSurface
332- val iconSelectionColor = MaterialTheme .colorScheme.onPrimary
333- val iconUnSelectionColor = MaterialTheme .colorScheme.primary
334- val circleSelectionColor = MaterialTheme .colorScheme.primary
335- val circleColor = if (enabled) circleSelectionColor else disableColor.copy(alpha = 0.12f )
336- val animatedTogglePosition by animateFloatAsState(
337- when (toggleState) {
338- ToggleState .Left -> 0f
339- ToggleState .Right -> 1f
340- null -> 0f
276+ leftIconDescription = if (enabled) {
277+ stringResource(id = R .string.capture_mode_image_capture_content_description)
278+ } else {
279+ stringResource(id = R .string.capture_mode_image_capture_content_description_disabled)
341280 },
342- label = " togglePosition"
343- )
344-
345- Surface (
346- modifier = modifier
347- .clip(shape = RoundedCornerShape (50 ))
348- .then(
349- Modifier .clickable(
350- role = Role .Switch
351- ) {
352- if (enabled && toggleState != null ) {
353- onToggle()
354- } else {
355- onToggleWhenDisabled()
356- }
357- }
358- )
359- .semantics {
360- stateDescription = when (toggleState) {
361- ToggleState .Left -> leftIconDescription
362- ToggleState .Right -> rightIconDescription
363- null -> " unknown togglestate"
364- }
365- }
366- .width(64 .dp)
367- .height(32 .dp),
368- color = backgroundColor
369- ) {
370- Box {
371- Row (
372- modifier = Modifier .matchParentSize(),
373- verticalAlignment = Alignment .CenterVertically
374- ) {
375- Box (
376- Modifier
377- .layout { measurable, constraints ->
378- val placeable = measurable.measure(constraints)
379- layout(placeable.width, placeable.height) {
380- val xPos = animatedTogglePosition *
381- (constraints.maxWidth - placeable.width)
382- placeable.placeRelative(xPos.toInt(), 0 )
383- }
384- }
385- .fillMaxHeight()
386- .aspectRatio(1f )
387- .clip(RoundedCornerShape (50 ))
388- .background(circleColor)
389- )
390- }
391- Row (
392- modifier = Modifier
393- .matchParentSize()
394- .then(
395- if (enabled) Modifier else Modifier .alpha(0.38f )
396- ),
397- verticalAlignment = Alignment .CenterVertically ,
398- horizontalArrangement = Arrangement .SpaceBetween
399- ) {
400- Icon (
401- painter = leftIcon,
402- contentDescription = " leftIcon" ,
403- modifier = Modifier .padding(iconPadding),
404- tint = if (! enabled) {
405- disableColor
406- } else if (toggleState == ToggleState .Left ) {
407- iconSelectionColor
408- } else {
409- iconUnSelectionColor
410- }
411- )
412- Icon (
413- painter = rightIcon,
414- contentDescription = " rightIcon" ,
415- modifier = Modifier .padding(iconPadding),
416- tint = if (! enabled) {
417- disableColor
418- } else if (toggleState == ToggleState .Right ) {
419- iconSelectionColor
420- } else {
421- iconUnSelectionColor
422- }
423- )
424- }
281+ rightIconDescription = if (enabled) {
282+ stringResource(id = R .string.capture_mode_video_recording_content_description)
283+ } else {
284+ stringResource(id = R .string.capture_mode_video_recording_content_description_disabled)
425285 }
426- }
286+ )
427287}
428288
429289@Composable
0 commit comments