Merge changes I05b50ace,Ic370f271

* changes:
  Add gallery page of item list operation
  Add arguments in displayName of page.
This commit is contained in:
Zekan Qian
2023-02-02 05:19:20 +00:00
committed by Android (Google) Code Review
10 changed files with 319 additions and 12 deletions

View File

@@ -24,6 +24,9 @@ import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.dialog.AlterDialogPageProvider
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.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.ChartPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
@@ -50,6 +53,8 @@ enum class SettingsPageProviderEnum(val displayName: String) {
HOME("home"),
PREFERENCE("preference"),
ARGUMENT("argument"),
ITEM_LIST("itemList"),
ITEM_OP_PAGE("itemOp"),
// Add your SPPs
}
@@ -76,6 +81,9 @@ class GallerySpaEnvironment(context: Context) : SpaEnvironment(context) {
LoadingBarPageProvider,
ChartPageProvider,
AlterDialogPageProvider,
ItemListPageProvider,
ItemOperatePageProvider,
OperateListPageProvider,
),
rootPages = listOf(
HomePageProvider.createSettingsPage(),

View File

@@ -18,6 +18,7 @@ package com.android.settingslib.spa.gallery.home
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsPageProvider
@@ -28,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.AlterDialogPageProvider
import com.android.settingslib.spa.gallery.itemList.OperateListPageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.ChartPageProvider
@@ -50,6 +52,7 @@ object HomePageProvider : SettingsPageProvider {
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
return listOf(
PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
OperateListPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
@@ -71,8 +74,10 @@ object HomePageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
HomeScaffold(title = getTitle(arguments)) {
for (entry in buildEntry(arguments)) {
val title = remember { getTitle(arguments) }
val entries = remember { buildEntry(arguments) }
HomeScaffold(title) {
for (entry in entries) {
if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
entry.UiLayout(ArgumentPageModel.buildArgument(intParam = 0))
} else {

View File

@@ -0,0 +1,99 @@
/*
* 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.itemList
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.common.EntrySearchData
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.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
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 OPERATOR_PARAM_NAME = "opParam"
object ItemListPageProvider : SettingsPageProvider {
override val name = SettingsPageProviderEnum.ITEM_LIST.name
override val displayName = SettingsPageProviderEnum.ITEM_LIST.displayName
override val parameter = listOf(
navArgument(OPERATOR_PARAM_NAME) { type = NavType.StringType },
)
override fun getTitle(arguments: Bundle?): String {
val operation = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments) ?: "NULL"
return "Operation: $operation"
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!ItemOperatePageProvider.isValidArgs(arguments)) return emptyList()
val operation = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments)!!
val owner = createSettingsPage(arguments)
return listOf(
ItemOperatePageProvider.buildInjectEntry(operation)!!.setLink(fromPage = owner).build(),
)
}
fun buildInjectEntry(opParam: String): SettingsEntryBuilder? {
val arguments = bundleOf(OPERATOR_PARAM_NAME to opParam)
if (!ItemOperatePageProvider.isValidArgs(arguments)) return null
return SettingsEntryBuilder.createInject(
owner = createSettingsPage(arguments),
displayName = "ItemList_$opParam",
).setUiLayoutFn {
Preference(
object : PreferenceModel {
override val title = opParam
override val onClick = navigator(
SettingsPageProviderEnum.ITEM_LIST.name + parameter.navLink(it)
)
}
)
}.setSearchDataFn {
EntrySearchData(title = "Operation: $opParam")
}
}
@Composable
override fun Page(arguments: Bundle?) {
val title = remember { getTitle(arguments) }
val entries = remember { buildEntry(arguments) }
val itemList = remember {
// Add logic to get item List during runtime.
listOf("itemFoo", "itemBar", "itemToy")
}
RegularScaffold(title) {
for (item in itemList) {
val rtArgs = ItemOperatePageProvider.genRuntimeArguments(item)
for (entry in entries) {
entry.UiLayout(rtArgs)
}
}
}
}
}

View File

@@ -0,0 +1,125 @@
/*
* 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.itemList
import android.os.Bundle
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
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.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
private const val OPERATOR_PARAM_NAME = "opParam"
private const val ITEM_NAME_PARAM_NAME = "rt_nameParam"
private val ALLOWED_OPERATOR_LIST = listOf("opDnD", "opPiP", "opInstall", "opConnect")
object ItemOperatePageProvider : SettingsPageProvider {
override val name = SettingsPageProviderEnum.ITEM_OP_PAGE.name
override val displayName = SettingsPageProviderEnum.ITEM_OP_PAGE.displayName
override val parameter = listOf(
navArgument(OPERATOR_PARAM_NAME) { type = NavType.StringType },
navArgument(ITEM_NAME_PARAM_NAME) { type = NavType.StringType },
)
override fun getTitle(arguments: Bundle?): String {
// Operation name is not a runtime parameter, which should always available
val operation = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments) ?: "opInValid"
// Item name is a runtime parameter, which could be missing
val itemName = parameter.getStringArg(ITEM_NAME_PARAM_NAME, arguments) ?: "[unset]"
return "$operation on $itemName"
}
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!isValidArgs(arguments)) return emptyList()
val owner = createSettingsPage(arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create("ItemName", owner)
.setUiLayoutFn {
// Item name is a runtime parameter, which needs to be read inside UiLayoutFn
val itemName = parameter.getStringArg(ITEM_NAME_PARAM_NAME, it) ?: "NULL"
Preference(
object : PreferenceModel {
override val title = "Item $itemName"
}
)
}.build()
)
// Operation name is not a runtime parameter, which can be read outside.
val opName = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments)!!
entryList.add(
SettingsEntryBuilder.create("ItemOp", owner)
.setUiLayoutFn {
val checked = rememberSaveable { mutableStateOf(false) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "Item operation: $opName"
override val checked = checked
override val onCheckedChange =
{ newChecked: Boolean -> checked.value = newChecked }
}
})
}.build(),
)
return entryList
}
fun buildInjectEntry(opParam: String): SettingsEntryBuilder? {
val arguments = bundleOf(OPERATOR_PARAM_NAME to opParam)
if (!isValidArgs(arguments)) return null
return SettingsEntryBuilder.createInject(
owner = createSettingsPage(arguments),
displayName = "ItemOp_$opParam",
).setUiLayoutFn {
// Item name is a runtime parameter, which needs to be read inside UiLayoutFn
val itemName = parameter.getStringArg(ITEM_NAME_PARAM_NAME, it) ?: "NULL"
Preference(
object : PreferenceModel {
override val title = "item: $itemName"
override val onClick = navigator(
SettingsPageProviderEnum.ITEM_OP_PAGE.name + parameter.navLink(it)
)
}
)
}
}
fun isValidArgs(arguments: Bundle?): Boolean {
val opParam = parameter.getStringArg(OPERATOR_PARAM_NAME, arguments)
return (opParam != null && ALLOWED_OPERATOR_LIST.contains(opParam))
}
fun genRuntimeArguments(itemName: String): Bundle {
return bundleOf(ITEM_NAME_PARAM_NAME to itemName)
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.itemList
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 = "Operate List Main"
object OperateListPageProvider : SettingsPageProvider {
override val name = "OpList"
private val owner = createSettingsPage()
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
return listOf(
ItemListPageProvider.buildInjectEntry("opPiP")!!.setLink(fromPage = owner).build(),
ItemListPageProvider.buildInjectEntry("opInstall")!!.setLink(fromPage = owner).build(),
ItemListPageProvider.buildInjectEntry("opDnD")!!.setLink(fromPage = owner).build(),
ItemListPageProvider.buildInjectEntry("opConnect")!!.setLink(fromPage = owner).build(),
)
}
override fun getTitle(arguments: Bundle?): String {
return TITLE
}
fun buildInjectEntry(): SettingsEntryBuilder {
return SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
Preference(object : PreferenceModel {
override val title = TITLE
override val onClick = navigator(name)
})
}
}
}

View File

@@ -18,6 +18,7 @@ package com.android.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
@@ -101,10 +102,13 @@ object ArgumentPageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
RegularScaffold(title = getTitle(arguments)) {
for (entry in buildEntry(arguments)) {
val title = remember { getTitle(arguments) }
val entries = remember { buildEntry(arguments) }
val rtArgNext = remember { ArgumentPageModel.buildNextArgument(arguments) }
RegularScaffold(title) {
for (entry in entries) {
if (entry.toPage != null) {
entry.UiLayout(ArgumentPageModel.buildNextArgument(arguments))
entry.UiLayout(rtArgNext)
} else {
entry.UiLayout()
}

View File

@@ -132,7 +132,7 @@ class ArgumentPageModel : PageModel() {
override val title = PAGE_TITLE
override val summary = stateOf(summaryArray.joinToString(", "))
override val onClick = navigator(
SettingsPageProviderEnum.ARGUMENT.displayName + parameter.navLink(arguments)
SettingsPageProviderEnum.ARGUMENT.name + parameter.navLink(arguments)
)
}
}

View File

@@ -22,6 +22,7 @@ import androidx.navigation.NamedNavArgument
import com.android.settingslib.spa.framework.util.isRuntimeParam
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.framework.util.normalize
import com.android.settingslib.spa.framework.util.normalizeArgList
private const val NULL_PAGE_NAME = "NULL"
@@ -114,9 +115,10 @@ data class SettingsPage(
fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): SettingsPage {
return SettingsPage.create(
name = name,
displayName = displayName,
displayName = displayName + parameter.normalizeArgList(arguments, eraseRuntimeValues = true)
.joinToString("") { arg -> "/$arg" },
parameter = parameter,
arguments = arguments
arguments = arguments,
)
}

View File

@@ -29,8 +29,16 @@ fun List<NamedNavArgument>.navRoute(): String {
}
fun List<NamedNavArgument>.navLink(arguments: Bundle? = null): String {
return normalizeArgList(arguments).joinToString("") { arg -> "/$arg" }
}
fun List<NamedNavArgument>.normalizeArgList(
arguments: Bundle? = null,
eraseRuntimeValues: Boolean = false
): List<String> {
val argsArray = mutableListOf<String>()
for (navArg in this) {
if (eraseRuntimeValues && navArg.isRuntimeParam()) continue
if (arguments == null || !arguments.containsKey(navArg.name)) {
argsArray.add(UNSET_PARAM_VALUE)
continue
@@ -44,7 +52,7 @@ fun List<NamedNavArgument>.navLink(arguments: Bundle? = null): String {
}
}
}
return argsArray.joinToString("") { arg -> "/$arg" }
return argsArray
}
fun List<NamedNavArgument>.normalize(
@@ -55,7 +63,7 @@ fun List<NamedNavArgument>.normalize(
val normArgs = Bundle()
for (navArg in this) {
// Erase value of runtime parameters.
if (navArg.isRuntimeParam() && eraseRuntimeValues) {
if (eraseRuntimeValues && navArg.isRuntimeParam()) {
normArgs.putString(navArg.name, null)
continue
}

View File

@@ -75,7 +75,7 @@ class SettingsPageTest {
)
)
assertThat(page.sppName).isEqualTo("SppWithParam")
assertThat(page.displayName).isEqualTo("SppWithParam")
assertThat(page.displayName).isEqualTo("SppWithParam/myStr/10")
assertThat(page.buildRoute()).isEqualTo("SppWithParam/myStr/10")
assertThat(page.isCreateBy("SppWithParam")).isTrue()
assertThat(page.hasRuntimeParam()).isFalse()
@@ -100,7 +100,7 @@ class SettingsPageTest {
)
)
assertThat(page.sppName).isEqualTo("SppWithRtParam")
assertThat(page.displayName).isEqualTo("SppWithRtParam")
assertThat(page.displayName).isEqualTo("SppWithRtParam/myStr/10")
assertThat(page.buildRoute()).isEqualTo("SppWithRtParam/myStr/10/rtStr")
assertThat(page.isCreateBy("SppWithRtParam")).isTrue()
assertThat(page.hasRuntimeParam()).isTrue()