diff options
11 files changed, 574 insertions, 5 deletions
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt index 01596d2bc004..d62b4907e96c 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt @@ -40,6 +40,7 @@ import com.android.settingslib.spa.gallery.page.LoadingBarPageProvider import com.android.settingslib.spa.gallery.page.ProgressBarPageProvider import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider import com.android.settingslib.spa.gallery.page.SliderPageProvider +import com.android.settingslib.spa.gallery.preference.ListPreferencePageProvider import com.android.settingslib.spa.gallery.preference.MainSwitchPreferencePageProvider import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider import com.android.settingslib.spa.gallery.preference.PreferencePageProvider @@ -74,6 +75,7 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) { PreferencePageProvider, SwitchPreferencePageProvider, MainSwitchPreferencePageProvider, + ListPreferencePageProvider, TwoTargetSwitchPreferencePageProvider, ArgumentPageProvider, SliderPageProvider, diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt new file mode 100644 index 000000000000..43b6d0b05696 --- /dev/null +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/ListPreferencePageProvider.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 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.gallery.preference + +import android.os.Bundle +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.settingslib.spa.framework.common.SettingsEntryBuilder +import com.android.settingslib.spa.framework.common.SettingsPageProvider +import com.android.settingslib.spa.framework.common.createSettingsPage +import com.android.settingslib.spa.framework.compose.navigator +import com.android.settingslib.spa.framework.theme.SettingsTheme +import com.android.settingslib.spa.widget.preference.ListPreference +import com.android.settingslib.spa.widget.preference.ListPreferenceModel +import com.android.settingslib.spa.widget.preference.ListPreferenceOption +import com.android.settingslib.spa.widget.preference.Preference +import com.android.settingslib.spa.widget.preference.PreferenceModel +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.flow + +private const val TITLE = "Sample ListPreference" + +object ListPreferencePageProvider : SettingsPageProvider { + override val name = "ListPreference" + private val owner = createSettingsPage() + + override fun buildEntry(arguments: Bundle?) = listOf( + SettingsEntryBuilder.create("ListPreference", owner) + .setUiLayoutFn { + SampleListPreference() + }.build(), + SettingsEntryBuilder.create("ListPreference not changeable", owner) + .setUiLayoutFn { + SampleNotChangeableListPreference() + }.build(), + ) + + fun buildInjectEntry(): SettingsEntryBuilder { + return SettingsEntryBuilder.createInject(owner) + .setUiLayoutFn { + Preference(object : PreferenceModel { + override val title = TITLE + override val onClick = navigator(name) + }) + } + } + + override fun getTitle(arguments: Bundle?) = TITLE +} + +@Composable +private fun SampleListPreference() { + val selectedId = rememberSaveable { mutableIntStateOf(1) } + ListPreference(remember { + object : ListPreferenceModel { + override val title = "Preferred network type" + override val options = listOf( + ListPreferenceOption(id = 1, text = "5G (recommended)"), + ListPreferenceOption(id = 2, text = "LTE"), + ListPreferenceOption(id = 3, text = "3G"), + ) + override val selectedId = selectedId + override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it } + } + }) +} + +@Composable +private fun SampleNotChangeableListPreference() { + val selectedId = rememberSaveable { mutableIntStateOf(1) } + val enableFlow = flow { + var enabled = true + while (true) { + delay(3.seconds) + enabled = !enabled + emit(enabled) + } + } + val enabled = enableFlow.collectAsStateWithLifecycle(initialValue = true) + ListPreference(remember { + object : ListPreferenceModel { + override val title = "Preferred network type" + override val enabled = enabled + override val options = listOf( + ListPreferenceOption(id = 1, text = "5G (recommended)"), + ListPreferenceOption(id = 2, text = "LTE"), + ListPreferenceOption(id = 3, text = "3G"), + ) + override val selectedId = selectedId + override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it } + } + }) +} + +@Preview +@Composable +private fun ListPreferencePagePreview() { + SettingsTheme { + ListPreferencePageProvider.Page(null) + } +} diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt index eddede752d06..ce9678bab684 100644 --- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMain.kt +++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferenceMainPageProvider.kt @@ -36,6 +36,7 @@ object PreferenceMainPageProvider : SettingsPageProvider { PreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(), SwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(), MainSwitchPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(), + ListPreferencePageProvider.buildInjectEntry().setLink(fromPage = owner).build(), TwoTargetSwitchPreferencePageProvider.buildInjectEntry() .setLink(fromPage = owner).build(), ) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt index 7962e601999a..4088ffd43986 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt @@ -40,8 +40,18 @@ object SettingsDimension { /** The size when app icon is displayed in App info page. */ val appIconInfoSize = 48.dp + /** The vertical padding for buttons. */ + val buttonPaddingVertical = 12.dp + /** The [PaddingValues] for buttons. */ - val buttonPadding = PaddingValues(horizontal = itemPaddingEnd, vertical = 12.dp) + val buttonPadding = PaddingValues(horizontal = itemPaddingEnd, vertical = buttonPaddingVertical) + + /** The horizontal padding for dialog items. */ + val dialogItemPaddingHorizontal = itemPaddingStart + + /** The [PaddingValues] for dialog items. */ + val dialogItemPadding = + PaddingValues(horizontal = dialogItemPaddingHorizontal, vertical = buttonPaddingVertical) /** The sizes info of illustration widget. */ val illustrationMaxWidth = 412.dp diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt index c8faef6d6703..a9cd0e9cd2e9 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsOpacity.kt @@ -16,10 +16,15 @@ package com.android.settingslib.spa.framework.theme +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha + object SettingsOpacity { const val Full = 1f const val Disabled = 0.38f const val Divider = 0.2f const val SurfaceTone = 0.14f const val Hint = 0.9f + + fun Modifier.alphaForEnabled(enabled: Boolean) = alpha(if (enabled) Full else Disabled) } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt new file mode 100644 index 000000000000..8b172da08dd2 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsDialog.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 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.dialog + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Card +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.window.Dialog +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsShape +import com.android.settingslib.spa.widget.ui.SettingsTitle + +@Composable +fun SettingsDialog( + title: String, + onDismissRequest: () -> Unit, + content: @Composable () -> Unit, +) { + Dialog(onDismissRequest = onDismissRequest) { + Card(shape = SettingsShape.CornerExtraLarge) { + Column(modifier = Modifier.padding(vertical = SettingsDimension.itemPaddingAround)) { + Box(modifier = Modifier.padding(SettingsDimension.dialogItemPadding)) { + SettingsTitle(title = title, useMediumWeight = true) + } + content() + } + } + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt index 6330ddf5bea4..4d42fbae24fd 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/BaseLayout.kt @@ -29,13 +29,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.alpha import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.compose.toState import com.android.settingslib.spa.framework.theme.SettingsDimension -import com.android.settingslib.spa.framework.theme.SettingsOpacity +import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.widget.ui.SettingsTitle @@ -57,8 +56,7 @@ internal fun BaseLayout( .padding(end = paddingEnd), verticalAlignment = Alignment.CenterVertically, ) { - val alphaModifier = - Modifier.alpha(if (enabled.value) SettingsOpacity.Full else SettingsOpacity.Disabled) + val alphaModifier = Modifier.alphaForEnabled(enabled.value) BaseIcon(icon, alphaModifier, paddingStart) Titles( title = title, diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt new file mode 100644 index 000000000000..19779f67ca48 --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2023 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.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.selection.selectable +import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.material3.RadioButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.IntState +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +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.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.semantics.Role +import com.android.settingslib.spa.framework.compose.stateOf +import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.widget.dialog.SettingsDialog +import com.android.settingslib.spa.widget.ui.SettingsDialogItem + +data class ListPreferenceOption( + val id: Int, + val text: String, +) + +/** + * The widget model for [ListPreference] widget. + */ +interface ListPreferenceModel { + /** + * The title of this [ListPreference]. + */ + val title: String + + /** + * The icon of this [ListPreference]. + * + * Default is `null` which means no icon. + */ + val icon: (@Composable () -> Unit)? + get() = null + + /** + * Indicates whether this [ListPreference] is enabled. + * + * Disabled [ListPreference] will be displayed in disabled style. + */ + val enabled: State<Boolean> + get() = stateOf(true) + + val options: List<ListPreferenceOption> + + val selectedId: IntState + + val onIdSelected: (id: Int) -> Unit +} + +@Composable +fun ListPreference(model: ListPreferenceModel) { + var dialogOpened by rememberSaveable { mutableStateOf(false) } + if (dialogOpened) { + SettingsDialog( + title = model.title, + onDismissRequest = { dialogOpened = false }, + ) { + Column(modifier = Modifier.selectableGroup()) { + for (option in model.options) { + Radio(option, model.selectedId, model.enabled) { + dialogOpened = false + model.onIdSelected(it) + } + } + } + } + } + Preference(model = remember(model) { + object : PreferenceModel { + override val title = model.title + override val summary = derivedStateOf { + model.options.find { it.id == model.selectedId.intValue }?.text ?: "" + } + override val icon = model.icon + override val enabled = model.enabled + override val onClick = { dialogOpened = true }.takeIf { model.options.isNotEmpty() } + } + }) +} + +@Composable +private fun Radio( + option: ListPreferenceOption, + selectedId: IntState, + enabledState: State<Boolean>, + onIdSelected: (id: Int) -> Unit, +) { + val selected = option.id == selectedId.intValue + val enabled = enabledState.value + Row( + modifier = Modifier + .fillMaxWidth() + .selectable( + selected = selected, + enabled = enabled, + onClick = { onIdSelected(option.id) }, + role = Role.RadioButton, + ) + .padding(SettingsDimension.dialogItemPadding), + verticalAlignment = Alignment.CenterVertically, + ) { + RadioButton(selected = selected, onClick = null, enabled = enabled) + Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd)) + SettingsDialogItem(text = option.text, enabled = enabled) + } +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt index 57319e760c69..7f1acffe7a8a 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Text.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.android.settingslib.spa.framework.theme.SettingsDimension +import com.android.settingslib.spa.framework.theme.SettingsOpacity.alphaForEnabled import com.android.settingslib.spa.framework.theme.SettingsTheme import com.android.settingslib.spa.framework.theme.toMediumWeight @@ -48,6 +49,17 @@ fun SettingsTitle(title: String, useMediumWeight: Boolean = false) { } @Composable +fun SettingsDialogItem(text: String, enabled: Boolean = true) { + Text( + text = text, + modifier = Modifier.alphaForEnabled(enabled), + color = MaterialTheme.colorScheme.onSurface, + style = MaterialTheme.typography.bodyLarge, + overflow = TextOverflow.Ellipsis, + ) +} + +@Composable fun SettingsBody( body: String, maxLines: Int = Int.MAX_VALUE, @@ -82,6 +94,9 @@ fun PlaceholderTitle(title: String) { private fun BasePreferencePreview() { SettingsTheme { Column(Modifier.width(100.dp)) { + SettingsTitle( + title = "Title", + ) SettingsBody( body = "Long long long long long long text", ) diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt new file mode 100644 index 000000000000..c7582b2601af --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsDialogTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 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.dialog + +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.onDialogText +import com.android.settingslib.spa.widget.ui.SettingsDialogItem +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SettingsDialogTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun title_displayed() { + composeTestRule.setContent { + SettingsDialog(title = TITLE, onDismissRequest = {}) {} + } + + composeTestRule.onDialogText(TITLE).assertIsDisplayed() + } + + @Test + fun text_displayed() { + composeTestRule.setContent { + SettingsDialog(title = "", onDismissRequest = {}) { + SettingsDialogItem(text = TEXT) + } + } + + composeTestRule.onDialogText(TEXT).assertIsDisplayed() + } + + private companion object { + const val TITLE = "Title" + const val TEXT = "Text" + } +} diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt new file mode 100644 index 000000000000..997a02369d9f --- /dev/null +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2023 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.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsNotEnabled +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.framework.compose.stateOf +import com.android.settingslib.spa.testutils.onDialogText +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ListPreferenceTest { + @get:Rule + val composeTestRule = createComposeRule() + + @Test + fun title_displayed() { + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = emptyList<ListPreferenceOption>() + override val selectedId = mutableIntStateOf(0) + override val onIdSelected: (Int) -> Unit = {} + } + }) + } + + composeTestRule.onNodeWithText(TITLE).assertIsDisplayed() + } + + @Test + fun summary_showSelectedText() { + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = listOf(ListPreferenceOption(id = 1, text = "A")) + override val selectedId = mutableIntStateOf(1) + override val onIdSelected: (Int) -> Unit = {} + } + }) + } + + composeTestRule.onNodeWithText("A").assertIsDisplayed() + } + + @Test + fun click_optionsIsEmpty_notShowDialog() { + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = emptyList<ListPreferenceOption>() + override val selectedId = mutableIntStateOf(0) + override val onIdSelected: (Int) -> Unit = {} + } + }) + } + + composeTestRule.onNodeWithText(TITLE).performClick() + + composeTestRule.onDialogText(TITLE).assertDoesNotExist() + } + + @Test + fun click_notEnabled_notShowDialog() { + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val enabled = stateOf(false) + override val options = listOf(ListPreferenceOption(id = 1, text = "A")) + override val selectedId = mutableIntStateOf(1) + override val onIdSelected: (Int) -> Unit = {} + } + }) + } + + composeTestRule.onNodeWithText(TITLE).performClick() + + composeTestRule.onDialogText(TITLE).assertDoesNotExist() + } + + @Test + fun click_optionsNotEmpty_showDialog() { + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = listOf(ListPreferenceOption(id = 1, text = "A")) + override val selectedId = mutableIntStateOf(1) + override val onIdSelected: (Int) -> Unit = {} + } + }) + } + + composeTestRule.onNodeWithText(TITLE).performClick() + + composeTestRule.onDialogText(TITLE).assertIsDisplayed() + } + + @Test + fun select() { + val selectedId = mutableIntStateOf(1) + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val options = listOf( + ListPreferenceOption(id = 1, text = "A"), + ListPreferenceOption(id = 2, text = "B"), + ) + override val selectedId = selectedId + override val onIdSelected = { id: Int -> selectedId.intValue = id } + } + }) + } + + composeTestRule.onNodeWithText(TITLE).performClick() + composeTestRule.onDialogText("B").performClick() + + composeTestRule.onNodeWithText("B").assertIsDisplayed() + } + + @Test + fun select_dialogOpenThenDisable_itemAlsoDisabled() { + val selectedId = mutableIntStateOf(1) + val enabledState = mutableStateOf(true) + composeTestRule.setContent { + ListPreference(remember { + object : ListPreferenceModel { + override val title = TITLE + override val enabled = enabledState + override val options = listOf( + ListPreferenceOption(id = 1, text = "A"), + ListPreferenceOption(id = 2, text = "B"), + ) + override val selectedId = selectedId + override val onIdSelected = { id: Int -> selectedId.intValue = id } + } + }) + } + + composeTestRule.onNodeWithText(TITLE).performClick() + enabledState.value = false + + composeTestRule.onDialogText("B").assertIsDisplayed().assertIsNotEnabled() + } + + private companion object { + const val TITLE = "Title" + } +} |