summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Zekan Qian <pierreqian@google.com> 2022-09-21 08:25:21 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-09-21 08:25:21 +0000
commit3bcba3e633ba4daab22c652ca7da79f4dd4adf94 (patch)
treeecb45adb97606d12b786c97e4b1d9a53b79f9459
parent9f07f3414e53abc811b876cba38299c5e89318cf (diff)
parentf839df8ca297b50082f966a9404fe413e49cd1c2 (diff)
Merge "Support StaticData / SearchData in SettingsEntry."
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt6
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt30
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt25
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt31
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt31
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt180
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt115
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt18
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt59
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt132
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMarco.kt30
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt33
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt229
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt33
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt118
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt3
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt38
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt5
20 files changed, 762 insertions, 364 deletions
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 317346d26f89..cfcd75f86d1f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,4 +18,8 @@ package com.android.settingslib.spa.gallery
import com.android.settingslib.spa.framework.DebugActivity
-class GalleryDebugActivity : DebugActivity(SpaEnvironment.EntryRepository, MainActivity::class.java)
+class GalleryDebugActivity : DebugActivity(
+ SpaEnvironment.EntryRepository,
+ MainActivity::class.java,
+ "com.android.spa.gallery.provider",
+)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
index 783fef7e91cf..773e5f3929fb 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
@@ -16,6 +16,8 @@
package com.android.settingslib.spa.gallery
+import android.os.Bundle
+import androidx.navigation.NamedNavArgument
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
@@ -34,6 +36,32 @@ import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferenceP
import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
+/**
+ * Enum to define all SPP name here.
+ * Since the SPP name would be used in log, DO NOT change it once it is set. One can still change
+ * the display name for better readability if necessary.
+ */
+enum class SettingsPageProviderEnum(val displayName: String) {
+ HOME("home"),
+ PREFERENCE("preference"),
+ ARGUMENT("argument"),
+
+ // Add your SPPs
+}
+
+fun createSettingsPage(
+ SppName: SettingsPageProviderEnum,
+ parameter: List<NamedNavArgument> = emptyList(),
+ arguments: Bundle? = null
+): SettingsPage {
+ return SettingsPage(
+ name = SppName.name,
+ displayName = SppName.displayName,
+ parameter = parameter,
+ arguments = arguments,
+ )
+}
+
object SpaEnvironment {
val PageProviderRepository: SettingsPageProviderRepository by
lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
@@ -55,7 +83,7 @@ object SpaEnvironment {
ActionButtonPageProvider,
),
rootPages = listOf(
- SettingsPage.create(HomePageProvider.name)
+ createSettingsPage(SettingsPageProviderEnum.HOME)
)
)
}
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/HomePage.kt
index a16ee6c85409..c8a4650d3f01 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/HomePage.kt
@@ -21,10 +21,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
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.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
@@ -38,20 +38,19 @@ import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
object HomePageProvider : SettingsPageProvider {
- override val name = "Home"
+ override val name = SettingsPageProviderEnum.HOME.name
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
- val owner = SettingsPage.create(name)
return listOf(
- PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
- SliderPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- SpinnerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
- ActionButtonPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ PreferenceMainPageProvider.buildInjectEntry().build(),
+ ArgumentPageProvider.buildInjectEntry("foo")!!.build(),
+ SliderPageProvider.buildInjectEntry().build(),
+ SpinnerPageProvider.buildInjectEntry().build(),
+ SettingsPagerPageProvider.buildInjectEntry().build(),
+ FooterPageProvider.buildInjectEntry().build(),
+ IllustrationPageProvider.buildInjectEntry().build(),
+ CategoryPageProvider.buildInjectEntry().build(),
+ ActionButtonPageProvider.buildInjectEntry().build(),
)
}
@@ -59,7 +58,7 @@ object HomePageProvider : SettingsPageProvider {
override fun Page(arguments: Bundle?) {
HomeScaffold(title = stringResource(R.string.app_name)) {
for (entry in buildEntry(arguments)) {
- if (entry.name.startsWith(ArgumentPageModel.name)) {
+ if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
entry.UiLayout(ArgumentPageModel.buildArgument(intParam = 0))
} else {
entry.UiLayout()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
index e32de7a67d9d..cc0a79adaa88 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
@@ -24,23 +24,38 @@ import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
+import com.android.settingslib.spa.gallery.createSettingsPage
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
object ArgumentPageProvider : SettingsPageProvider {
- override val name = ArgumentPageModel.name
+ // Defines all entry name in this page.
+ // Note that entry name would be used in log. DO NOT change it once it is set.
+ // One can still change the display name for better readability if necessary.
+ private enum class EntryEnum(val displayName: String) {
+ STRING_PARAM("string_param"),
+ INT_PARAM("int_param"),
+ }
+
+ private fun createEntry(owner: SettingsPage, entry: EntryEnum): SettingsEntryBuilder {
+ return SettingsEntryBuilder.create(owner, entry.name, entry.displayName)
+ }
+
+ override val name = SettingsPageProviderEnum.ARGUMENT.name
override val parameter = ArgumentPageModel.parameter
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!ArgumentPageModel.isValidArgument(arguments)) return emptyList()
- val owner = SettingsPage.create(name, parameter, arguments)
+ val owner = createSettingsPage(SettingsPageProviderEnum.ARGUMENT, parameter, arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
- SettingsEntryBuilder.create("string_param", owner)
+ createEntry(owner, EntryEnum.STRING_PARAM)
// Set attributes
.setIsAllowSearch(true)
+ .setSearchDataFn { ArgumentPageModel.genStringParamSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genStringParamPreferenceModel())
@@ -48,9 +63,10 @@ object ArgumentPageProvider : SettingsPageProvider {
)
entryList.add(
- SettingsEntryBuilder.create("int_param", owner)
+ createEntry(owner, EntryEnum.INT_PARAM)
// Set attributes
.setIsAllowSearch(true)
+ .setSearchDataFn { ArgumentPageModel.genIntParamSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genIntParamPreferenceModel())
@@ -68,11 +84,12 @@ object ArgumentPageProvider : SettingsPageProvider {
if (!ArgumentPageModel.isValidArgument(arguments)) return null
return SettingsEntryBuilder.createInject(
- entryName = "${name}_$stringParam",
- owner = SettingsPage.create(name, parameter, arguments)
+ owner = createSettingsPage(SettingsPageProviderEnum.ARGUMENT, parameter, arguments),
+ displayName = "${name}_$stringParam",
)
// Set attributes
.setIsAllowSearch(false)
+ .setSearchDataFn { ArgumentPageModel.genInjectSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genInjectPreferenceModel())
@@ -83,7 +100,7 @@ object ArgumentPageProvider : SettingsPageProvider {
override fun Page(arguments: Bundle?) {
RegularScaffold(title = ArgumentPageModel.create(arguments).genPageTitle()) {
for (entry in buildEntry(arguments)) {
- if (entry.name.startsWith(name)) {
+ if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
entry.UiLayout(ArgumentPageModel.buildNextArgument(arguments))
} else {
entry.UiLayout()
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
index 6e86fd785a0e..e75d09b7d17c 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
@@ -23,22 +23,28 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType
import androidx.navigation.navArgument
import com.android.settingslib.spa.framework.BrowseActivity
+import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.PageModel
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.getIntArg
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.PreferenceModel
-private const val TITLE = "Sample page with arguments"
+// Defines all the resources for this page.
+// In real Settings App, resources data is defined in xml, rather than SPP.
+private const val PAGE_TITLE = "Sample page with arguments"
+private const val STRING_PARAM_TITLE = "String param value"
+private const val INT_PARAM_TITLE = "Int param value"
private const val STRING_PARAM_NAME = "stringParam"
private const val INT_PARAM_NAME = "intParam"
+private val ARGUMENT_PAGE_KEYWORDS = listOf("argument keyword1", "argument keyword2")
class ArgumentPageModel : PageModel() {
companion object {
- const val name = "Argument"
val parameter = listOf(
navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
navArgument(INT_PARAM_NAME) { type = NavType.IntType },
@@ -62,6 +68,18 @@ class ArgumentPageModel : PageModel() {
return (stringParam != null && listOf("foo", "bar").contains(stringParam))
}
+ fun genStringParamSearchData(): EntrySearchData {
+ return EntrySearchData(title = STRING_PARAM_TITLE)
+ }
+
+ fun genIntParamSearchData(): EntrySearchData {
+ return EntrySearchData(title = INT_PARAM_TITLE)
+ }
+
+ fun genInjectSearchData(): EntrySearchData {
+ return EntrySearchData(title = PAGE_TITLE, keyword = ARGUMENT_PAGE_KEYWORDS)
+ }
+
@Composable
fun create(arguments: Bundle?): ArgumentPageModel {
val pageModel: ArgumentPageModel = viewModel(key = arguments.toString())
@@ -70,7 +88,7 @@ class ArgumentPageModel : PageModel() {
}
}
- private val title = TITLE
+ private val title = PAGE_TITLE
private var arguments: Bundle? = null
private var stringParam: String? = null
private var intParam: Int? = null
@@ -92,7 +110,7 @@ class ArgumentPageModel : PageModel() {
@Composable
fun genStringParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
- override val title = "String param value"
+ override val title = STRING_PARAM_TITLE
override val summary = stateOf(stringParam!!)
}
}
@@ -100,7 +118,7 @@ class ArgumentPageModel : PageModel() {
@Composable
fun genIntParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
- override val title = "Int param value"
+ override val title = INT_PARAM_TITLE
override val summary = stateOf(intParam!!.toString())
}
}
@@ -114,7 +132,8 @@ class ArgumentPageModel : PageModel() {
return object : PreferenceModel {
override val title = genPageTitle()
override val summary = stateOf(summaryArray.joinToString(", "))
- override val onClick = navigator(name + parameter.navLink(arguments))
+ override val onClick = navigator(
+ SettingsPageProviderEnum.ARGUMENT.displayName + parameter.navLink(arguments))
}
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
index cbd028d17cba..9fc736c911af 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
@@ -18,132 +18,166 @@ package com.android.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Autorenew
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.material.icons.outlined.TouchApp
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
+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.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
+import com.android.settingslib.spa.gallery.createSettingsPage
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.AUTO_UPDATE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_SUMMARY
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.MANUAL_UPDATE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.PAGE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_KEYWORDS
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_SUMMARY
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.logMsg
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.SimplePreferenceMarco
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.SettingsIcon
-import kotlinx.coroutines.delay
-
-private const val TITLE = "Sample Preference"
object PreferencePageProvider : SettingsPageProvider {
- override val name = "Preference"
+ // Defines all entry name in this page.
+ // Note that entry name would be used in log. DO NOT change it once it is set.
+ // One can still change the display name for better readability if necessary.
+ enum class EntryEnum(val displayName: String) {
+ SIMPLE_PREFERENCE("preference"),
+ SUMMARY_PREFERENCE("preference_with_summary"),
+ DISABLED_PREFERENCE("preference_disable"),
+ ASYNC_SUMMARY_PREFERENCE("preference_with_async_summary"),
+ MANUAL_UPDATE_PREFERENCE("preference_actionable"),
+ AUTO_UPDATE_PREFERENCE("preference_auto_update"),
+ }
+
+ override val name = SettingsPageProviderEnum.PREFERENCE.name
+ private val owner = createSettingsPage(SettingsPageProviderEnum.PREFERENCE)
+
+ private fun createEntry(entry: EntryEnum): SettingsEntryBuilder {
+ return SettingsEntryBuilder.create(owner, entry.name, entry.displayName)
+ }
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
- val owner = SettingsPage.create(name)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
- SettingsEntryBuilder.create("Preference", owner)
+ createEntry(EntryEnum.SIMPLE_PREFERENCE)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Preference"
- })
- }.build()
+ .setMarco {
+ logMsg("create marco for ${EntryEnum.SIMPLE_PREFERENCE}")
+ SimplePreferenceMarco(title = SIMPLE_PREFERENCE_TITLE)
+ }
+ .build()
)
entryList.add(
- SettingsEntryBuilder.create("Preference with summary", owner)
+ createEntry(EntryEnum.SUMMARY_PREFERENCE)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Preference"
- override val summary = "With summary".toState()
- })
- }.build()
+ .setMarco {
+ logMsg("create marco for ${EntryEnum.SUMMARY_PREFERENCE}")
+ SimplePreferenceMarco(
+ title = SIMPLE_PREFERENCE_TITLE,
+ summary = SIMPLE_PREFERENCE_SUMMARY,
+ searchKeywords = SIMPLE_PREFERENCE_KEYWORDS,
+ )
+ }
+ .build()
)
entryList.add(
- SettingsEntryBuilder.create("Preference with async summary", owner)
+ createEntry(EntryEnum.DISABLED_PREFERENCE)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Preference"
- override val summary = produceState(initialValue = " ") {
- delay(1000L)
- value = "Async summary"
- }
- })
- }.build()
+ .setMarco {
+ logMsg("create marco for ${EntryEnum.DISABLED_PREFERENCE}")
+ SimplePreferenceMarco(
+ title = DISABLE_PREFERENCE_TITLE,
+ summary = DISABLE_PREFERENCE_SUMMARY,
+ disabled = true,
+ icon = Icons.Outlined.DisabledByDefault,
+ )
+ }
+ .build()
)
entryList.add(
- SettingsEntryBuilder.create("Click me", owner)
+ createEntry(EntryEnum.ASYNC_SUMMARY_PREFERENCE)
.setIsAllowSearch(true)
+ .setSearchDataFn {
+ EntrySearchData(title = ASYNC_PREFERENCE_TITLE)
+ }
.setUiLayoutFn {
- var count by rememberSaveable { mutableStateOf(0) }
- Preference(object : PreferenceModel {
- override val title = "Click me"
- override val summary = derivedStateOf { count.toString() }
- override val onClick: (() -> Unit) = { count++ }
- override val icon = @Composable {
- SettingsIcon(imageVector = Icons.Outlined.TouchApp)
+ val model = PreferencePageModel.create()
+ val asyncSummary = remember { model.getAsyncSummary() }
+ Preference(
+ object : PreferenceModel {
+ override val title = ASYNC_PREFERENCE_TITLE
+ override val summary = asyncSummary
}
- })
+ )
}.build()
)
entryList.add(
- SettingsEntryBuilder.create("Ticker", owner)
+ createEntry(EntryEnum.MANUAL_UPDATE_PREFERENCE)
.setIsAllowSearch(true)
.setUiLayoutFn {
- var ticks by rememberSaveable { mutableStateOf(0) }
- LaunchedEffect(ticks) {
- delay(1000L)
- ticks++
- }
- Preference(object : PreferenceModel {
- override val title = "Ticker"
- override val summary = derivedStateOf { ticks.toString() }
- })
+ val model = PreferencePageModel.create()
+ val manualUpdaterSummary = remember { model.getManualUpdaterSummary() }
+ Preference(
+ object : PreferenceModel {
+ override val title = MANUAL_UPDATE_PREFERENCE_TITLE
+ override val summary = manualUpdaterSummary
+ override val onClick = { model.manualUpdaterOnClick() }
+ override val icon = @Composable {
+ SettingsIcon(imageVector = Icons.Outlined.TouchApp)
+ }
+ }
+ )
}.build()
)
entryList.add(
- SettingsEntryBuilder.create("Disabled", owner)
+ createEntry(EntryEnum.AUTO_UPDATE_PREFERENCE)
.setIsAllowSearch(true)
.setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Disabled"
- override val summary = "Disabled".toState()
- override val enabled = false.toState()
- override val icon = @Composable {
- SettingsIcon(imageVector = Icons.Outlined.DisabledByDefault)
+ val model = PreferencePageModel.create()
+ val autoUpdaterSummary = remember { model.getAutoUpdaterSummary() }
+ Preference(
+ object : PreferenceModel {
+ override val title = AUTO_UPDATE_PREFERENCE_TITLE
+ override val summary = autoUpdaterSummary.observeAsState(" ")
+ override val icon = @Composable {
+ SettingsIcon(imageVector = Icons.Outlined.Autorenew)
+ }
}
- })
- }.build()
+ )
+ }
+ .build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name))
+ return SettingsEntryBuilder.createInject(owner = owner)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
+ .setMarco {
+ logMsg("create marco for INJECT entry")
+ SimplePreferenceMarco(
+ title = PAGE_TITLE,
+ clickRoute = SettingsPageProviderEnum.PREFERENCE.name
+ )
}
}
@Composable
override fun Page(arguments: Bundle?) {
- RegularScaffold(title = TITLE) {
+ RegularScaffold(title = PAGE_TITLE) {
for (entry in buildEntry(arguments)) {
entry.UiLayout()
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
new file mode 100644
index 000000000000..1188e1e006b2
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
@@ -0,0 +1,115 @@
+/*
+ * 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.gallery.preference
+
+import android.os.Bundle
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.settingslib.spa.framework.common.PageModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+class PreferencePageModel : PageModel() {
+ companion object {
+ // Defines all the resources for this page.
+ // In real Settings App, resources data is defined in xml, rather than SPP.
+ const val PAGE_TITLE = "Sample Preference"
+ const val SIMPLE_PREFERENCE_TITLE = "Preference"
+ const val SIMPLE_PREFERENCE_SUMMARY = "Simple summary"
+ const val DISABLE_PREFERENCE_TITLE = "Disabled"
+ const val DISABLE_PREFERENCE_SUMMARY = "Disabled summary"
+ const val ASYNC_PREFERENCE_TITLE = "Async Preference"
+ private const val ASYNC_PREFERENCE_SUMMARY = "Async summary"
+ const val MANUAL_UPDATE_PREFERENCE_TITLE = "Manual Updater"
+ const val AUTO_UPDATE_PREFERENCE_TITLE = "Auto Updater"
+ val SIMPLE_PREFERENCE_KEYWORDS = listOf("simple keyword1", "simple keyword2")
+
+ @Composable
+ fun create(): PreferencePageModel {
+ val pageModel: PreferencePageModel = viewModel()
+ pageModel.initOnce()
+ return pageModel
+ }
+
+ fun logMsg(message: String) {
+ Log.d("PreferencePageModel", message)
+ }
+ }
+
+ private val asyncSummary = mutableStateOf(" ")
+
+ private val manualUpdater = mutableStateOf(0)
+
+ private val autoUpdater = object : MutableLiveData<String>(" ") {
+ private var tick = 0
+ private var updateJob: Job? = null
+ override fun onActive() {
+ logMsg("autoUpdater.active")
+ updateJob = viewModelScope.launch(Dispatchers.IO) {
+ while (true) {
+ delay(1000L)
+ tick++
+ logMsg("autoUpdater.value $tick")
+ postValue(tick.toString())
+ }
+ }
+ }
+
+ override fun onInactive() {
+ logMsg("autoUpdater.inactive")
+ updateJob?.cancel()
+ }
+ }
+
+ override fun initialize(arguments: Bundle?) {
+ logMsg("init with args " + arguments.toString())
+
+ viewModelScope.launch(Dispatchers.IO) {
+ delay(2000L)
+ asyncSummary.value = ASYNC_PREFERENCE_SUMMARY
+ }
+ }
+
+ fun getAsyncSummary(): State<String> {
+ logMsg("getAsyncSummary")
+ return asyncSummary
+ }
+
+ fun getManualUpdaterSummary(): State<String> {
+ logMsg("getManualUpdaterSummary")
+ return derivedStateOf { manualUpdater.value.toString() }
+ }
+
+ fun manualUpdaterOnClick() {
+ logMsg("manualUpdaterOnClick")
+ manualUpdater.value = manualUpdater.value + 1
+ }
+
+ fun getAutoUpdaterSummary(): LiveData<String> {
+ logMsg("getAutoUpdaterSummary")
+ return autoUpdater
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index e5a1862599b6..c1bed07b0385 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -31,12 +31,22 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.ROOT_PAGE_NAME
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.navRoute
+const val NULL_PAGE_NAME = "NULL"
+
+/**
+ * The Activity to render ALL SPA pages, and handles jumps between SPA pages.
+ * One can open any SPA page by:
+ * $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination <SpaPageRoute>
+ * For gallery, BrowseActivityComponent = com.android.settingslib.spa.gallery/.MainActivity
+ * Some examples:
+ * $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
+ * $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
+ */
open class BrowseActivity(
private val sppRepository: SettingsPageProviderRepository,
) : ComponentActivity() {
@@ -55,8 +65,8 @@ open class BrowseActivity(
private fun MainContent() {
val navController = rememberNavController()
CompositionLocalProvider(navController.localNavController()) {
- NavHost(navController, ROOT_PAGE_NAME) {
- composable(ROOT_PAGE_NAME) {}
+ NavHost(navController, NULL_PAGE_NAME) {
+ composable(NULL_PAGE_NAME) {}
for (page in sppRepository.getAllProviders()) {
composable(
route = page.name + page.parameter.navRoute() +
@@ -82,7 +92,7 @@ open class BrowseActivity(
destinationNavigated.value = true
LaunchedEffect(Unit) {
val destination =
- intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
+ intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPage()
if (destination.isNotEmpty()) {
navController.navigate(destination) {
popUpTo(navController.graph.findStartDestination().id) {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index 8aef2c6aa068..b196300abb63 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -25,6 +25,7 @@ import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
@@ -33,7 +34,6 @@ import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
-import com.android.settingslib.spa.framework.EntryProvider.Companion.PAGE_INFO_QUERY
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
@@ -54,6 +54,12 @@ private const val ROUTE_ENTRY = "entry"
private const val PARAM_NAME_PAGE_ID = "pid"
private const val PARAM_NAME_ENTRY_ID = "eid"
+/**
+ * The Debug Activity to display all Spa Pages & Entries.
+ * One can open the debug activity by:
+ * $ adb shell am start -n <Activity>
+ * For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
+ */
open class DebugActivity(
private val entryRepository: SettingsEntryRepository,
private val browseActivityClass: Class<*>,
@@ -75,15 +81,16 @@ open class DebugActivity(
if (entryProviderAuthorities == null) return
try {
+ val query = EntryProvider.QueryEnum.PAGE_INFO_QUERY
contentResolver.query(
- Uri.parse("content://$entryProviderAuthorities/${PAGE_INFO_QUERY.queryPath}"),
+ Uri.parse("content://$entryProviderAuthorities/${query.queryPath}"),
null, null, null
).use { cursor ->
while (cursor != null && cursor.moveToNext()) {
- val route = cursor.getString(PAGE_INFO_QUERY.getIndex(ColumnName.PAGE_ROUTE))
- val entryCount = cursor.getInt(PAGE_INFO_QUERY.getIndex(ColumnName.ENTRY_COUNT))
+ val route = cursor.getString(query, EntryProvider.ColumnEnum.PAGE_ROUTE)
+ val entryCount = cursor.getInt(query, EntryProvider.ColumnEnum.ENTRY_COUNT)
val hasRuntimeParam =
- cursor.getInt(PAGE_INFO_QUERY.getIndex(ColumnName.HAS_RUNTIME_PARAM)) == 1
+ cursor.getBoolean(query, EntryProvider.ColumnEnum.HAS_RUNTIME_PARAM)
Log.d(
"DEBUG ACTIVITY", "Page Info: $route ($entryCount) " +
(if (hasRuntimeParam) "with" else "no") + "-runtime-params"
@@ -106,13 +113,13 @@ open class DebugActivity(
composable(
route = "$ROUTE_PAGE/{$PARAM_NAME_PAGE_ID}",
arguments = listOf(
- navArgument(PARAM_NAME_PAGE_ID) { type = NavType.IntType },
+ navArgument(PARAM_NAME_PAGE_ID) { type = NavType.StringType },
)
) { navBackStackEntry -> OnePage(navBackStackEntry.arguments) }
composable(
route = "$ROUTE_ENTRY/{$PARAM_NAME_ENTRY_ID}",
arguments = listOf(
- navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.IntType },
+ navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.StringType },
)
) { navBackStackEntry -> OneEntry(navBackStackEntry.arguments) }
}
@@ -121,13 +128,15 @@ open class DebugActivity(
@Composable
fun RootPage() {
+ val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
+ val allEntry = remember { entryRepository.getAllEntries() }
HomeScaffold(title = "Settings Debug") {
Preference(object : PreferenceModel {
- override val title = "List All Pages"
+ override val title = "List All Pages (${allPageWithEntry.size})"
override val onClick = navigator(route = ROUTE_All_PAGES)
})
Preference(object : PreferenceModel {
- override val title = "List All Entries"
+ override val title = "List All Entries (${allEntry.size})"
override val onClick = navigator(route = ROUTE_All_ENTRIES)
})
}
@@ -135,14 +144,15 @@ open class DebugActivity(
@Composable
fun AllPages() {
- RegularScaffold(title = "All Pages") {
- for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
+ val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
+ RegularScaffold(title = "All Pages (${allPageWithEntry.size})") {
+ for (pageWithEntry in allPageWithEntry) {
Preference(object : PreferenceModel {
override val title =
- "${pageWithEntry.page.name} (${pageWithEntry.entries.size})"
+ "${pageWithEntry.page.displayName} (${pageWithEntry.entries.size})"
override val summary = pageWithEntry.page.formatArguments().toState()
override val onClick =
- navigator(route = ROUTE_PAGE + "/${pageWithEntry.page.id}")
+ navigator(route = ROUTE_PAGE + "/${pageWithEntry.page.id()}")
})
}
}
@@ -150,16 +160,18 @@ open class DebugActivity(
@Composable
fun AllEntries() {
- RegularScaffold(title = "All Entries") {
- EntryList(entryRepository.getAllEntries())
+ val allEntry = remember { entryRepository.getAllEntries() }
+ RegularScaffold(title = "All Entries (${allEntry.size})") {
+ EntryList(allEntry)
}
}
@Composable
fun OnePage(arguments: Bundle?) {
- val id = arguments!!.getInt(PARAM_NAME_PAGE_ID)
+ val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "")
val pageWithEntry = entryRepository.getPageWithEntry(id)!!
- RegularScaffold(title = "Page ${pageWithEntry.page.name}") {
+ RegularScaffold(title = "Page - ${pageWithEntry.page.displayName}") {
+ Text(text = pageWithEntry.page.id().toString())
Text(text = pageWithEntry.page.formatArguments())
Text(text = "Entry size: ${pageWithEntry.entries.size}")
Preference(model = object : PreferenceModel {
@@ -173,15 +185,16 @@ open class DebugActivity(
@Composable
fun OneEntry(arguments: Bundle?) {
- val id = arguments!!.getInt(PARAM_NAME_ENTRY_ID)
+ val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
val entry = entryRepository.getEntry(id)!!
- RegularScaffold(title = "Entry ${entry.displayName()}") {
+ val entryContent = remember { entry.formatContent() }
+ RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"
override val enabled = (!entry.hasRuntimeParam()).toState()
override val onClick = openEntry(entry)
})
- Text(text = entry.formatAll())
+ Text(text = entryContent)
}
}
@@ -189,10 +202,10 @@ open class DebugActivity(
private fun EntryList(entries: Collection<SettingsEntry>) {
for (entry in entries) {
Preference(object : PreferenceModel {
- override val title = entry.displayName()
+ override val title = entry.displayTitle()
override val summary =
- "${entry.fromPage?.name} -> ${entry.toPage?.name}".toState()
- override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id}")
+ "${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}".toState()
+ override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id()}")
})
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
index 90ce182cd2bd..5e4419a60ef0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -28,35 +28,55 @@ import android.util.Log
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
/**
- * Enum to define all column names in provider.
+ * The content provider to return entry related data, which can be used for search and hierarchy.
+ * One can query the provider result by:
+ * $ adb shell content query --uri content://<AuthorityPath>/<QueryPath>
+ * For gallery, AuthorityPath = com.android.spa.gallery.provider
+ * Some examples:
+ * $ adb shell content query --uri content://<AuthorityPath>/page_start
+ * $ adb shell content query --uri content://<AuthorityPath>/page_info
*/
-enum class ColumnName(val id: String) {
- PAGE_NAME("pageName"),
- PAGE_ROUTE("pageRoute"),
- ENTRY_COUNT("entryCount"),
- HAS_RUNTIME_PARAM("hasRuntimeParam"),
- PAGE_START_ADB("pageStartAdb"),
-}
-
-data class QueryDefinition(
- val queryPath: String,
- val queryMatchCode: Int,
- val columnNames: List<ColumnName>,
-) {
- fun getColumns(): Array<String> {
- return columnNames.map { it.id }.toTypedArray()
- }
-
- fun getIndex(name: ColumnName): Int {
- return columnNames.indexOf(name)
- }
-}
-
open class EntryProvider(
private val entryRepository: SettingsEntryRepository,
private val browseActivityComponentName: String? = null,
) : ContentProvider() {
+ /**
+ * Enum to define all column names in provider.
+ */
+ enum class ColumnEnum(val id: String) {
+ PAGE_ID("pageId"),
+ PAGE_NAME("pageName"),
+ PAGE_ROUTE("pageRoute"),
+ ENTRY_COUNT("entryCount"),
+ HAS_RUNTIME_PARAM("hasRuntimeParam"),
+ PAGE_START_ADB("pageStartAdb"),
+ }
+
+ /**
+ * Enum to define all queries supported in the provider.
+ */
+ enum class QueryEnum(
+ val queryPath: String,
+ val queryMatchCode: Int,
+ val columnNames: List<ColumnEnum>
+ ) {
+ PAGE_START_COMMAND(
+ "page_start", 1,
+ listOf(ColumnEnum.PAGE_START_ADB)
+ ),
+ PAGE_INFO_QUERY(
+ "page_info", 2,
+ listOf(
+ ColumnEnum.PAGE_ID,
+ ColumnEnum.PAGE_NAME,
+ ColumnEnum.PAGE_ROUTE,
+ ColumnEnum.ENTRY_COUNT,
+ ColumnEnum.HAS_RUNTIME_PARAM,
+ )
+ ),
+ }
+
private var mMatcher: UriMatcher? = null
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
@@ -92,13 +112,13 @@ open class EntryProvider(
if (info != null) {
mMatcher!!.addURI(
info.authority,
- PAGE_START_COMMAND_QUERY.queryPath,
- PAGE_START_COMMAND_QUERY.queryMatchCode
+ QueryEnum.PAGE_START_COMMAND.queryPath,
+ QueryEnum.PAGE_START_COMMAND.queryMatchCode
)
mMatcher!!.addURI(
info.authority,
- PAGE_INFO_QUERY.queryPath,
- PAGE_INFO_QUERY.queryMatchCode
+ QueryEnum.PAGE_INFO_QUERY.queryPath,
+ QueryEnum.PAGE_INFO_QUERY.queryMatchCode
)
}
super.attachInfo(context, info)
@@ -113,8 +133,8 @@ open class EntryProvider(
): Cursor? {
return try {
when (mMatcher!!.match(uri)) {
- PAGE_START_COMMAND_QUERY.queryMatchCode -> queryPageStartCommand()
- PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo()
+ QueryEnum.PAGE_START_COMMAND.queryMatchCode -> queryPageStartCommand()
+ QueryEnum.PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo()
else -> throw UnsupportedOperationException("Unknown Uri $uri")
}
} catch (e: UnsupportedOperationException) {
@@ -126,13 +146,13 @@ open class EntryProvider(
}
private fun queryPageStartCommand(): Cursor {
- val componentName = browseActivityComponentName ?: "[component-name]"
- val cursor = MatrixCursor(PAGE_START_COMMAND_QUERY.getColumns())
+ val componentName = browseActivityComponentName ?: "<activity-component-name>"
+ val cursor = MatrixCursor(QueryEnum.PAGE_START_COMMAND.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val page = pageWithEntry.page
if (!page.hasRuntimeParam()) {
cursor.newRow().add(
- ColumnName.PAGE_START_ADB.id,
+ ColumnEnum.PAGE_START_ADB.id,
"adb shell am start -n $componentName" +
" -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
)
@@ -142,31 +162,39 @@ open class EntryProvider(
}
private fun queryPageInfo(): Cursor {
- val cursor = MatrixCursor(PAGE_INFO_QUERY.getColumns())
+ val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val page = pageWithEntry.page
- cursor.newRow().add(ColumnName.PAGE_NAME.id, page.name)
- .add(ColumnName.PAGE_ROUTE.id, page.buildRoute())
- .add(ColumnName.ENTRY_COUNT.id, pageWithEntry.entries.size)
- .add(ColumnName.HAS_RUNTIME_PARAM.id, if (page.hasRuntimeParam()) 1 else 0)
+ cursor.newRow()
+ .add(ColumnEnum.PAGE_ID.id, page.id())
+ .add(ColumnEnum.PAGE_NAME.id, page.displayName)
+ .add(ColumnEnum.PAGE_ROUTE.id, page.buildRoute())
+ .add(ColumnEnum.ENTRY_COUNT.id, pageWithEntry.entries.size)
+ .add(ColumnEnum.HAS_RUNTIME_PARAM.id, if (page.hasRuntimeParam()) 1 else 0)
}
return cursor
}
+}
- companion object {
- val PAGE_START_COMMAND_QUERY = QueryDefinition(
- "page_start", 1,
- listOf(ColumnName.PAGE_START_ADB)
- )
+fun EntryProvider.QueryEnum.getColumns(): Array<String> {
+ return columnNames.map { it.id }.toTypedArray()
+}
- val PAGE_INFO_QUERY = QueryDefinition(
- "page_info", 2,
- listOf(
- ColumnName.PAGE_NAME,
- ColumnName.PAGE_ROUTE,
- ColumnName.ENTRY_COUNT,
- ColumnName.HAS_RUNTIME_PARAM,
- )
- )
- }
+fun EntryProvider.QueryEnum.getIndex(name: EntryProvider.ColumnEnum): Int {
+ return columnNames.indexOf(name)
+}
+
+fun Cursor.getString(query: EntryProvider.QueryEnum, columnName: EntryProvider.ColumnEnum): String {
+ return this.getString(query.getIndex(columnName))
+}
+
+fun Cursor.getInt(query: EntryProvider.QueryEnum, columnName: EntryProvider.ColumnEnum): Int {
+ return this.getInt(query.getIndex(columnName))
+}
+
+fun Cursor.getBoolean(
+ query: EntryProvider.QueryEnum,
+ columnName: EntryProvider.ColumnEnum
+): Boolean {
+ return this.getInt(query.getIndex(columnName)) == 1
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMarco.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMarco.kt
new file mode 100644
index 000000000000..8399d125b24e
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMarco.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.framework.common
+
+import androidx.compose.runtime.Composable
+
+/**
+ * Defines interface of a entry marco, which contains all entry functions to support different
+ * scenarios, such as browsing (UiLayout), search, etc.
+ * SPA team will rebuild some entry marcos, in order to make the entry creation easier.
+ */
+interface EntryMarco {
+ @Composable
+ fun UiLayout() {}
+ fun getSearchData(): EntrySearchData? = null
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt
new file mode 100644
index 000000000000..9b262afc9c53
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt
@@ -0,0 +1,33 @@
+/*
+ * 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.framework.common
+
+/**
+ * Defines Search data of one Settings entry.
+ */
+data class EntrySearchData(
+ val title: String = "",
+ val keyword: List<String> = emptyList(),
+) {
+ fun format(): String {
+ val content = listOf(
+ "search_title = $title",
+ "search_keyword = $keyword",
+ )
+ return content.joinToString("\n")
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
index ee12f02e5a7a..edcca18017c1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
@@ -22,7 +22,7 @@ import androidx.lifecycle.ViewModel
open class PageModel : ViewModel() {
var initialized = false
- fun initOnce(arguments: Bundle?) {
+ fun initOnce(arguments: Bundle? = null) {
// Initialize only once
if (initialized) return
initialized = true
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
index 445c4ebfcee2..2239066201bf 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
@@ -18,84 +18,20 @@ package com.android.settingslib.spa.framework.common
import android.os.Bundle
import androidx.compose.runtime.Composable
-import androidx.navigation.NamedNavArgument
-import androidx.navigation.NavType
-import com.android.settingslib.spa.framework.BrowseActivity
-import com.android.settingslib.spa.framework.util.navLink
+import androidx.compose.runtime.remember
const val INJECT_ENTRY_NAME = "INJECT"
const val ROOT_ENTRY_NAME = "ROOT"
-const val ROOT_PAGE_NAME = "Root"
-
-/**
- * Defines data of one Settings entry for Settings search.
- */
-data class SearchData(val keyword: String = "")
-
-/**
- * Defines data of one Settings entry for UI rendering.
- */
-data class UiData(val title: String = "")
-
-/**
- * Defines data to identify a Settings page.
- */
-data class SettingsPage(
- // The unique id of this page, which is computed by name + normalized(arguments)
- val id: Int,
-
- // The name of the page, which is used to compute the unique id, and need to be stable.
- val name: String,
-
- // Defined parameters of this page.
- val parameter: List<NamedNavArgument> = emptyList(),
-
- // The arguments of this page.
- val arguments: Bundle? = null,
-) {
- companion object {
- fun create(
- name: String,
- parameter: List<NamedNavArgument> = emptyList(),
- arguments: Bundle? = null
- ): SettingsPage {
- return SettingsPageBuilder(name, parameter).setArguments(arguments).build()
- }
- }
-
- fun formatArguments(): String {
- val normalizedArguments = parameter.normalize(arguments)
- if (normalizedArguments == null || normalizedArguments.isEmpty) return "[No arguments]"
- return normalizedArguments.toString().removeRange(0, 6)
- }
-
- fun formatAll(): String {
- return "$name ${formatArguments()}"
- }
-
- fun buildRoute(highlightEntryName: String? = null): String {
- val highlightParam =
- if (highlightEntryName == null)
- ""
- else
- "?${BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME}=$highlightEntryName"
- return name + parameter.navLink(arguments) + highlightParam
- }
-
- fun hasRuntimeParam(): Boolean {
- return parameter.hasRuntimeParam(arguments)
- }
-}
/**
* Defines data of a Settings entry.
*/
data class SettingsEntry(
- // The unique id of this entry, which is computed by name + owner + fromPage + toPage.
- val id: Int,
-
// The name of the page, which is used to compute the unique id, and need to be stable.
- val name: String,
+ private val name: String,
+
+ // The display name of the page, for better readability.
+ val displayName: String,
// The owner page of this entry.
val owner: SettingsPage,
@@ -109,7 +45,7 @@ data class SettingsEntry(
* Defines entry attributes here.
* ========================================
*/
- val isAllowSearch: Boolean,
+ val isAllowSearch: Boolean = false,
/**
* ========================================
@@ -121,13 +57,7 @@ data class SettingsEntry(
* API to get Search related data for this entry.
* Returns null if this entry is not available for the search at the moment.
*/
- val searchData: () -> SearchData? = { null },
-
- /**
- * API to get UI related data for this entry.
- * Returns null if the entry is not render-able.
- */
- val uiData: () -> UiData? = { null },
+ private val searchDataImpl: (arguments: Bundle?) -> EntrySearchData? = { null },
/**
* API to Render UI of this entry directly. For now, we use it in the internal injection, to
@@ -135,68 +65,56 @@ data class SettingsEntry(
* injected entry. In the long term, we may deprecate the @Composable Page() API in SPP, and
* use each entries' UI rendering function in the page instead.
*/
- val uiLayoutImpl: (@Composable (arguments: Bundle?) -> Unit) = {},
+ private val uiLayoutImpl: (@Composable (arguments: Bundle?) -> Unit) = {},
) {
- fun formatAll(): String {
+ // The unique id of this entry, which is computed by name + owner + fromPage + toPage.
+ fun id(): String {
+ return "$name:${owner.id()}(${fromPage?.id()}-${toPage?.id()})".toHashId()
+ }
+
+ fun formatContent(): String {
val content = listOf(
- "owner = ${owner.formatAll()}",
- "linkFrom = ${fromPage?.formatAll()}",
- "linkTo = ${toPage?.formatAll()}",
+ "id = ${id()}",
+ "owner = ${owner.formatDisplayTitle()}",
+ "linkFrom = ${fromPage?.formatDisplayTitle()}",
+ "linkTo = ${toPage?.formatDisplayTitle()}",
+ "${getSearchData()?.format()}",
)
return content.joinToString("\n")
}
- // The display name of the entry, for better readability.
- fun displayName(): String {
- return "${owner.name}:$name"
+ fun displayTitle(): String {
+ return "${owner.displayName}:$displayName"
}
- private fun getDisplayPage(): SettingsPage {
- // Display the entry on its from-page, or on its owner page if the from-page is unset.
+ private fun containerPage(): SettingsPage {
+ // The Container page of the entry, which is the from-page or
+ // the owner-page if from-page is unset.
return fromPage ?: owner
}
fun buildRoute(): String {
- return getDisplayPage().buildRoute(name)
+ return containerPage().buildRoute(id())
}
fun hasRuntimeParam(): Boolean {
- return getDisplayPage().hasRuntimeParam()
+ return containerPage().hasRuntimeParam()
}
- @Composable
- fun UiLayout(runtimeArguments: Bundle? = null) {
+ private fun fullArgument(runtimeArguments: Bundle? = null): Bundle {
val arguments = Bundle()
if (owner.arguments != null) arguments.putAll(owner.arguments)
if (runtimeArguments != null) arguments.putAll(runtimeArguments)
- uiLayoutImpl(arguments)
+ return arguments
}
-}
-
-data class SettingsPageWithEntry(
- val page: SettingsPage,
- val entries: List<SettingsEntry>,
-)
-class SettingsPageBuilder(
- private val name: String,
- private val parameter: List<NamedNavArgument> = emptyList()
-) {
- private var arguments: Bundle? = null
-
- fun build(): SettingsPage {
- val normArguments = parameter.normalize(arguments)
- return SettingsPage(
- id = "$name:${normArguments?.toString()}".toUniqueId(),
- name = name,
- parameter = parameter,
- arguments = arguments,
- )
+ fun getSearchData(runtimeArguments: Bundle? = null): EntrySearchData? {
+ return searchDataImpl(fullArgument(runtimeArguments))
}
- fun setArguments(arguments: Bundle?): SettingsPageBuilder {
- this.arguments = arguments
- return this
+ @Composable
+ fun UiLayout(runtimeArguments: Bundle? = null) {
+ uiLayoutImpl(fullArgument(runtimeArguments))
}
}
@@ -204,32 +122,41 @@ class SettingsPageBuilder(
* The helper to build a Settings Entry instance.
*/
class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
+ private var displayName = name
private var fromPage: SettingsPage? = null
private var toPage: SettingsPage? = null
- private var isAllowSearch: Boolean? = null
- private var searchDataFn: () -> SearchData? = { null }
- private var uiLayoutFn: (@Composable (arguments: Bundle?) -> Unit) = {}
+ // Attributes
+ private var isAllowSearch: Boolean = false
+
+ // Functions
+ private var searchDataFn: (arguments: Bundle?) -> EntrySearchData? = { null }
+ private var uiLayoutFn: (@Composable (arguments: Bundle?) -> Unit) = { }
fun build(): SettingsEntry {
return SettingsEntry(
- id = "$name:${owner.id}(${fromPage?.id}-${toPage?.id})".toUniqueId(),
name = name,
owner = owner,
+ displayName = displayName,
// linking data
fromPage = fromPage,
toPage = toPage,
// attributes
- isAllowSearch = getIsSearchable(),
+ isAllowSearch = isAllowSearch,
// functions
- searchData = searchDataFn,
+ searchDataImpl = searchDataFn,
uiLayoutImpl = uiLayoutFn,
)
}
+ fun setDisplayName(displayName: String): SettingsEntryBuilder {
+ this.displayName = displayName
+ return this
+ }
+
fun setLink(
fromPage: SettingsPage? = null,
toPage: SettingsPage? = null
@@ -244,7 +171,16 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return this
}
- fun setSearchDataFn(fn: () -> SearchData?): SettingsEntryBuilder {
+ fun setMarco(fn: (arguments: Bundle?) -> EntryMarco): SettingsEntryBuilder {
+ setSearchDataFn { fn(it).getSearchData() }
+ setUiLayoutFn {
+ val marco = remember { fn(it) }
+ marco.UiLayout()
+ }
+ return this
+ }
+
+ fun setSearchDataFn(fn: (arguments: Bundle?) -> EntrySearchData?): SettingsEntryBuilder {
this.searchDataFn = fn
return this
}
@@ -254,8 +190,6 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return this
}
- private fun getIsSearchable(): Boolean = isAllowSearch ?: false
-
companion object {
fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
return SettingsEntryBuilder(entryName, owner)
@@ -269,48 +203,19 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return create(entryName, owner).setLink(toPage = owner)
}
- fun createInject(owner: SettingsPage, entryName: String? = null): SettingsEntryBuilder {
- val name = entryName ?: "${INJECT_ENTRY_NAME}_${owner.name}"
- return createLinkTo(name, owner)
+ fun create(owner: SettingsPage, entryName: String, displayName: String? = null):
+ SettingsEntryBuilder {
+ return SettingsEntryBuilder(entryName, owner).setDisplayName(displayName ?: entryName)
}
- fun createRoot(owner: SettingsPage, entryName: String? = null): SettingsEntryBuilder {
- val name = entryName ?: "${ROOT_ENTRY_NAME}_${owner.name}"
- return createLinkTo(name, owner)
+ fun createInject(owner: SettingsPage, displayName: String? = null): SettingsEntryBuilder {
+ val name = displayName ?: "${INJECT_ENTRY_NAME}_${owner.displayName}"
+ return createLinkTo(INJECT_ENTRY_NAME, owner).setDisplayName(name)
}
- }
-}
-
-private fun String.toUniqueId(): Int {
- return this.hashCode()
-}
-private fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
- if (this.isEmpty()) return null
- val normArgs = Bundle()
- for (navArg in this) {
- when (navArg.argument.type) {
- NavType.StringType -> {
- val value = arguments?.getString(navArg.name)
- if (value != null)
- normArgs.putString(navArg.name, value)
- else
- normArgs.putString("unset_" + navArg.name, null)
- }
- NavType.IntType -> {
- if (arguments != null && arguments.containsKey(navArg.name))
- normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
- else
- normArgs.putString("unset_" + navArg.name, null)
- }
+ fun createRoot(owner: SettingsPage, displayName: String? = null): SettingsEntryBuilder {
+ val name = displayName ?: "${ROOT_ENTRY_NAME}_${owner.displayName}"
+ return createLinkTo(ROOT_ENTRY_NAME, owner).setDisplayName(name)
}
}
- return normArgs
-}
-
-private fun List<NamedNavArgument>.hasRuntimeParam(arguments: Bundle? = null): Boolean {
- for (navArg in this) {
- if (arguments == null || !arguments.containsKey(navArg.name)) return true
- }
- return false
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
index a4e5a58246ff..77f064d35eb7 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
@@ -21,15 +21,20 @@ import java.util.LinkedList
private const val MAX_ENTRY_SIZE = 5000
+data class SettingsPageWithEntry(
+ val page: SettingsPage,
+ val entries: List<SettingsEntry>,
+)
+
/**
* The repository to maintain all Settings entries
*/
class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
// Map of entry unique Id to entry
- private val entryMap: Map<Int, SettingsEntry>
+ private val entryMap: Map<String, SettingsEntry>
// Map of Settings page to its contained entries.
- private val pageWithEntryMap: Map<Int, SettingsPageWithEntry>
+ private val pageWithEntryMap: Map<String, SettingsPageWithEntry>
init {
logMsg("Initialize")
@@ -39,23 +44,29 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
val entryQueue = LinkedList<SettingsEntry>()
for (page in sppRepository.getAllRootPages()) {
val rootEntry = SettingsEntryBuilder.createRoot(owner = page).build()
- if (!entryMap.containsKey(rootEntry.id)) {
+ val rootEntryId = rootEntry.id()
+ if (!entryMap.containsKey(rootEntryId)) {
entryQueue.push(rootEntry)
- entryMap.put(rootEntry.id, rootEntry)
+ entryMap.put(rootEntryId, rootEntry)
}
}
while (entryQueue.isNotEmpty() && entryMap.size < MAX_ENTRY_SIZE) {
val entry = entryQueue.pop()
val page = entry.toPage
- if (page == null || pageWithEntryMap.containsKey(page.id)) continue
+ val pageId = page?.id()
+ if (pageId == null || pageWithEntryMap.containsKey(pageId)) continue
val spp = sppRepository.getProviderOrNull(page.name) ?: continue
- val newEntries = spp.buildEntry(page.arguments)
- pageWithEntryMap[page.id] = SettingsPageWithEntry(page, newEntries)
+ val newEntries = spp.buildEntry(page.arguments).map {
+ // Set from-page if it is missing.
+ if (it.fromPage == null) it.copy(fromPage = page) else it
+ }
+ pageWithEntryMap[pageId] = SettingsPageWithEntry(page, newEntries)
for (newEntry in newEntries) {
- if (!entryMap.containsKey(newEntry.id)) {
+ val newEntryId = newEntry.id()
+ if (!entryMap.containsKey(newEntryId)) {
entryQueue.push(newEntry)
- entryMap.put(newEntry.id, newEntry)
+ entryMap.put(newEntryId, newEntry)
}
}
}
@@ -67,7 +78,7 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
return pageWithEntryMap.values
}
- fun getPageWithEntry(pageId: Int): SettingsPageWithEntry? {
+ fun getPageWithEntry(pageId: String): SettingsPageWithEntry? {
return pageWithEntryMap[pageId]
}
@@ -75,7 +86,7 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
return entryMap.values
}
- fun getEntry(entryId: Int): SettingsEntry? {
+ fun getEntry(entryId: String): SettingsEntry? {
return entryMap[entryId]
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
new file mode 100644
index 000000000000..124743a23274
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.framework.common
+
+import android.os.Bundle
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavType
+import com.android.settingslib.spa.framework.BrowseActivity
+import com.android.settingslib.spa.framework.util.navLink
+
+/**
+ * Defines data to identify a Settings page.
+ */
+data class SettingsPage(
+ // The name of the page, which is used to compute the unique id, and need to be stable.
+ val name: String,
+
+ // The display name of the page, for better readability.
+ val displayName: String,
+
+ // Defined parameters of this page.
+ val parameter: List<NamedNavArgument> = emptyList(),
+
+ // The arguments of this page.
+ val arguments: Bundle? = null,
+) {
+ companion object {
+ fun create(
+ name: String,
+ parameter: List<NamedNavArgument> = emptyList(),
+ arguments: Bundle? = null
+ ): SettingsPage {
+ return SettingsPage(name, name, parameter, arguments)
+ }
+ }
+
+ // The unique id of this page, which is computed by name + normalized(arguments)
+ fun id(): String {
+ val normArguments = parameter.normalize(arguments)
+ return "$name:${normArguments?.toString()}".toHashId()
+ }
+
+ // Returns if this Settings Page is created by the given Spp.
+ fun isCreateBy(SppName: String): Boolean {
+ return name == SppName
+ }
+
+ fun formatArguments(): String {
+ val normalizedArguments = parameter.normalize(arguments)
+ if (normalizedArguments == null || normalizedArguments.isEmpty) return "[No arguments]"
+ return normalizedArguments.toString().removeRange(0, 6)
+ }
+
+ fun formatDisplayTitle(): String {
+ return "$displayName ${formatArguments()}"
+ }
+
+ fun buildRoute(highlightEntryName: String? = null): String {
+ val highlightParam =
+ if (highlightEntryName == null)
+ ""
+ else
+ "?${BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME}=$highlightEntryName"
+ return name + parameter.navLink(arguments) + highlightParam
+ }
+
+ fun hasRuntimeParam(): Boolean {
+ return parameter.hasRuntimeParam(arguments)
+ }
+}
+
+private fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
+ if (this.isEmpty()) return null
+ val normArgs = Bundle()
+ for (navArg in this) {
+ when (navArg.argument.type) {
+ NavType.StringType -> {
+ val value = arguments?.getString(navArg.name)
+ if (value != null)
+ normArgs.putString(navArg.name, value)
+ else
+ normArgs.putString("unset_" + navArg.name, null)
+ }
+ NavType.IntType -> {
+ if (arguments != null && arguments.containsKey(navArg.name))
+ normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
+ else
+ normArgs.putString("unset_" + navArg.name, null)
+ }
+ }
+ }
+ return normArgs
+}
+
+private fun List<NamedNavArgument>.hasRuntimeParam(arguments: Bundle? = null): Boolean {
+ for (navArg in this) {
+ if (arguments == null || !arguments.containsKey(navArg.name)) return true
+ }
+ return false
+}
+
+fun String.toHashId(): String {
+ return this.hashCode().toString(36)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
index 6adda6b2aa01..77a157fe815b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
@@ -30,12 +30,8 @@ class SettingsPageProviderRepository(
logMsg("Initialize Completed: ${pageProviderMap.size} spp")
}
- fun getDefaultStartPageName(): String {
- return if (rootPages.isNotEmpty()) {
- rootPages[0].name
- } else {
- ""
- }
+ fun getDefaultStartPage(): String {
+ return if (rootPages.isEmpty()) "" else rootPages[0].buildRoute()
}
fun getAllRootPages(): Collection<SettingsPage> {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
index 32ef0bb3d19b..13a2cc9d252c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
@@ -49,7 +49,8 @@ val LocalNavController = compositionLocalOf<NavControllerWrapper> {
}
@Composable
-fun navigator(route: String): () -> Unit {
+fun navigator(route: String?): () -> Unit {
+ if (route == null) return {}
val navController = LocalNavController.current
return { navController.navigate(route) }
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index 0e6f53abca40..39b8d57d9da4 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -21,7 +21,39 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.framework.common.EntryMarco
+import com.android.settingslib.spa.framework.common.EntrySearchData
+import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.ui.createSettingsIcon
+
+data class SimplePreferenceMarco(
+ val title: String,
+ val summary: String? = null,
+ val icon: ImageVector? = null,
+ val disabled: Boolean = false,
+ val clickRoute: String? = null,
+ val searchKeywords: List<String> = emptyList(),
+) : EntryMarco {
+ @Composable
+ override fun UiLayout() {
+ Preference(model = object : PreferenceModel {
+ override val title: String = this@SimplePreferenceMarco.title
+ override val summary = stateOf(this@SimplePreferenceMarco.summary ?: "")
+ override val icon = createSettingsIcon(this@SimplePreferenceMarco.icon)
+ override val enabled = stateOf(!this@SimplePreferenceMarco.disabled)
+ override val onClick = navigator(clickRoute)
+ })
+ }
+
+ override fun getSearchData(): EntrySearchData {
+ return EntrySearchData(
+ title = this@SimplePreferenceMarco.title,
+ keyword = searchKeywords
+ )
+ }
+}
/**
* The widget model for [Preference] widget.
@@ -71,9 +103,9 @@ interface PreferenceModel {
@Composable
fun Preference(model: PreferenceModel) {
val modifier = remember(model.enabled.value, model.onClick) {
- model.onClick?.let { onClick ->
- Modifier.clickable(enabled = model.enabled.value, onClick = onClick)
- } ?: Modifier
+ model.onClick?.let { onClick ->
+ Modifier.clickable(enabled = model.enabled.value, onClick = onClick)
+ } ?: Modifier
}
BasePreference(
title = model.title,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
index 4f28e378e510..25a4e7088a1a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
@@ -33,3 +33,8 @@ fun SettingsIcon(imageVector: ImageVector) {
tint = MaterialTheme.colorScheme.onSurface,
)
}
+
+fun createSettingsIcon(imageVector: ImageVector?): (@Composable () -> Unit)? {
+ if (imageVector == null) return null
+ return { SettingsIcon(imageVector = imageVector) }
+}