Add debug page in gallery to show page & entry debug information.
Bug: 244122804
Test: manual - build Spa gallery
Change-Id: I4853e17028dfe0eed4342b4691962b508bc87357
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index 2229986..e5bf8ca 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -29,6 +29,10 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- </application>
+ <activity
+ android:name=".GalleryDebugActivity"
+ android:exported="true">
+ </activity>
+ </application>
</manifest>
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
new file mode 100644
index 0000000..52bbf75
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -0,0 +1,21 @@
+/*
+ * 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
+
+import com.android.settingslib.spa.framework.DebugActivity
+
+class GalleryDebugActivity : DebugActivity(SpaEnvironment.EntryRepository)
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 e75e76c..6f675a3 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
@@ -51,7 +51,7 @@
IllustrationPageProvider,
),
rootPages = listOf(
- SettingsPage(HomePageProvider.name)
+ SettingsPage.create(HomePageProvider.name)
) + ArgumentPageProvider.buildRootPages()
)
}
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 2428bba..7c57e75 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
@@ -17,15 +17,12 @@
package com.android.settingslib.spa.gallery.home
import android.os.Bundle
-import androidx.compose.material3.Button
-import androidx.compose.material3.Text
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.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.gallery.R
-import com.android.settingslib.spa.gallery.SpaEnvironment
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
@@ -55,17 +52,6 @@
SettingsPagerPageProvider.EntryItem()
FooterPageProvider.EntryItem()
IllustrationPageProvider.EntryItem()
-
- /**
- * A test button to generate hierarchy.
- * TODO: remove it once the content provider is ready.
- */
- Button(onClick = {
- SpaEnvironment.EntryRepository.printAllPages()
- SpaEnvironment.EntryRepository.printAllEntries()
- }) {
- Text(text = "Generate Entry")
- }
}
}
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
new file mode 100644
index 0000000..d1ec7d0
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -0,0 +1,155 @@
+/*
+ * 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
+
+import android.os.Bundle
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.navigation.NavType
+import androidx.navigation.compose.NavHost
+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.SettingsEntry
+import com.android.settingslib.spa.framework.common.SettingsEntryRepository
+import com.android.settingslib.spa.framework.compose.localNavController
+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.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.HomeScaffold
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val ROUTE_ROOT = "root"
+private const val ROUTE_All_PAGES = "pages"
+private const val ROUTE_All_ENTRIES = "entries"
+private const val ROUTE_PAGE = "page"
+private const val ROUTE_ENTRY = "entry"
+private const val PARAM_NAME_PAGE_ID = "pid"
+private const val PARAM_NAME_ENTRY_ID = "eid"
+
+open class DebugActivity(
+ private val entryRepository: SettingsEntryRepository
+) : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ setTheme(R.style.Theme_SpaLib_DayNight)
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ SettingsTheme {
+ MainContent()
+ }
+ }
+ }
+
+ @Composable
+ private fun MainContent() {
+ val navController = rememberNavController()
+ CompositionLocalProvider(navController.localNavController()) {
+ NavHost(navController, ROUTE_ROOT) {
+ composable(route = ROUTE_ROOT) { RootPage() }
+ composable(route = ROUTE_All_PAGES) { AllPages() }
+ composable(route = ROUTE_All_ENTRIES) { AllEntries() }
+ composable(
+ route = "$ROUTE_PAGE/{$PARAM_NAME_PAGE_ID}",
+ arguments = listOf(
+ navArgument(PARAM_NAME_PAGE_ID) { type = NavType.IntType },
+ )
+ ) { navBackStackEntry -> OnePage(navBackStackEntry.arguments) }
+ composable(
+ route = "$ROUTE_ENTRY/{$PARAM_NAME_ENTRY_ID}",
+ arguments = listOf(
+ navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.IntType },
+ )
+ ) { navBackStackEntry -> OneEntry(navBackStackEntry.arguments) }
+ }
+ }
+ }
+
+ @Composable
+ fun RootPage() {
+ HomeScaffold(title = "Entry Debug") {
+ Preference(object : PreferenceModel {
+ override val title = "List All Pages"
+ override val onClick = navigator(route = ROUTE_All_PAGES)
+ })
+ Preference(object : PreferenceModel {
+ override val title = "List All Entries"
+ override val onClick = navigator(route = ROUTE_All_ENTRIES)
+ })
+ }
+ }
+
+ @Composable
+ fun AllPages() {
+ RegularScaffold(title = "All Pages") {
+ for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
+ Preference(object : PreferenceModel {
+ override val title =
+ "${pageWithEntry.page.displayName} (${pageWithEntry.entries.size})"
+ override val summary = pageWithEntry.page.formatArguments().toState()
+ override val onClick =
+ navigator(route = ROUTE_PAGE + "/${pageWithEntry.page.id}")
+ })
+ }
+ }
+ }
+
+ @Composable
+ fun AllEntries() {
+ RegularScaffold(title = "All Entries") {
+ EntryList(entryRepository.getAllEntries())
+ }
+ }
+
+ @Composable
+ fun OnePage(arguments: Bundle?) {
+ val id = arguments!!.getInt(PARAM_NAME_PAGE_ID)
+ val pageWithEntry = entryRepository.getPageWithEntry(id)!!
+ RegularScaffold(title = "Page ${pageWithEntry.page.displayName}") {
+ Text(text = pageWithEntry.page.formatArguments())
+ Text(text = "Entry size: ${pageWithEntry.entries.size}")
+ EntryList(pageWithEntry.entries)
+ }
+ }
+
+ @Composable
+ fun OneEntry(arguments: Bundle?) {
+ val id = arguments!!.getInt(PARAM_NAME_ENTRY_ID)
+ val entry = entryRepository.getEntry(id)!!
+ RegularScaffold(title = "Entry ${entry.displayName}") {
+ Text (text = entry.formatAll())
+ }
+ }
+
+ @Composable
+ private fun EntryList(entries: Collection<SettingsEntry>) {
+ for (entry in entries) {
+ Preference(object : PreferenceModel {
+ override val title = entry.displayName
+ override val summary =
+ "${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/common/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
index 98734da..4452b81 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
@@ -37,38 +37,57 @@
/**
* Defines data to identify a Settings page.
*/
-data class SettingsPage(val name: String = "", val arguments: Bundle? = null) {
- override fun toString(): String {
- val argsStr = arguments?.toString()?.removeRange(0, 6) ?: ""
- return name + argsStr
- }
+data class SettingsPage(
+ // The unique id of this page, which is computed by name + 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,
+
+ // The display name of the page, for better readability.
+ // By default, it is the same as name.
+ val displayName: String,
+
+ // 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, parameter.normalize(arguments))
+ return SettingsPageBuilder(name, parameter).setArguments(arguments).build()
}
}
+
+ fun formatArguments(): String {
+ if (arguments == null || arguments.isEmpty) return "[No arguments]"
+ return arguments.toString().removeRange(0, 6)
+ }
+
+ fun formatAll(): String {
+ return "$displayName ${formatArguments()}"
+ }
}
/**
* Defines data of a Settings entry.
*/
data class SettingsEntry(
- // The unique id of this entry.
- // By default, it is computed by name + owner + fromPage + toPage
- val id: String,
+ // The unique id of this entry, which is computed by name + owner + fromPage + toPage.
+ val id: Int,
- // The display name of this entry, which is used to be shown in hierarchy.
- // By default, it is computed by name + owner
- val displayName: String,
-
+ // The name of the page, which is used to compute the unique id, and need to be stable.
val name: String,
+
+ // The owner page of this entry.
val owner: SettingsPage,
+ // The display name of the entry, for better readability.
+ // By default, it is $owner:$name
+ val displayName: String,
+
// Defines linking of Settings entries
val fromPage: SettingsPage? = null,
val toPage: SettingsPage? = null,
@@ -106,8 +125,41 @@
*/
val uiLayout: (@Composable () -> Unit) = {},
) {
- override fun toString(): String {
- return displayName + "(${fromPage?.toString()}->${toPage?.toString()})"
+ fun formatAll(): String {
+ val content = listOf<String>(
+ "owner = ${owner.formatAll()}",
+ "linkFrom = ${fromPage?.formatAll()}",
+ "linkTo = ${toPage?.formatAll()}",
+ )
+ return content.joinToString("\n")
+ }
+}
+
+data class SettingsPageWithEntry(
+ val page: SettingsPage,
+ val entries: List<SettingsEntry>,
+)
+
+class SettingsPageBuilder(
+ private val name: String,
+ private val parameter: List<NamedNavArgument> = emptyList()
+) {
+ private var displayName: String? = null
+ private var arguments: Bundle? = null
+
+ fun build(): SettingsPage {
+ val normArguments = parameter.normalize(arguments)
+ return SettingsPage(
+ id = "$name:${normArguments?.toString()}".toUniqueId(),
+ name = name,
+ displayName = displayName ?: name,
+ arguments = normArguments,
+ )
+ }
+
+ fun setArguments(arguments: Bundle?): SettingsPageBuilder {
+ this.arguments = arguments
+ return this
}
}
@@ -115,7 +167,6 @@
* The helper to build a Settings Entry instance.
*/
class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
- private var uniqueId: String? = null
private var displayName: String? = null
private var fromPage: SettingsPage? = null
private var toPage: SettingsPage? = null
@@ -126,8 +177,8 @@
fun build(): SettingsEntry {
return SettingsEntry(
- id = computeUniqueId(),
- displayName = computeDisplayName(),
+ id = "$name:${owner.id}(${fromPage?.id}-${toPage?.id})".toUniqueId(),
+ displayName = displayName ?: "${owner.displayName}:$name",
name = name,
owner = owner,
@@ -136,7 +187,7 @@
toPage = toPage,
// attributes
- isAllowSearch = computeSearchable(),
+ isAllowSearch = getIsSearchable(),
// functions
searchData = searchDataFn,
@@ -168,12 +219,7 @@
return this
}
- private fun computeUniqueId(): String =
- uniqueId ?: "$owner:$name" + fromPage?.toString() + toPage?.toString()
-
- private fun computeDisplayName(): String = displayName ?: "$owner:$name"
-
- private fun computeSearchable(): Boolean = isAllowSearch ?: false
+ private fun getIsSearchable(): Boolean = isAllowSearch ?: false
companion object {
fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
@@ -197,3 +243,7 @@
}
}
}
+
+private fun String.toUniqueId(): Int {
+ return this.hashCode()
+}
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 e3a55e5..c5f72c5 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
@@ -26,15 +26,15 @@
*/
class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
// Map of entry unique Id to entry
- private val entryMap: Map<String, SettingsEntry>
+ private val entryMap: Map<Int, SettingsEntry>
// Map of Settings page to its contained entries.
- private val pageToEntryListMap: Map<String, List<SettingsEntry>>
+ private val pageWithEntryMap: Map<Int, SettingsPageWithEntry>
init {
logMsg("Initialize")
entryMap = mutableMapOf()
- pageToEntryListMap = mutableMapOf()
+ pageWithEntryMap = mutableMapOf()
val entryQueue = LinkedList<SettingsEntry>()
for (page in sppRepository.getAllRootPages()) {
@@ -48,10 +48,10 @@
while (entryQueue.isNotEmpty() && entryMap.size < MAX_ENTRY_SIZE) {
val entry = entryQueue.pop()
val page = entry.toPage
- if (page == null || pageToEntryListMap.containsKey(page.toString())) continue
+ if (page == null || pageWithEntryMap.containsKey(page.id)) continue
val spp = sppRepository.getProviderOrNull(page.name) ?: continue
val newEntries = spp.buildEntry(page.arguments)
- pageToEntryListMap[page.toString()] = newEntries
+ pageWithEntryMap[page.id] = SettingsPageWithEntry(page, newEntries)
for (newEntry in newEntries) {
if (!entryMap.containsKey(newEntry.id)) {
entryQueue.push(newEntry)
@@ -60,19 +60,23 @@
}
}
- logMsg("Initialize Completed: ${entryMap.size} entries in ${pageToEntryListMap.size} pages")
+ logMsg("Initialize Completed: ${entryMap.size} entries in ${pageWithEntryMap.size} pages")
}
- fun printAllPages() {
- for (entry in pageToEntryListMap.entries) {
- logMsg("page: ${entry.key} with ${entry.value.size} entries")
- }
+ fun getAllPageWithEntry(): Collection<SettingsPageWithEntry> {
+ return pageWithEntryMap.values
}
- fun printAllEntries() {
- for (entry in entryMap.values) {
- logMsg("entry: $entry")
- }
+ fun getPageWithEntry(pageId: Int): SettingsPageWithEntry? {
+ return pageWithEntryMap[pageId]
+ }
+
+ fun getAllEntries(): Collection<SettingsEntry> {
+ return entryMap.values
+ }
+
+ fun getEntry(entryId: Int): SettingsEntry? {
+ return entryMap[entryId]
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index d4d8ea4..c031fe8 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -82,7 +82,7 @@
internal fun navigator(permissionType: String, app: ApplicationInfo) =
navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
- internal fun buildPageId(permissionType: String): SettingsPage {
+ internal fun buildPageData(permissionType: String): SettingsPage {
return SettingsPage.create(
PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index f91a34a..de8a65e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -56,7 +56,7 @@
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val permissionType = parameter.getStringArg(PERMISSION, arguments)!!
val appListPage = SettingsPage.create(name, parameter, arguments)
- val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageId(permissionType)
+ val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageData(permissionType)
val entryList = mutableListOf<SettingsEntry>()
// TODO: add more categories, such as personal, work, cloned, etc.
for (category in listOf("personal")) {