Merge pull request #683 from d4rken-org/worktree-anc-selector-redesign
General: Redesign the noise control mode selector
@@ -7,7 +7,7 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.twotone.AutoAwesome
|
import androidx.compose.material.icons.twotone.AutoAwesome
|
||||||
import androidx.compose.material.icons.twotone.DoNotDisturbOn
|
import androidx.compose.material.icons.twotone.DoNotDisturbOn
|
||||||
import androidx.compose.material.icons.twotone.Headphones
|
import androidx.compose.material.icons.twotone.Headphones
|
||||||
import androidx.compose.material.icons.twotone.Hearing
|
import androidx.compose.material.icons.twotone.NoiseAware
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import eu.darken.capod.R
|
import eu.darken.capod.R
|
||||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
||||||
@@ -25,7 +25,9 @@ fun AapSetting.AncMode.Value.shortLabel(context: Context): String = context.getS
|
|||||||
fun AapSetting.AncMode.Value.icon(): ImageVector = when (this) {
|
fun AapSetting.AncMode.Value.icon(): ImageVector = when (this) {
|
||||||
AapSetting.AncMode.Value.OFF -> Icons.TwoTone.DoNotDisturbOn
|
AapSetting.AncMode.Value.OFF -> Icons.TwoTone.DoNotDisturbOn
|
||||||
AapSetting.AncMode.Value.ON -> Icons.TwoTone.Headphones
|
AapSetting.AncMode.Value.ON -> Icons.TwoTone.Headphones
|
||||||
AapSetting.AncMode.Value.TRANSPARENCY -> Icons.TwoTone.Hearing
|
// Not Hearing: that glyph is already the "In ear" status chip, so the two looked identical
|
||||||
|
// sitting in the same card. NoiseAware is the purpose-built ambient/transparency icon.
|
||||||
|
AapSetting.AncMode.Value.TRANSPARENCY -> Icons.TwoTone.NoiseAware
|
||||||
AapSetting.AncMode.Value.ADAPTIVE -> Icons.TwoTone.AutoAwesome
|
AapSetting.AncMode.Value.ADAPTIVE -> Icons.TwoTone.AutoAwesome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -284,10 +284,10 @@ private fun ColumnScope.DualPodsCardExpanded(
|
|||||||
// ANC mode selector
|
// ANC mode selector
|
||||||
val ancMode = device.ancMode
|
val ancMode = device.ancMode
|
||||||
if (device.isAapConnected && device.hasAncControl && ancMode != null) {
|
if (device.isAapConnected && device.hasAncControl && ancMode != null) {
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
AncModeSelector(
|
AncModeSelector(
|
||||||
currentMode = ancMode.current,
|
currentMode = ancMode.current,
|
||||||
supportedModes = device.visibleAncModes,
|
supportedModes = device.visibleAncModes,
|
||||||
onModeSelected = { onAncModeChange?.invoke(it) },
|
onModeSelected = { onAncModeChange?.invoke(it) },
|
||||||
pendingMode = device.pendingAncMode,
|
pendingMode = device.pendingAncMode,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -335,7 +335,7 @@ private fun ColumnScope.SinglePodsCardExpanded(
|
|||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
AncModeSelector(
|
AncModeSelector(
|
||||||
currentMode = ancMode.current,
|
currentMode = ancMode.current,
|
||||||
supportedModes = device.visibleAncModes,
|
supportedModes = device.visibleAncModes,
|
||||||
onModeSelected = { onAncModeChange?.invoke(it) },
|
onModeSelected = { onAncModeChange?.invoke(it) },
|
||||||
pendingMode = device.pendingAncMode,
|
pendingMode = device.pendingAncMode,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,34 +1,74 @@
|
|||||||
package eu.darken.capod.main.ui.overview.cards.components
|
package eu.darken.capod.main.ui.overview.cards.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.animation.core.RepeatMode
|
||||||
|
import androidx.compose.animation.core.Spring
|
||||||
|
import androidx.compose.animation.core.animateFloat
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
|
import androidx.compose.animation.core.spring
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.selection.selectable
|
||||||
|
import androidx.compose.foundation.selection.selectableGroup
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.SegmentedButton
|
|
||||||
import androidx.compose.material3.SegmentedButtonDefaults
|
|
||||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.SideEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.lerp
|
||||||
|
import androidx.compose.ui.layout.layout
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.text.style.TextDecoration
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.darken.capod.R
|
|
||||||
import eu.darken.capod.common.compose.Preview2
|
import eu.darken.capod.common.compose.Preview2
|
||||||
import eu.darken.capod.common.compose.PreviewWrapper
|
import eu.darken.capod.common.compose.PreviewWrapper
|
||||||
import eu.darken.capod.main.ui.components.icon
|
import eu.darken.capod.main.ui.components.icon
|
||||||
import eu.darken.capod.main.ui.components.shortLabelRes
|
import eu.darken.capod.main.ui.components.shortLabelRes
|
||||||
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
import eu.darken.capod.pods.core.apple.aap.protocol.AapSetting
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
private val TrackShape = RoundedCornerShape(16.dp)
|
||||||
|
private val ThumbShape = RoundedCornerShape(12.dp)
|
||||||
|
private val TrackInset = 4.dp
|
||||||
|
private val TrackHeight = 48.dp
|
||||||
|
private val CollapsedSlotWidth = 52.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listening-mode picker: one thumb travelling across a filled track, naming only the mode it sits
|
||||||
|
* on. Inactive modes stay icon-sized, which keeps the active label free of the width budget and
|
||||||
|
* therefore readable in every locale.
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun AncModeSelector(
|
fun AncModeSelector(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
@@ -38,73 +78,212 @@ fun AncModeSelector(
|
|||||||
pendingMode: AapSetting.AncMode.Value? = null,
|
pendingMode: AapSetting.AncMode.Value? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
) {
|
) {
|
||||||
val displayMode = pendingMode ?: currentMode
|
if (supportedModes.isEmpty()) return
|
||||||
Box(modifier = modifier.fillMaxWidth()) {
|
|
||||||
SingleChoiceSegmentedButtonRow(modifier = Modifier.fillMaxWidth()) {
|
val currentIndex = supportedModes.indexOf(currentMode)
|
||||||
supportedModes.forEachIndexed { index, mode ->
|
// A requested mode can be missing from the list (e.g. one filtered out of the listening-mode
|
||||||
SegmentedButton(
|
// cycle), in which case the thumb stays on the mode the pods are actually in.
|
||||||
selected = mode == displayMode,
|
val requestedIndex = supportedModes.indexOf(pendingMode ?: currentMode)
|
||||||
onClick = { onModeSelected(mode) },
|
val targetIndex = if (requestedIndex >= 0) requestedIndex else currentIndex
|
||||||
enabled = enabled,
|
val isPending = pendingMode != null && pendingMode != currentMode
|
||||||
shape = SegmentedButtonDefaults.itemShape(index, supportedModes.size),
|
|
||||||
contentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp),
|
// With neither mode on the list there is nothing to highlight: park the thumb where it was and
|
||||||
colors = if (pendingMode != null && mode == displayMode) {
|
// fade it out rather than falsely marking the first slot as active.
|
||||||
SegmentedButtonDefaults.colors(
|
val lastKnownIndex = remember { mutableIntStateOf(targetIndex.coerceAtLeast(0)) }
|
||||||
activeContainerColor = MaterialTheme.colorScheme.secondaryContainer,
|
val thumbIndex = if (targetIndex >= 0) targetIndex else lastKnownIndex.intValue
|
||||||
activeContentColor = MaterialTheme.colorScheme.onSecondaryContainer,
|
SideEffect { if (targetIndex >= 0) lastKnownIndex.intValue = targetIndex }
|
||||||
)
|
|
||||||
} else {
|
val animatedIndex by animateFloatAsState(
|
||||||
SegmentedButtonDefaults.colors()
|
targetValue = thumbIndex.toFloat(),
|
||||||
},
|
animationSpec = spring(dampingRatio = 0.72f, stiffness = Spring.StiffnessMediumLow),
|
||||||
icon = {},
|
label = "ancThumbPosition",
|
||||||
label = {
|
)
|
||||||
val isSelected = mode == displayMode
|
val thumbAlpha by animateFloatAsState(
|
||||||
Column(
|
targetValue = if (targetIndex >= 0) 1f else 0f,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
animationSpec = tween(200),
|
||||||
verticalArrangement = Arrangement.Center,
|
label = "ancThumbAlpha",
|
||||||
modifier = Modifier
|
)
|
||||||
.fillMaxWidth()
|
// While a mode change is in flight the thumb stays hollow and breathes; it only fills in solid
|
||||||
.padding(
|
// once the pods confirm the new mode.
|
||||||
start = if (index == 0) 6.dp else 0.dp,
|
val pendingFill = if (isPending) {
|
||||||
end = if (index == supportedModes.lastIndex) 6.dp else 0.dp,
|
val pulse by rememberInfiniteTransition(label = "ancPending").animateFloat(
|
||||||
top = 4.dp,
|
initialValue = 0.10f,
|
||||||
bottom = 4.dp,
|
targetValue = 0.30f,
|
||||||
),
|
animationSpec = infiniteRepeatable(tween(900), RepeatMode.Reverse),
|
||||||
) {
|
label = "ancPendingPulse",
|
||||||
Icon(
|
)
|
||||||
imageVector = mode.icon(),
|
pulse
|
||||||
contentDescription = null,
|
} else {
|
||||||
modifier = Modifier.size(18.dp),
|
0f
|
||||||
)
|
}
|
||||||
Text(
|
|
||||||
text = stringResource(mode.shortLabelRes()),
|
val accent = MaterialTheme.colorScheme.primary
|
||||||
maxLines = 1,
|
val activeContent = if (isPending) accent else MaterialTheme.colorScheme.onPrimary
|
||||||
overflow = TextOverflow.Ellipsis,
|
val idleContent = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
textAlign = TextAlign.Center,
|
|
||||||
style = MaterialTheme.typography.labelSmall,
|
Surface(
|
||||||
fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Medium,
|
modifier = modifier
|
||||||
textDecoration = if (isSelected) TextDecoration.Underline else TextDecoration.None,
|
.fillMaxWidth()
|
||||||
modifier = Modifier.fillMaxWidth(),
|
.then(if (enabled) Modifier else Modifier.alpha(0.5f)),
|
||||||
)
|
shape = TrackShape,
|
||||||
}
|
// Same tonal step as the battery panel above it, so the two read as siblings and the thumb
|
||||||
},
|
// is the only thing in the card competing with the gauges for attention.
|
||||||
)
|
tonalElevation = 4.dp,
|
||||||
|
) {
|
||||||
|
BoxWithConstraints(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(TrackInset)
|
||||||
|
.height(TrackHeight),
|
||||||
|
) {
|
||||||
|
val slots = supportedModes.size
|
||||||
|
val equalWidth = maxWidth / slots.toFloat()
|
||||||
|
val collapsedWidth = minOf(CollapsedSlotWidth, equalWidth)
|
||||||
|
val expandedWidth = maxWidth - collapsedWidth * (slots - 1)
|
||||||
|
|
||||||
|
// The spring is underdamped and overshoots past the end slots; clamp before deriving any
|
||||||
|
// geometry, otherwise the reveals stop summing to 1 and the slots no longer fill the row.
|
||||||
|
val position = animatedIndex.coerceIn(0f, (slots - 1).toFloat())
|
||||||
|
// Distance to the (fractional) thumb position drives slot width, label reveal and tint
|
||||||
|
// alike, so geometry and content stay in lockstep for the whole travel.
|
||||||
|
val reveals = supportedModes.indices.map { (1f - abs(it - position)).coerceIn(0f, 1f) }
|
||||||
|
val widths = reveals.map { collapsedWidth + (expandedWidth - collapsedWidth) * it }
|
||||||
|
// Everything left of the thumb is collapsed by definition, so its leading edge is just
|
||||||
|
// the fractional position in collapsed-slot units. Summing the animating widths instead
|
||||||
|
// would overshoot the target and crawl back.
|
||||||
|
val thumbOffset = collapsedWidth * position
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.offset(x = thumbOffset)
|
||||||
|
.width(expandedWidth)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.alpha(thumbAlpha)
|
||||||
|
.clip(ThumbShape)
|
||||||
|
.background(if (isPending) accent.copy(alpha = pendingFill) else accent)
|
||||||
|
.then(if (isPending) Modifier.border(1.5.dp, accent, ThumbShape) else Modifier),
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.selectableGroup(),
|
||||||
|
) {
|
||||||
|
supportedModes.forEachIndexed { index, mode ->
|
||||||
|
AncModeSlot(
|
||||||
|
mode = mode,
|
||||||
|
label = stringResource(mode.shortLabelRes()),
|
||||||
|
reveal = reveals[index],
|
||||||
|
width = widths[index],
|
||||||
|
contentColor = when {
|
||||||
|
// Mid-flight the still-active mode keeps an accent tint, so it stays
|
||||||
|
// readable what the pods are doing while the request is unconfirmed.
|
||||||
|
isPending && index == currentIndex -> accent
|
||||||
|
else -> lerp(idleContent, activeContent, reveals[index])
|
||||||
|
},
|
||||||
|
selected = index == targetIndex,
|
||||||
|
enabled = enabled,
|
||||||
|
onClick = { onModeSelected(mode) },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AncModeSlot(
|
||||||
|
mode: AapSetting.AncMode.Value,
|
||||||
|
label: String,
|
||||||
|
reveal: Float,
|
||||||
|
width: Dp,
|
||||||
|
contentColor: Color,
|
||||||
|
selected: Boolean,
|
||||||
|
enabled: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(width)
|
||||||
|
.fillMaxHeight()
|
||||||
|
.clip(ThumbShape)
|
||||||
|
.selectable(
|
||||||
|
selected = selected,
|
||||||
|
enabled = enabled,
|
||||||
|
role = Role.RadioButton,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
// Named on the slot itself rather than on whichever child happens to be visible: the
|
||||||
|
// label only exists once it has revealed, so deriving the name from the children would
|
||||||
|
// leave the freshly selected mode unnamed for the length of the animation.
|
||||||
|
.semantics { contentDescription = label }
|
||||||
|
.clipToBounds(),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier.padding(horizontal = 2.dp),
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = mode.icon(),
|
||||||
|
contentDescription = null,
|
||||||
|
tint = contentColor,
|
||||||
|
modifier = Modifier.size(20.dp),
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(6.dp * reveal))
|
||||||
|
RevealingLabel(text = label, reveal = reveal, color = contentColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label that wipes in from the left as [reveal] grows: it is measured at its natural width but
|
||||||
|
* reports only a fraction of it, so the row around it grows continuously instead of jumping the
|
||||||
|
* moment the text appears.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
private fun RevealingLabel(
|
||||||
|
text: String,
|
||||||
|
reveal: Float,
|
||||||
|
color: Color,
|
||||||
|
) {
|
||||||
|
if (reveal <= 0f) return
|
||||||
|
Box(modifier = Modifier.clipToBounds()) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.labelLarge,
|
||||||
|
color = color,
|
||||||
|
maxLines = 1,
|
||||||
|
softWrap = false,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
// The slot already carries the accessible name; the text is decoration on top of it.
|
||||||
|
modifier = Modifier
|
||||||
|
.clearAndSetSemantics { }
|
||||||
|
.alpha(reveal)
|
||||||
|
.layout { measurable, constraints ->
|
||||||
|
val placeable = measurable.measure(constraints.copy(minWidth = 0))
|
||||||
|
layout((placeable.width * reveal).roundToInt(), placeable.height) {
|
||||||
|
// Relative, so RTL anchors the text at its start edge and wipes in from the
|
||||||
|
// right instead of exposing the tail of the word.
|
||||||
|
placeable.placeRelative(0, 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val PreviewModes = listOf(
|
||||||
|
AapSetting.AncMode.Value.OFF,
|
||||||
|
AapSetting.AncMode.Value.ON,
|
||||||
|
AapSetting.AncMode.Value.TRANSPARENCY,
|
||||||
|
AapSetting.AncMode.Value.ADAPTIVE,
|
||||||
|
)
|
||||||
|
|
||||||
@Preview2
|
@Preview2
|
||||||
@Composable
|
@Composable
|
||||||
private fun AncModeSelectorPreview() = PreviewWrapper {
|
private fun AncModeSelectorPreview() = PreviewWrapper {
|
||||||
AncModeSelector(
|
AncModeSelector(
|
||||||
currentMode = AapSetting.AncMode.Value.ON,
|
currentMode = AapSetting.AncMode.Value.ON,
|
||||||
supportedModes = listOf(
|
supportedModes = PreviewModes,
|
||||||
AapSetting.AncMode.Value.OFF,
|
|
||||||
AapSetting.AncMode.Value.ON,
|
|
||||||
AapSetting.AncMode.Value.TRANSPARENCY,
|
|
||||||
AapSetting.AncMode.Value.ADAPTIVE,
|
|
||||||
),
|
|
||||||
onModeSelected = {},
|
onModeSelected = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -114,12 +293,19 @@ private fun AncModeSelectorPreview() = PreviewWrapper {
|
|||||||
private fun AncModeSelectorPendingPreview() = PreviewWrapper {
|
private fun AncModeSelectorPendingPreview() = PreviewWrapper {
|
||||||
AncModeSelector(
|
AncModeSelector(
|
||||||
currentMode = AapSetting.AncMode.Value.ON,
|
currentMode = AapSetting.AncMode.Value.ON,
|
||||||
supportedModes = listOf(
|
supportedModes = PreviewModes.dropLast(1),
|
||||||
AapSetting.AncMode.Value.OFF,
|
|
||||||
AapSetting.AncMode.Value.ON,
|
|
||||||
AapSetting.AncMode.Value.TRANSPARENCY,
|
|
||||||
),
|
|
||||||
onModeSelected = {},
|
onModeSelected = {},
|
||||||
pendingMode = AapSetting.AncMode.Value.TRANSPARENCY,
|
pendingMode = AapSetting.AncMode.Value.TRANSPARENCY,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview2
|
||||||
|
@Composable
|
||||||
|
private fun AncModeSelectorDisabledPreview() = PreviewWrapper {
|
||||||
|
AncModeSelector(
|
||||||
|
currentMode = AapSetting.AncMode.Value.ADAPTIVE,
|
||||||
|
supportedModes = PreviewModes,
|
||||||
|
onModeSelected = {},
|
||||||
|
enabled = false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 178 KiB |
|
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 194 KiB |
|
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 181 KiB |