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 89daeb1ee0e6a..d3efaa7480f84 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 @@ -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(), 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 3f25c568966c3..38f41bc44a89e 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 @@ -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) { 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 index 9b262afc9c539..9bc620f907e52 100644 --- 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 @@ -22,12 +22,4 @@ package com.android.settingslib.spa.framework.common data class EntrySearchData( val title: String = "", val keyword: List = 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/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt index 39fd916f6019d..224fe1d03390c 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 @@ -99,21 +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" - } - fun containerPage(): SettingsPage { // The Container page of the entry, which is the from-page or // the owner-page if from-page is unset. 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 ea20233288b43..e63e4c9d231e9 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,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, + // 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() 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 { + val entryPath = ArrayList() + 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 { + val entryPath = getEntryPath(entryId) + return entryPath.map { it.displayName } + } + + fun getEntryPathWithTitle(entryId: String, defaultTitle: String): List { + val entryPath = getEntryPath(entryId) + return entryPath.map { + if (it.toPage == null) + defaultTitle + else + it.toPage.getTitle()!! + } } } 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 index 07df96e778c44..2fa9229415c81 100644 --- 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 @@ -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, @@ -78,16 +84,6 @@ data class SettingsPage( return sppName == SppName } - fun formatArguments(): String { - val normArguments = parameter.normalize(arguments) - if (normArguments == null || normArguments.isEmpty) return "[No arguments]" - return normArguments.toString().removeRange(0, 6) - } - - fun formatDisplayTitle(): String { - return "$displayName ${formatArguments()}" - } - fun buildRoute(): String { return sppName + parameter.navLink(arguments) } @@ -99,12 +95,17 @@ 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, LogEvent.PAGE_ENTER, category = LogCategory.FRAMEWORK, - details = formatDisplayTitle() + details = displayName, ) } @@ -113,7 +114,7 @@ data class SettingsPage( id, LogEvent.PAGE_LEAVE, category = LogCategory.FRAMEWORK, - details = formatDisplayTitle() + details = displayName, ) } @@ -149,6 +150,7 @@ data class SettingsPage( fun isBrowsable(context: Context?, browseActivityClass: Class?): Boolean { return context != null && browseActivityClass != null && + !isCreateBy(NULL_PAGE_NAME) && !hasRuntimeParam() } } diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt index e8a44119ba848..f8963b2a88371 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProvider.kt @@ -41,6 +41,8 @@ interface SettingsPageProvider { fun Page(arguments: Bundle?) fun buildEntry(arguments: Bundle?): List = emptyList() + + fun getTitle(arguments: Bundle?): String = displayName ?: name } fun SettingsPageProvider.createSettingsPage(arguments: Bundle? = null): SettingsPage { diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt index 301508074f307..9eaa88ae31680 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt @@ -120,12 +120,11 @@ class DebugActivity : ComponentActivity() { val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() } RegularScaffold(title = "All Pages (${allPageWithEntry.size})") { for (pageWithEntry in allPageWithEntry) { + val page = pageWithEntry.page 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}") + override val title = "${page.debugBrief()} (${pageWithEntry.entries.size})" + override val summary = page.debugArguments().toState() + override val onClick = navigator(route = ROUTE_PAGE + "/${page.id}") }) } } @@ -146,16 +145,16 @@ class DebugActivity : ComponentActivity() { val entryRepository by spaEnvironment.entryRepository val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "") val pageWithEntry = entryRepository.getPageWithEntry(id)!! - RegularScaffold(title = "Page - ${pageWithEntry.page.displayName}") { - Text(text = "id = ${pageWithEntry.page.id}") - Text(text = pageWithEntry.page.formatArguments()) + val page = pageWithEntry.page + RegularScaffold(title = "Page - ${page.debugBrief()}") { + Text(text = "id = ${page.id}") + Text(text = page.debugArguments()) Text(text = "Entry size: ${pageWithEntry.entries.size}") Preference(model = object : PreferenceModel { override val title = "open page" override val enabled = - pageWithEntry.page.isBrowsable(context, spaEnvironment.browseActivityClass) - .toState() - override val onClick = openPage(pageWithEntry.page) + page.isBrowsable(context, spaEnvironment.browseActivityClass).toState() + override val onClick = openPage(page) }) EntryList(pageWithEntry.entries) } @@ -167,8 +166,8 @@ 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() } - RegularScaffold(title = "Entry - ${entry.displayTitle()}") { + val entryContent = remember { entry.debugContent(entryRepository) } + RegularScaffold(title = "Entry - ${entry.debugBrief()}") { Preference(model = object : PreferenceModel { override val title = "open entry" override val enabled = @@ -184,7 +183,7 @@ class DebugActivity : ComponentActivity() { private fun EntryList(entries: Collection) { for (entry in entries) { Preference(object : PreferenceModel { - override val title = entry.displayTitle() + override val title = entry.debugBrief() 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/debug/DebugFormat.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugFormat.kt new file mode 100644 index 0000000000000..538d2b577bada --- /dev/null +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugFormat.kt @@ -0,0 +1,75 @@ +/* + * 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.EntrySearchData +import com.android.settingslib.spa.framework.common.EntryStatusData +import com.android.settingslib.spa.framework.common.SettingsEntry +import com.android.settingslib.spa.framework.common.SettingsEntryRepository +import com.android.settingslib.spa.framework.common.SettingsPage +import com.android.settingslib.spa.framework.util.normalize + +private fun EntrySearchData.debugContent(): String { + val content = listOf( + "search_title = $title", + "search_keyword = $keyword", + ) + return content.joinToString("\n") +} + +private fun EntryStatusData.debugContent(): String { + val content = listOf( + "is_disabled = $isDisabled", + "is_switch_off = $isSwitchOff", + ) + return content.joinToString("\n") +} + +fun SettingsPage.debugArguments(): String { + val normArguments = parameter.normalize(arguments) + if (normArguments == null || normArguments.isEmpty) return "[No arguments]" + return normArguments.toString().removeRange(0, 6) +} + +fun SettingsPage.debugBrief(): String { + return displayName +} + +fun SettingsEntry.debugBrief(): String { + return "${owner.displayName}:$displayName" +} + +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.debugBrief()} ${owner.debugArguments()}", + "linkFrom = ${fromPage?.debugBrief()} ${fromPage?.debugArguments()}", + "linkTo = ${toPage?.debugBrief()} ${toPage?.debugArguments()}", + "hierarchy_path = $entryPathWithName", + "------ SEARCH ------", + "search_path = $entryPathWithTitle", + searchData?.debugContent() ?: "no search data", + statusData?.debugContent() ?: "no status data", + ) + return content.joinToString("\n") +} diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugProvider.kt index e4cb3edb9ab24..399278ded6480 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugProvider.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugProvider.kt @@ -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 }