From 370a4a6f4135cc9c53a963c79c3b69937acf376d Mon Sep 17 00:00:00 2001 From: Kelly Date: Wed, 19 Oct 2022 10:05:11 +0800 Subject: Extract slider in SettingsSlider to a simple ui widget and rename the slider preference widget to SliderPreference. Test: Manually Test on device and unit test. Bug: 241857459 Change-Id: I90302f997211213f9d91987bf9d19acba088035a --- .../settingslib/spa/gallery/page/SliderPage.kt | 12 +- .../settingslib/spa/widget/SettingsSlider.kt | 188 --------------------- .../spa/widget/preference/SliderPreference.kt | 182 ++++++++++++++++++++ .../settingslib/spa/widget/ui/SettingsSlider.kt | 49 ++++++ .../settingslib/spa/widget/SettingsSliderTest.kt | 45 ----- .../spa/widget/preference/SliderPreferenceTest.kt | 45 +++++ 6 files changed, 282 insertions(+), 239 deletions(-) delete mode 100644 packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt create mode 100644 packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt create mode 100644 packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt delete mode 100644 packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt create mode 100644 packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt index 0f95bf6f32c7..7567c6daf996 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/SliderPage.kt @@ -33,8 +33,8 @@ import com.android.settingslib.spa.framework.common.SettingsPage import com.android.settingslib.spa.framework.common.SettingsPageProvider import com.android.settingslib.spa.framework.compose.navigator import com.android.settingslib.spa.framework.theme.SettingsTheme -import com.android.settingslib.spa.widget.SettingsSlider -import com.android.settingslib.spa.widget.SettingsSliderModel +import com.android.settingslib.spa.widget.preference.SliderPreference +import com.android.settingslib.spa.widget.preference.SliderPreferenceModel import com.android.settingslib.spa.widget.preference.Preference import com.android.settingslib.spa.widget.preference.PreferenceModel import com.android.settingslib.spa.widget.scaffold.RegularScaffold @@ -51,7 +51,7 @@ object SliderPageProvider : SettingsPageProvider { SettingsEntryBuilder.create("Simple Slider", owner) .setIsAllowSearch(true) .setUiLayoutFn { - SettingsSlider(object : SettingsSliderModel { + SliderPreference(object : SliderPreferenceModel { override val title = "Simple Slider" override val initValue = 40 }) @@ -61,7 +61,7 @@ object SliderPageProvider : SettingsPageProvider { SettingsEntryBuilder.create("Slider with icon", owner) .setIsAllowSearch(true) .setUiLayoutFn { - SettingsSlider(object : SettingsSliderModel { + SliderPreference(object : SliderPreferenceModel { override val title = "Slider with icon" override val initValue = 30 override val onValueChangeFinished = { @@ -78,7 +78,7 @@ object SliderPageProvider : SettingsPageProvider { val initValue = 0 var icon by remember { mutableStateOf(Icons.Outlined.MusicOff) } var sliderPosition by remember { mutableStateOf(initValue) } - SettingsSlider(object : SettingsSliderModel { + SliderPreference(object : SliderPreferenceModel { override val title = "Slider with changeable icon" override val initValue = initValue override val onValueChange = { it: Int -> @@ -96,7 +96,7 @@ object SliderPageProvider : SettingsPageProvider { SettingsEntryBuilder.create("Slider with steps", owner) .setIsAllowSearch(true) .setUiLayoutFn { - SettingsSlider(object : SettingsSliderModel { + SliderPreference(object : SliderPreferenceModel { override val title = "Slider with steps" override val initValue = 2 override val valueRange = 1..5 diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt deleted file mode 100644 index 822ccc680039..000000000000 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/SettingsSlider.kt +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.spa.widget - -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.AccessAlarm -import androidx.compose.material.icons.outlined.MusicNote -import androidx.compose.material.icons.outlined.MusicOff -import androidx.compose.material3.Icon -import androidx.compose.material3.Slider -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.tooling.preview.Preview -import com.android.settingslib.spa.framework.theme.SettingsTheme -import com.android.settingslib.spa.framework.util.EntryHighlight -import com.android.settingslib.spa.widget.preference.BaseLayout -import kotlin.math.roundToInt - -/** - * The widget model for [SettingsSlider] widget. - */ -interface SettingsSliderModel { - /** - * The title of this [SettingsSlider]. - */ - val title: String - - /** - * The initial position of the [SettingsSlider]. - */ - val initValue: Int - - /** - * The value range for this [SettingsSlider]. - */ - val valueRange: IntRange - get() = 0..100 - - /** - * The lambda to be invoked during the value change by dragging or a click. This callback is - * used to get the real time value of the [SettingsSlider]. - */ - val onValueChange: ((value: Int) -> Unit)? - get() = null - - /** - * The lambda to be invoked when value change has ended. This callback is used to get when the - * user has completed selecting a new value by ending a drag or a click. - */ - val onValueChangeFinished: (() -> Unit)? - get() = null - - /** - * The icon image for [SettingsSlider]. If not specified, the slider hides the icon by default. - */ - val icon: ImageVector? - get() = null - - /** - * Indicates whether to show step marks. If show step marks, when user finish sliding, - * the slider will automatically jump to the nearest step mark. Otherwise, the slider hides - * the step marks by default. - * - * The step is fixed to 1. - */ - val showSteps: Boolean - get() = false -} - -/** - * Settings slider widget. - * - * Data is provided through [SettingsSliderModel]. - */ -@Composable -fun SettingsSlider(model: SettingsSliderModel) { - SettingsSlider( - title = model.title, - initValue = model.initValue, - valueRange = model.valueRange, - onValueChange = model.onValueChange, - onValueChangeFinished = model.onValueChangeFinished, - icon = model.icon, - showSteps = model.showSteps, - ) -} - -@Composable -internal fun SettingsSlider( - title: String, - initValue: Int, - modifier: Modifier = Modifier, - valueRange: IntRange = 0..100, - onValueChange: ((value: Int) -> Unit)? = null, - onValueChangeFinished: (() -> Unit)? = null, - icon: ImageVector? = null, - showSteps: Boolean = false, -) { - var sliderPosition by rememberSaveable { mutableStateOf(initValue.toFloat()) } - EntryHighlight { - BaseLayout( - title = title, - subTitle = { - Slider( - value = sliderPosition, - onValueChange = { - sliderPosition = it - onValueChange?.invoke(sliderPosition.roundToInt()) - }, - modifier = modifier, - valueRange = valueRange.first.toFloat()..valueRange.last.toFloat(), - steps = if (showSteps) (valueRange.count() - 2) else 0, - onValueChangeFinished = onValueChangeFinished, - ) - }, - icon = if (icon != null) ({ - Icon(imageVector = icon, contentDescription = null) - }) else null, - ) - } -} - -@Preview -@Composable -private fun SettingsSliderPreview() { - SettingsTheme { - val initValue = 30 - var sliderPosition by rememberSaveable { mutableStateOf(initValue) } - SettingsSlider( - title = "Alarm Volume", - initValue = 30, - onValueChange = { sliderPosition = it }, - onValueChangeFinished = { - println("onValueChangeFinished: the value is $sliderPosition") - }, - icon = Icons.Outlined.AccessAlarm, - ) - } -} - -@Preview -@Composable -private fun SettingsSliderIconChangePreview() { - SettingsTheme { - var icon by remember { mutableStateOf(Icons.Outlined.MusicNote) } - SettingsSlider( - title = "Media Volume", - initValue = 40, - onValueChange = { it: Int -> - icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff - }, - icon = icon, - ) - } -} - -@Preview -@Composable -private fun SettingsSliderStepsPreview() { - SettingsTheme { - SettingsSlider( - title = "Display Text", - initValue = 2, - valueRange = 1..5, - showSteps = true, - ) - } -} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt new file mode 100644 index 000000000000..7bca38fdb48f --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SliderPreference.kt @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.preference + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AccessAlarm +import androidx.compose.material.icons.outlined.MusicNote +import androidx.compose.material.icons.outlined.MusicOff +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.tooling.preview.Preview +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.framework.util.EntryHighlight +import com.android.settingslib.spa.widget.ui.SettingsSlider + +/** + * The widget model for [SliderPreference] widget. + */ +interface SliderPreferenceModel { + /** + * The title of this [SliderPreference]. + */ + val title: String + + /** + * The initial position of the [SliderPreference]. + */ + val initValue: Int + + /** + * The value range for this [SliderPreference]. + */ + val valueRange: IntRange + get() = 0..100 + + /** + * The lambda to be invoked during the value change by dragging or a click. This callback is + * used to get the real time value of the [SliderPreference]. + */ + val onValueChange: ((value: Int) -> Unit)? + get() = null + + /** + * The lambda to be invoked when value change has ended. This callback is used to get when the + * user has completed selecting a new value by ending a drag or a click. + */ + val onValueChangeFinished: (() -> Unit)? + get() = null + + /** + * The icon image for [SliderPreference]. If not specified, the slider hides the icon by default. + */ + val icon: ImageVector? + get() = null + + /** + * Indicates whether to show step marks. If show step marks, when user finish sliding, + * the slider will automatically jump to the nearest step mark. Otherwise, the slider hides + * the step marks by default. + * + * The step is fixed to 1. + */ + val showSteps: Boolean + get() = false +} + +/** + * Settings slider widget. + * + * Data is provided through [SliderPreferenceModel]. + */ +@Composable +fun SliderPreference(model: SliderPreferenceModel) { + EntryHighlight { + SliderPreference( + title = model.title, + initValue = model.initValue, + valueRange = model.valueRange, + onValueChange = model.onValueChange, + onValueChangeFinished = model.onValueChangeFinished, + icon = model.icon, + showSteps = model.showSteps, + ) + } +} + +@Composable +internal fun SliderPreference( + title: String, + initValue: Int, + modifier: Modifier = Modifier, + valueRange: IntRange = 0..100, + onValueChange: ((value: Int) -> Unit)? = null, + onValueChangeFinished: (() -> Unit)? = null, + icon: ImageVector? = null, + showSteps: Boolean = false, +) { + BaseLayout( + title = title, + subTitle = { + SettingsSlider( + initValue, + modifier, + valueRange, + onValueChange, + onValueChangeFinished, + showSteps + ) + }, + icon = if (icon != null) ({ + Icon(imageVector = icon, contentDescription = null) + }) else null, + ) +} + +@Preview +@Composable +private fun SliderPreferencePreview() { + SettingsTheme { + val initValue = 30 + var sliderPosition by rememberSaveable { mutableStateOf(initValue) } + SliderPreference( + title = "Alarm Volume", + initValue = 30, + onValueChange = { sliderPosition = it }, + onValueChangeFinished = { + println("onValueChangeFinished: the value is $sliderPosition") + }, + icon = Icons.Outlined.AccessAlarm, + ) + } +} + +@Preview +@Composable +private fun SliderPreferenceIconChangePreview() { + SettingsTheme { + var icon by remember { mutableStateOf(Icons.Outlined.MusicNote) } + SliderPreference( + title = "Media Volume", + initValue = 40, + onValueChange = { it: Int -> + icon = if (it > 0) Icons.Outlined.MusicNote else Icons.Outlined.MusicOff + }, + icon = icon, + ) + } +} + +@Preview +@Composable +private fun SliderPreferenceStepsPreview() { + SettingsTheme { + SliderPreference( + title = "Display Text", + initValue = 2, + valueRange = 1..5, + showSteps = true, + ) + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt new file mode 100644 index 000000000000..d8455e46ec4a --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/SettingsSlider.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.ui + +import androidx.compose.material3.Slider +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import kotlin.math.roundToInt + +@Composable +fun SettingsSlider( + initValue: Int, + modifier: Modifier = Modifier, + valueRange: IntRange = 0..100, + onValueChange: ((value: Int) -> Unit)? = null, + onValueChangeFinished: (() -> Unit)? = null, + showSteps: Boolean = false, +) { + var sliderPosition by rememberSaveable { mutableStateOf(initValue.toFloat()) } + Slider( + value = sliderPosition, + onValueChange = { + sliderPosition = it + onValueChange?.invoke(sliderPosition.roundToInt()) + }, + modifier = modifier, + valueRange = valueRange.first.toFloat()..valueRange.last.toFloat(), + steps = if (showSteps) (valueRange.count() - 2) else 0, + onValueChangeFinished = onValueChangeFinished, + ) +} diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt deleted file mode 100644 index 1d95e3383b51..000000000000 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/SettingsSliderTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.spa.widget - -import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.compose.ui.test.onNodeWithText -import androidx.test.ext.junit.runners.AndroidJUnit4 -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class SettingsSliderTest { - @get:Rule - val composeTestRule = createComposeRule() - - @Test - fun title_displayed() { - composeTestRule.setContent { - SettingsSlider(object : SettingsSliderModel { - override val title = "Slider" - override val initValue = 40 - }) - } - - composeTestRule.onNodeWithText("Slider").assertIsDisplayed() - } - - // TODO: Add more unit tests for SettingsSlider widget. -} diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt new file mode 100644 index 000000000000..7ae11758a0ce --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SliderPreferenceTest.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spa.widget.preference + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SliderPreferenceTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun title_displayed() { + composeTestRule.setContent { + SliderPreference(object : SliderPreferenceModel { + override val title = "Slider" + override val initValue = 40 + }) + } + + composeTestRule.onNodeWithText("Slider").assertIsDisplayed() + } + + // TODO: Add more unit tests for SliderPreference widget. +} -- cgit v1.2.3-59-g8ed1b