Add entryPath support.

Add getTitle API in SPP.
Generate two types of entry path: by displayName of each entry, and by Title of each entry's container page.

Bug: 244122804
Test: manual - build gallery
Change-Id: I405def96fa1b2be0e4638ef5e97a058fdc970f49
This commit is contained in:
Zekan Qian
2022-10-22 11:22:26 +08:00
parent 1d98a8de68
commit 71e66c9f09
9 changed files with 109 additions and 22 deletions

View File

@@ -35,6 +35,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.LocalNavController
@@ -44,7 +45,6 @@ import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.navRoute
private const val TAG = "BrowseActivity"
private const val NULL_PAGE_NAME = "NULL"
/**
* The Activity to render ALL SPA pages, and handles jumps between SPA pages.
@@ -81,9 +81,10 @@ open class BrowseActivity : ComponentActivity() {
private fun MainContent() {
val sppRepository by spaEnvironment.pageProviderRepository
val navController = rememberNavController()
val nullPage = SettingsPage.createNull()
CompositionLocalProvider(navController.localNavController()) {
NavHost(navController, NULL_PAGE_NAME) {
composable(NULL_PAGE_NAME) {}
NavHost(navController, nullPage.sppName) {
composable(nullPage.sppName) {}
for (spp in sppRepository.getAllProviders()) {
composable(
route = spp.name + spp.parameter.navRoute(),

View File

@@ -169,7 +169,8 @@ open class EntryProvider : ContentProvider() {
.add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(Intent.URI_INTENT_SCHEME))
.add(ColumnEnum.SEARCH_TITLE.id, searchData.title)
.add(ColumnEnum.SEARCH_KEYWORD.id, searchData.keyword)
.add(ColumnEnum.SEARCH_PATH.id, entryRepository.getEntryPath(entry.id))
.add(ColumnEnum.SEARCH_PATH.id,
entryRepository.getEntryPathWithTitle(entry.id, searchData.title))
}
private fun fetchStatusData(entry: SettingsEntry, cursor: MatrixCursor) {

View File

@@ -99,17 +99,6 @@ data class SettingsEntry(
*/
private val uiLayoutImpl: (@Composable (arguments: Bundle?) -> Unit) = {},
) {
fun formatContent(): String {
val content = listOf(
"id = $id",
"owner = ${owner.formatDisplayTitle()}",
"linkFrom = ${fromPage?.formatDisplayTitle()}",
"linkTo = ${toPage?.formatDisplayTitle()}",
"${getSearchData()?.format()}",
)
return content.joinToString("\n")
}
fun displayTitle(): String {
return "${owner.displayName}:$displayName"
}

View File

@@ -21,10 +21,13 @@ import java.util.LinkedList
private const val TAG = "EntryRepository"
private const val MAX_ENTRY_SIZE = 5000
private const val MAX_ENTRY_DEPTH = 10
data class SettingsPageWithEntry(
val page: SettingsPage,
val entries: List<SettingsEntry>,
// The inject entry, which to-page is current page.
val injectEntry: SettingsEntry,
)
/**
@@ -42,9 +45,11 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
entryMap = mutableMapOf()
pageWithEntryMap = mutableMapOf()
val nullPage = SettingsPage.createNull()
val entryQueue = LinkedList<SettingsEntry>()
for (page in sppRepository.getAllRootPages()) {
val rootEntry = SettingsEntryBuilder.createRoot(owner = page).build()
val rootEntry =
SettingsEntryBuilder.createRoot(owner = page).setLink(fromPage = nullPage).build()
if (!entryMap.containsKey(rootEntry.id)) {
entryQueue.push(rootEntry)
entryMap.put(rootEntry.id, rootEntry)
@@ -57,7 +62,11 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
if (page == null || pageWithEntryMap.containsKey(page.id)) continue
val spp = sppRepository.getProviderOrNull(page.sppName) ?: continue
val newEntries = spp.buildEntry(page.arguments)
pageWithEntryMap[page.id] = SettingsPageWithEntry(page, newEntries)
pageWithEntryMap[page.id] = SettingsPageWithEntry(
page = page,
entries = newEntries,
injectEntry = entry
)
for (newEntry in newEntries) {
if (!entryMap.containsKey(newEntry.id)) {
entryQueue.push(newEntry)
@@ -88,7 +97,29 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
return entryMap[entryId]
}
fun getEntryPath(entryId: String): String {
return "TODO(path_of_$entryId)"
private fun getEntryPath(entryId: String): List<SettingsEntry> {
val entryPath = ArrayList<SettingsEntry>()
var currentEntry = entryMap[entryId]
while (currentEntry != null && entryPath.size < MAX_ENTRY_DEPTH) {
entryPath.add(currentEntry)
val currentPage = currentEntry.containerPage()
currentEntry = pageWithEntryMap[currentPage.id]?.injectEntry
}
return entryPath
}
fun getEntryPathWithDisplayName(entryId: String): List<String> {
val entryPath = getEntryPath(entryId)
return entryPath.map { it.displayName }
}
fun getEntryPathWithTitle(entryId: String, defaultTitle: String): List<String> {
val entryPath = getEntryPath(entryId)
return entryPath.map {
if (it.toPage == null)
defaultTitle
else
it.toPage.getTitle()!!
}
}
}

View File

@@ -27,6 +27,8 @@ import com.android.settingslib.spa.framework.util.isRuntimeParam
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.framework.util.normalize
private const val NULL_PAGE_NAME = "NULL"
/**
* Defines data to identify a Settings page.
*/
@@ -47,6 +49,10 @@ data class SettingsPage(
val arguments: Bundle? = null,
) {
companion object {
fun createNull(): SettingsPage {
return create(NULL_PAGE_NAME)
}
fun create(
name: String,
displayName: String? = null,
@@ -99,6 +105,11 @@ data class SettingsPage(
return false
}
fun getTitle(): String? {
val sppRepository by SpaEnvironmentFactory.instance.pageProviderRepository
return sppRepository.getProviderOrNull(sppName)?.getTitle(arguments)
}
fun enterPage() {
SpaEnvironmentFactory.instance.logger.event(
id,
@@ -149,6 +160,7 @@ data class SettingsPage(
fun isBrowsable(context: Context?, browseActivityClass: Class<out Activity>?): Boolean {
return context != null &&
browseActivityClass != null &&
!isCreateBy(NULL_PAGE_NAME) &&
!hasRuntimeParam()
}
}

View File

@@ -41,6 +41,8 @@ interface SettingsPageProvider {
fun Page(arguments: Bundle?)
fun buildEntry(arguments: Bundle?): List<SettingsEntry> = emptyList()
fun getTitle(arguments: Bundle?): String = displayName ?: name
}
fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): SettingsPage {

View File

@@ -167,7 +167,7 @@ class DebugActivity : ComponentActivity() {
val entryRepository by spaEnvironment.entryRepository
val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
val entry = entryRepository.getEntry(id)!!
val entryContent = remember { entry.formatContent() }
val entryContent = remember { entry.debugContent(entryRepository) }
RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"

View File

@@ -0,0 +1,50 @@
/*
* 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.debug
import com.android.settingslib.spa.framework.common.EntryStatusData
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryRepository
fun EntryStatusData.debugContent(): String {
val content = listOf(
"is_disabled = $isDisabled",
"is_switch_off = $isSwitchOff",
)
return content.joinToString("\n")
}
fun SettingsEntry.debugContent(entryRepository: SettingsEntryRepository): String {
val searchData = getSearchData()
val statusData = getStatusData()
val entryPathWithName = entryRepository.getEntryPathWithDisplayName(id)
val entryPathWithTitle = entryRepository.getEntryPathWithTitle(id,
searchData?.title ?: displayName)
val content = listOf(
"------ STATIC ------",
"id = $id",
"owner = ${owner.formatDisplayTitle()}",
"linkFrom = ${fromPage?.formatDisplayTitle()}",
"linkTo = ${toPage?.formatDisplayTitle()}",
"hierarchy_path = $entryPathWithName",
"------ SEARCH ------",
"search_path = $entryPathWithTitle",
"${searchData?.format()}",
"${statusData?.debugContent()}"
)
return content.joinToString("\n")
}

View File

@@ -151,9 +151,9 @@ class DebugProvider : ContentProvider() {
.add(ColumnEnum.PAGE_ID.id, page.id)
.add(ColumnEnum.PAGE_NAME.id, page.displayName)
.add(ColumnEnum.PAGE_ROUTE.id, page.buildRoute())
.add(ColumnEnum.PAGE_INTENT_URI.id, intent.toUri(URI_INTENT_SCHEME))
.add(ColumnEnum.PAGE_ENTRY_COUNT.id, pageWithEntry.entries.size)
.add(ColumnEnum.HAS_RUNTIME_PARAM.id, if (page.hasRuntimeParam()) 1 else 0)
.add(ColumnEnum.PAGE_INTENT_URI.id, intent.toUri(URI_INTENT_SCHEME))
}
return cursor
}
@@ -170,7 +170,8 @@ class DebugProvider : ContentProvider() {
.add(ColumnEnum.ENTRY_NAME.id, entry.displayName)
.add(ColumnEnum.ENTRY_ROUTE.id, entry.containerPage().buildRoute())
.add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(URI_INTENT_SCHEME))
.add(ColumnEnum.ENTRY_HIERARCHY_PATH.id, entryRepository.getEntryPath(entry.id))
.add(ColumnEnum.ENTRY_HIERARCHY_PATH.id,
entryRepository.getEntryPathWithDisplayName(entry.id))
}
return cursor
}