Loading packages/SettingsLib/Spa/spa/Android.bp +1 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ android_library { "androidx.window_window", "androidx.window_window", "com.google.android.material_material", "com.google.android.material_material", "lottie_compose", "lottie_compose", "elib", ], ], kotlincflags: [ kotlincflags: [ "-Xjvm-default=all", "-Xjvm-default=all", Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt +1 −3 Original line number Original line Diff line number Diff line Loading @@ -146,9 +146,7 @@ internal fun InternalSwitchPreference( SettingsSwitch( SettingsSwitch( checked = checked, checked = checked, changeable = { changeable }, changeable = { changeable }, // The onCheckedChange is handled on the whole SwitchPreference. onCheckedChange = onChangeWithLog, // DO NOT set it on SettingsSwitch. onCheckedChange = null, interactionSource = interactionSource, interactionSource = interactionSource, ) ) } } Loading packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt +79 −32 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,20 @@ package com.android.settingslib.spa.widget.ui 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.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.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.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close 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.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.SwitchDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.remember import androidx.compose.ui.Modifier 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.compose.contentDescription import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog import foundation.e.elib.R import kotlin.math.roundToInt @Composable @Composable internal fun SettingsSwitch( internal fun SettingsSwitch( Loading @@ -40,48 +59,76 @@ internal fun SettingsSwitch( interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { ) { if (checked != null) { if (checked != null) { Switch( ESwitch( checked = checked, checked = checked, onCheckedChange = wrapOnSwitchWithLog(onCheckedChange), onCheckedChange = wrapOnSwitchWithLog(onCheckedChange), modifier = Modifier.contentDescription(contentDescription), enabled = changeable(), enabled = changeable(), interactionSource = interactionSource, 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 { } else { Switch( ESwitch( checked = false, checked = false, onCheckedChange = null, onCheckedChange = null, enabled = false, enabled = false, interactionSource = interactionSource, 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 Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ android_library { "androidx.window_window", "androidx.window_window", "com.google.android.material_material", "com.google.android.material_material", "lottie_compose", "lottie_compose", "elib", ], ], kotlincflags: [ kotlincflags: [ "-Xjvm-default=all", "-Xjvm-default=all", Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt +1 −3 Original line number Original line Diff line number Diff line Loading @@ -146,9 +146,7 @@ internal fun InternalSwitchPreference( SettingsSwitch( SettingsSwitch( checked = checked, checked = checked, changeable = { changeable }, changeable = { changeable }, // The onCheckedChange is handled on the whole SwitchPreference. onCheckedChange = onChangeWithLog, // DO NOT set it on SettingsSwitch. onCheckedChange = null, interactionSource = interactionSource, interactionSource = interactionSource, ) ) } } Loading
packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Switch.kt +79 −32 Original line number Original line Diff line number Diff line Loading @@ -16,8 +16,20 @@ package com.android.settingslib.spa.widget.ui 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.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.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.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Close 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.Switch import androidx.compose.material3.SwitchDefaults import androidx.compose.material3.SwitchDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.runtime.remember import androidx.compose.ui.Modifier 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.compose.contentDescription import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog import com.android.settingslib.spa.framework.util.wrapOnSwitchWithLog import foundation.e.elib.R import kotlin.math.roundToInt @Composable @Composable internal fun SettingsSwitch( internal fun SettingsSwitch( Loading @@ -40,48 +59,76 @@ internal fun SettingsSwitch( interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, ) { ) { if (checked != null) { if (checked != null) { Switch( ESwitch( checked = checked, checked = checked, onCheckedChange = wrapOnSwitchWithLog(onCheckedChange), onCheckedChange = wrapOnSwitchWithLog(onCheckedChange), modifier = Modifier.contentDescription(contentDescription), enabled = changeable(), enabled = changeable(), interactionSource = interactionSource, 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 { } else { Switch( ESwitch( checked = false, checked = false, onCheckedChange = null, onCheckedChange = null, enabled = false, enabled = false, interactionSource = interactionSource, 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) ) ) } } } }