Loading packages/SettingsLib/Spa/spa/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ android_library { "androidx.window_window", "com.google.android.material_material", "lottie_compose", "elib", ], kotlincflags: [ "-Xjvm-default=all", Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt +1 −3 Original line number Diff line number Diff line Loading @@ -146,9 +146,7 @@ internal fun InternalSwitchPreference( SettingsSwitch( checked = checked, changeable = { changeable }, // The onCheckedChange is handled on the whole SwitchPreference. // DO NOT set it on SettingsSwitch. onCheckedChange = null, onCheckedChange = onChangeWithLog, interactionSource = interactionSource, ) } Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt +79 −32 Original line number Diff line number Diff line Loading @@ -16,8 +16,20 @@ package com.android.settingslib.spa.widget.ui import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.spring import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close Loading @@ -25,11 +37,18 @@ import androidx.compose.material3.Icon import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.colorResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.compose.contentDescription import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog import foundation.e.elib.R import kotlin.math.roundToInt @Composable internal fun SettingsSwitch( Loading @@ -40,48 +59,76 @@ internal fun SettingsSwitch( interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { if (checked != null) { Switch( ESwitch( checked = checked, onCheckedChange = wrapOnSwitchWithLog(onCheckedChange), modifier = Modifier.contentDescription(contentDescription), enabled = changeable(), interactionSource = interactionSource, thumbContent = if (isSpaExpressiveEnabled) { if (checked) { { Icon( imageVector = Icons.Filled.Check, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) } } else { { Icon( imageVector = Icons.Filled.Close, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) } } } else null) } else { Switch( ESwitch( checked = false, onCheckedChange = null, enabled = false, interactionSource = interactionSource, thumbContent = if (isSpaExpressiveEnabled) { { Icon( imageVector = Icons.Filled.Close, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) } } else null } @Composable fun ESwitch( checked: Boolean, onCheckedChange: ((newChecked: Boolean) -> Unit)? = null, enabled: Boolean, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { val thumbSize = 24.dp val trackWidth = 52.dp val trackHeight = 28.dp val switchPadding = 2.dp val transition = updateTransition(targetState = checked, label = "switch") val thumbOffsetX by transition.animateDp( transitionSpec = { spring( dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessMediumLow ) }, label = "Thumb offset" ) { state -> if (state) trackWidth - thumbSize - switchPadding else switchPadding } val trackColor = if (checked && enabled) colorResource(R.color.e_switch_track_on) else colorResource(R.color.e_switch_track_off) val thumbColor = if (checked && enabled) colorResource(R.color.e_switch_thumb_on) else colorResource(R.color.e_switch_thumb_off) Box( modifier = Modifier.width(trackWidth) .height(trackHeight) .clip(RoundedCornerShape(14.dp)) .background(color = trackColor) .clickable( enabled = enabled, // Handle whether switch can be clicked onClick = { onCheckedChange?.invoke(!checked) }, interactionSource = interactionSource, indication = null ) ) { Box( modifier = Modifier.size(thumbSize) .offset { IntOffset(thumbOffsetX.roundToPx(), switchPadding.toPx().roundToInt()) } .clip(CircleShape) .background(color = thumbColor) ) } } Loading
packages/SettingsLib/Spa/spa/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -36,6 +36,7 @@ android_library { "androidx.window_window", "com.google.android.material_material", "lottie_compose", "elib", ], kotlincflags: [ "-Xjvm-default=all", Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt +1 −3 Original line number Diff line number Diff line Loading @@ -146,9 +146,7 @@ internal fun InternalSwitchPreference( SettingsSwitch( checked = checked, changeable = { changeable }, // The onCheckedChange is handled on the whole SwitchPreference. // DO NOT set it on SettingsSwitch. onCheckedChange = null, onCheckedChange = onChangeWithLog, interactionSource = interactionSource, ) } Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt +79 −32 Original line number Diff line number Diff line Loading @@ -16,8 +16,20 @@ package com.android.settingslib.spa.widget.ui import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.spring import androidx.compose.animation.core.updateTransition import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close Loading @@ -25,11 +37,18 @@ import androidx.compose.material3.Icon import androidx.compose.material3.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.colorResource import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.compose.contentDescription import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog import foundation.e.elib.R import kotlin.math.roundToInt @Composable internal fun SettingsSwitch( Loading @@ -40,48 +59,76 @@ internal fun SettingsSwitch( interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { if (checked != null) { Switch( ESwitch( checked = checked, onCheckedChange = wrapOnSwitchWithLog(onCheckedChange), modifier = Modifier.contentDescription(contentDescription), enabled = changeable(), interactionSource = interactionSource, thumbContent = if (isSpaExpressiveEnabled) { if (checked) { { Icon( imageVector = Icons.Filled.Check, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) } } else { { Icon( imageVector = Icons.Filled.Close, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) } } } else null) } else { Switch( ESwitch( checked = false, onCheckedChange = null, enabled = false, interactionSource = interactionSource, thumbContent = if (isSpaExpressiveEnabled) { { Icon( imageVector = Icons.Filled.Close, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) } } else null } @Composable fun ESwitch( checked: Boolean, onCheckedChange: ((newChecked: Boolean) -> Unit)? = null, enabled: Boolean, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } ) { val thumbSize = 24.dp val trackWidth = 52.dp val trackHeight = 28.dp val switchPadding = 2.dp val transition = updateTransition(targetState = checked, label = "switch") val thumbOffsetX by transition.animateDp( transitionSpec = { spring( dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessMediumLow ) }, label = "Thumb offset" ) { state -> if (state) trackWidth - thumbSize - switchPadding else switchPadding } val trackColor = if (checked && enabled) colorResource(R.color.e_switch_track_on) else colorResource(R.color.e_switch_track_off) val thumbColor = if (checked && enabled) colorResource(R.color.e_switch_thumb_on) else colorResource(R.color.e_switch_thumb_off) Box( modifier = Modifier.width(trackWidth) .height(trackHeight) .clip(RoundedCornerShape(14.dp)) .background(color = trackColor) .clickable( enabled = enabled, // Handle whether switch can be clicked onClick = { onCheckedChange?.invoke(!checked) }, interactionSource = interactionSource, indication = null ) ) { Box( modifier = Modifier.size(thumbSize) .offset { IntOffset(thumbOffsetX.roundToPx(), switchPadding.toPx().roundToInt()) } .clip(CircleShape) .background(color = thumbColor) ) } }