summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Charlotte Lu <charlottelu@google.com> 2023-09-08 01:54:52 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-09-08 01:54:52 +0000
commitd3965882d85bc4f57c9e01c5ebda70498218cfc5 (patch)
tree62c24c0b042178c9d71f09f40edcafd9d934cab9
parent3e5da2007c3b72a42a8027df05a1a207ec7d0b23 (diff)
parentfebe9889489fc16c0ce8ffa44a6026465059ea24 (diff)
Merge "Create new editor in spa." into main
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt8
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt58
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt77
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt73
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsOutlinedTextFieldPageProvider.kt74
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt (renamed from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt)2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt107
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt131
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt66
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt94
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt111
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextFieldsTest.kt98
12 files changed, 899 insertions, 0 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 11a89566a2a8..4a252a91b12f 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
@@ -23,10 +23,14 @@ import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuBoxPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsExposedDropdownMenuCheckBoxProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.itemList.ItemListPageProvider
import com.android.settingslib.spa.gallery.itemList.ItemOperatePageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
+import com.android.settingslib.spa.gallery.editor.SettingsOutlinedTextFieldPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.ChartPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
@@ -84,6 +88,10 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
ItemListPageProvider,
ItemOperatePageProvider,
OperateListPageProvider,
+ EditorMainPageProvider,
+ SettingsOutlinedTextFieldPageProvider,
+ SettingsExposedDropdownMenuBoxPageProvider,
+ SettingsExposedDropdownMenuCheckBoxProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
new file mode 100644
index 000000000000..b74af213a058
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/EditorMainPageProvider.kt
@@ -0,0 +1,58 @@
+/*
+ * 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.editor
+
+import android.os.Bundle
+import com.android.settingslib.spa.framework.common.SettingsEntry
+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.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+
+private const val TITLE = "Category: Editor"
+
+object EditorMainPageProvider : SettingsPageProvider {
+ override val name = "EditorMain"
+ private val owner = createSettingsPage()
+
+ override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
+ return listOf(
+ SettingsOutlinedTextFieldPageProvider.buildInjectEntry().setLink(fromPage = owner)
+ .build(),
+ SettingsExposedDropdownMenuBoxPageProvider.buildInjectEntry().setLink(fromPage = owner)
+ .build(),
+ SettingsExposedDropdownMenuCheckBoxProvider.buildInjectEntry().setLink(fromPage = owner)
+ .build(),
+ )
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = owner)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
new file mode 100644
index 000000000000..6d22e6abf7e3
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuBoxPageProvider.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+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.editor.SettingsExposedDropdownMenuBox
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsExposedDropdownMenuBox"
+
+object SettingsExposedDropdownMenuBoxPageProvider : SettingsPageProvider {
+ override val name = "SettingsExposedDropdownMenuBox"
+ private const val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ var selectedItem by remember { mutableStateOf("item1") }
+ val options = listOf("item1", "item2", "item3")
+ RegularScaffold(title = TITLE) {
+ SettingsExposedDropdownMenuBox(
+ label = exposedDropdownMenuBoxLabel,
+ options = options,
+ selectedOptionText = selectedItem,
+ enabled = true,
+ onselectedOptionTextChange = { selectedItem = it })
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsExposedDropdownMenuBoxPagePreview() {
+ SettingsTheme {
+ SettingsExposedDropdownMenuBoxPageProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
new file mode 100644
index 000000000000..292e002779f5
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsExposedDropdownMenuCheckBoxProvider.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.tooling.preview.Preview
+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.editor.SettingsExposedDropdownMenuCheckBox
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsExposedDropdownMenuCheckBox"
+
+object SettingsExposedDropdownMenuCheckBoxProvider : SettingsPageProvider {
+ override val name = "SettingsExposedDropdownMenuCheckBox"
+ private const val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
+ private val options = listOf("item1", "item2", "item3")
+ private val selectedOptionsState1 = mutableStateListOf("item1", "item2")
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ RegularScaffold(title = TITLE) {
+ SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+ options = options,
+ selectedOptionsState = remember { selectedOptionsState1 },
+ enabled = true,
+ onselectedOptionStateChange = {})
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage()).setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsExposedDropdownMenuCheckBoxPagePreview() {
+ SettingsTheme {
+ SettingsExposedDropdownMenuCheckBoxProvider.Page(null)
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsOutlinedTextFieldPageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsOutlinedTextFieldPageProvider.kt
new file mode 100644
index 000000000000..54fa5a95d812
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/editor/SettingsOutlinedTextFieldPageProvider.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.editor
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+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.editor.SettingsOutlinedTextField
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample SettingsOutlinedTextField"
+
+object SettingsOutlinedTextFieldPageProvider : SettingsPageProvider {
+ override val name = "SettingsOutlinedTextField"
+
+ override fun getTitle(arguments: Bundle?): String {
+ return TITLE
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ var value by remember { mutableStateOf("Enabled Value") }
+ RegularScaffold(title = TITLE) {
+ SettingsOutlinedTextField(
+ value = value,
+ label = "OutlinedTextField Enabled",
+ enabled = true,
+ onTextChange = {value = it})
+ }
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = createSettingsPage())
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SettingsOutlinedTextFieldPagePreview() {
+ SettingsTheme {
+ SettingsOutlinedTextFieldPageProvider.Page(null)
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
index c60ebfdb1bd3..bb311a52052a 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePageProvider.kt
@@ -29,6 +29,7 @@ import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.dialog.AlertDialogPageProvider
+import com.android.settingslib.spa.gallery.editor.EditorMainPageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -65,6 +66,7 @@ object HomePageProvider : SettingsPageProvider {
LoadingBarPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ChartPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
AlertDialogPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ EditorMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
new file mode 100644
index 000000000000..d7bbf0871f71
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBox.kt
@@ -0,0 +1,107 @@
+/*
+ * 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.editor
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+fun SettingsExposedDropdownMenuBox(
+ label: String,
+ options: List<String>,
+ selectedOptionText: String,
+ enabled: Boolean,
+ onselectedOptionTextChange: (String) -> Unit,
+) {
+ var expanded by remember { mutableStateOf(false) }
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = { expanded = it },
+ modifier = Modifier
+ .padding(SettingsDimension.itemPadding),
+ ) {
+ OutlinedTextField(
+ // The `menuAnchor` modifier must be passed to the text field for correctness.
+ modifier = Modifier
+ .menuAnchor()
+ .fillMaxWidth(),
+ value = selectedOptionText,
+ onValueChange = onselectedOptionTextChange,
+ label = { Text(text = label) },
+ trailingIcon = {
+ ExposedDropdownMenuDefaults.TrailingIcon(
+ expanded = expanded
+ )
+ },
+ singleLine = true,
+ readOnly = true,
+ enabled = enabled
+ )
+ if (options.isNotEmpty()) {
+ ExposedDropdownMenu(
+ expanded = expanded,
+ modifier = Modifier
+ .fillMaxWidth(),
+ onDismissRequest = { expanded = false },
+ ) {
+ options.forEach { option ->
+ DropdownMenuItem(
+ text = { Text(option) },
+ onClick = {
+ onselectedOptionTextChange(option)
+ expanded = false
+ },
+ contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding,
+ )
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun SettingsExposedDropdownMenuBoxsPreview() {
+ val item1 = "item1"
+ val item2 = "item2"
+ val item3 = "item3"
+ val options = listOf(item1, item2, item3)
+ SettingsTheme {
+ SettingsExposedDropdownMenuBox(
+ label = "ExposedDropdownMenuBoxLabel",
+ options = options,
+ selectedOptionText = item1,
+ enabled = true,
+ onselectedOptionTextChange = {})
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
new file mode 100644
index 000000000000..6b3ae772f224
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBox.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.editor
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExposedDropdownMenuBox
+import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.SnapshotStateList
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onSizeChanged
+import androidx.compose.ui.platform.LocalDensity
+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.SettingsTheme
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsExposedDropdownMenuCheckBox(
+ label: String,
+ options: List<String>,
+ selectedOptionsState: SnapshotStateList<String>,
+ enabled: Boolean,
+ onselectedOptionStateChange: (String) -> Unit,
+) {
+ var dropDownWidth by remember { mutableStateOf(0) }
+ var expanded by remember { mutableStateOf(false) }
+ ExposedDropdownMenuBox(
+ expanded = expanded,
+ onExpandedChange = { expanded = it },
+ modifier = Modifier.width(350.dp).padding(
+ horizontal = SettingsDimension.itemPaddingEnd,
+ vertical = SettingsDimension.itemPaddingVertical
+ ).onSizeChanged {
+ dropDownWidth = it.width
+ },
+ ) {
+ OutlinedTextField(
+ // The `menuAnchor` modifier must be passed to the text field for correctness.
+ modifier = Modifier.menuAnchor().fillMaxWidth(),
+ value = selectedOptionsState.joinToString(", "),
+ onValueChange = onselectedOptionStateChange,
+ label = { Text(text = label) },
+ trailingIcon = {
+ ExposedDropdownMenuDefaults.TrailingIcon(
+ expanded = expanded
+ )
+ },
+ readOnly = true,
+ enabled = enabled
+ )
+ if (options.isNotEmpty()) {
+ ExposedDropdownMenu(
+ expanded = expanded,
+ modifier = Modifier.fillMaxWidth()
+ .width(with(LocalDensity.current) { dropDownWidth.toDp() }),
+ onDismissRequest = { expanded = false },
+ ) {
+ options.forEach { option ->
+ TextButton(modifier = Modifier.fillMaxHeight().fillMaxWidth(), onClick = {
+ if (selectedOptionsState.contains(option)) {
+ selectedOptionsState.remove(
+ option
+ )
+ } else {
+ selectedOptionsState.add(
+ option
+ )
+ }
+ }) {
+ Row(
+ modifier = Modifier.fillMaxHeight().fillMaxWidth(),
+ horizontalArrangement = Arrangement.Start,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Checkbox(checked = selectedOptionsState.contains(
+ option
+ ), onCheckedChange = {})
+ Text(text = option)
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+@Preview
+@Composable
+private fun ActionButtonsPreview() {
+ val options = listOf("item1", "item2", "item3")
+ val selectedOptionsState = remember { mutableStateListOf("item1", "item2") }
+ SettingsTheme {
+ SettingsExposedDropdownMenuCheckBox(label = "label",
+ options = options,
+ selectedOptionsState = selectedOptionsState,
+ enabled = true,
+ onselectedOptionStateChange = {})
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
new file mode 100644
index 000000000000..3819a1075adb
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextField.kt
@@ -0,0 +1,66 @@
+/*
+ * 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.editor
+
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+@Composable
+fun SettingsOutlinedTextField(
+ value: String,
+ label: String,
+ singleLine: Boolean = true,
+ enabled: Boolean = true,
+ onTextChange: (String) -> Unit,
+) {
+ OutlinedTextField(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(SettingsDimension.itemPadding),
+ value = value,
+ onValueChange = onTextChange,
+ label = {
+ Text(text = label)
+ },
+ singleLine = singleLine,
+ enabled = enabled,
+ )
+}
+
+@Preview
+@Composable
+private fun SettingsOutlinedTextFieldPreview() {
+ var value by remember { mutableStateOf("Enabled Value") }
+ SettingsTheme {
+ SettingsOutlinedTextField(
+ value = value,
+ label = "OutlinedTextField Enabled",
+ enabled = true,
+ onTextChange = {value = it})
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
new file mode 100644
index 000000000000..09f59457b32d
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuBoxTest.kt
@@ -0,0 +1,94 @@
+/*
+ * 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.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+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 org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsExposedDropdownMenuBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+ private val options = listOf("item1", "item2", "item3")
+ private val item2 = "item2"
+ private val exposedDropdownMenuBoxLabel = "ExposedDropdownMenuBoxLabel"
+
+ @Test
+ fun exposedDropdownMenuBoxs_displayed() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableStateOf("item1") }
+ SettingsExposedDropdownMenuBox(
+ label = exposedDropdownMenuBoxLabel,
+ options = options,
+ selectedOptionText = selectedItem,
+ enabled = true,
+ onselectedOptionTextChange = {selectedItem = it})
+ }
+ composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun exposedDropdownMenuBoxs_expanded() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableStateOf("item1") }
+ SettingsExposedDropdownMenuBox(
+ label = exposedDropdownMenuBoxLabel,
+ options = options,
+ selectedOptionText = selectedItem,
+ enabled = true,
+ onselectedOptionTextChange = {selectedItem = it})
+ }
+ composeTestRule.onNodeWithText(item2, substring = true)
+ .assertDoesNotExist()
+ composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
+ .performClick()
+ composeTestRule.onNodeWithText(item2, substring = true)
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun exposedDropdownMenuBoxs_valueChanged() {
+ composeTestRule.setContent {
+ var selectedItem by remember { mutableStateOf("item1") }
+ SettingsExposedDropdownMenuBox(
+ label = exposedDropdownMenuBoxLabel,
+ options = options,
+ selectedOptionText = selectedItem,
+ enabled = true,
+ onselectedOptionTextChange = {selectedItem = it})
+ }
+ composeTestRule.onNodeWithText(item2, substring = true)
+ .assertDoesNotExist()
+ composeTestRule.onNodeWithText(exposedDropdownMenuBoxLabel, substring = true)
+ .performClick()
+ composeTestRule.onNodeWithText(item2, substring = true)
+ .performClick()
+ composeTestRule.onNodeWithText(item2, substring = true)
+ .assertIsDisplayed()
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
new file mode 100644
index 000000000000..58bc72213a1b
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsExposedDropdownMenuCheckBoxTest.kt
@@ -0,0 +1,111 @@
+/*
+ * 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.editor
+
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.SemanticsNodeInteraction
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.hasText
+import androidx.compose.ui.test.isFocused
+import androidx.compose.ui.test.junit4.ComposeContentTestRule
+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 org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsExposedDropdownMenuCheckBoxTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+ private val item1 = "item1"
+ private val item2 = "item2"
+ private val item3 = "item3"
+ private val options = listOf(item1, item2, item3)
+ private val selectedOptionsState1 = mutableStateListOf(item1, item2)
+ private val exposedDropdownMenuCheckBoxLabel = "ExposedDropdownMenuCheckBoxLabel"
+
+ @Test
+ fun exposedDropdownMenuCheckBox_displayed() {
+ composeTestRule.setContent {
+ SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+ options = options,
+ selectedOptionsState = remember { selectedOptionsState1 },
+ enabled = true,
+ onselectedOptionStateChange = {})
+ }
+ composeTestRule.onNodeWithText(
+ exposedDropdownMenuCheckBoxLabel, substring = true
+ ).assertIsDisplayed()
+ }
+
+ @Test
+ fun exposedDropdownMenuCheckBox_expanded() {
+ composeTestRule.setContent {
+ SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+ options = options,
+ selectedOptionsState = remember { selectedOptionsState1 },
+ enabled = true,
+ onselectedOptionStateChange = {})
+ }
+ composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
+ composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
+ .performClick()
+ composeTestRule.onNodeWithText(item3, substring = true).assertIsDisplayed()
+ }
+
+ @Test
+ fun exposedDropdownMenuCheckBox_valueAdded() {
+ composeTestRule.setContent {
+ SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+ options = options,
+ selectedOptionsState = remember { selectedOptionsState1 },
+ enabled = true,
+ onselectedOptionStateChange = {})
+ }
+ composeTestRule.onNodeWithText(item3, substring = true).assertDoesNotExist()
+ composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
+ .performClick()
+ composeTestRule.onNodeWithText(item3, substring = true).performClick()
+ composeTestRule.onFocusedText(item3).assertIsDisplayed()
+ }
+
+ @Test
+ fun exposedDropdownMenuCheckBox_valueDeleted() {
+ composeTestRule.setContent {
+ SettingsExposedDropdownMenuCheckBox(label = exposedDropdownMenuCheckBoxLabel,
+ options = options,
+ selectedOptionsState = remember { selectedOptionsState1 },
+ enabled = true,
+ onselectedOptionStateChange = {})
+ }
+ composeTestRule.onNodeWithText(item2, substring = true).assertIsDisplayed()
+ composeTestRule.onNodeWithText(exposedDropdownMenuCheckBoxLabel, substring = true)
+ .performClick()
+ composeTestRule.onNotFocusedText(item2).performClick()
+ composeTestRule.onFocusedText(item2).assertDoesNotExist()
+ }
+}
+
+fun ComposeContentTestRule.onFocusedText(text: String): SemanticsNodeInteraction =
+ onNode(isFocused() and hasText(text, substring = true))
+
+fun ComposeContentTestRule.onNotFocusedText(text: String): SemanticsNodeInteraction =
+ onNode(!isFocused() and hasText(text, substring = true)) \ No newline at end of file
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextFieldsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextFieldsTest.kt
new file mode 100644
index 000000000000..5eff1a29e12c
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/editor/SettingsOutlinedTextFieldsTest.kt
@@ -0,0 +1,98 @@
+/*
+ * 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.editor
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
+import androidx.compose.ui.test.assertTextContains
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performTextReplacement
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsOutlinedTextFieldsTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+ private val outlinedTextFieldLabel = "OutlinedTextField Enabled"
+ private val enabledValue = "Enabled Value"
+ private val disabledValue = "Disabled Value"
+ private val valueChanged = "Value Changed"
+
+ @Test
+ fun outlinedTextField_displayed() {
+ composeTestRule.setContent {
+ SettingsOutlinedTextField(
+ value = enabledValue,
+ label = outlinedTextFieldLabel,
+ enabled = true,
+ onTextChange = {})
+ }
+ composeTestRule.onNodeWithText(outlinedTextFieldLabel, substring = true)
+ .assertIsDisplayed()
+ }
+
+ @Test
+ fun outlinedTextFields_enabled() {
+ composeTestRule.setContent {
+ SettingsOutlinedTextField(
+ value = enabledValue,
+ label = outlinedTextFieldLabel,
+ enabled = true,
+ onTextChange = {})
+ }
+ composeTestRule.onNodeWithText(outlinedTextFieldLabel, substring = true)
+ .assertIsEnabled()
+ }
+
+ @Test
+ fun outlinedTextFields_disabled() {
+ composeTestRule.setContent {
+ SettingsOutlinedTextField(
+ value = disabledValue,
+ label = outlinedTextFieldLabel,
+ enabled = false,
+ onTextChange = {})
+ }
+ composeTestRule.onNodeWithText(outlinedTextFieldLabel, substring = true)
+ .assertIsNotEnabled()
+ }
+
+ @Test
+ fun outlinedTextFields_inputValue() {
+ composeTestRule.setContent {
+ var value by remember { mutableStateOf(enabledValue) }
+ SettingsOutlinedTextField(
+ value = value,
+ label = outlinedTextFieldLabel,
+ enabled = true,
+ onTextChange = { value = it })
+ }
+ composeTestRule.onNodeWithText(outlinedTextFieldLabel, substring = true)
+ .performTextReplacement(valueChanged)
+ composeTestRule.onNodeWithText(outlinedTextFieldLabel, substring = true)
+ .assertTextContains(valueChanged)
+ }
+}