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 773e5f3929fb8..6fe88e1fd37cc 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 @@ -54,7 +54,7 @@ fun createSettingsPage( parameter: List = emptyList(), arguments: Bundle? = null ): SettingsPage { - return SettingsPage( + return SettingsPage.create( name = SppName.name, displayName = SppName.displayName, parameter = parameter, 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 c4e88e158e7a3..c698d9c7ca9a0 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 @@ -153,7 +153,7 @@ open class DebugActivity( "${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}") }) } } @@ -172,7 +172,7 @@ open class DebugActivity( 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 = "id = ${pageWithEntry.page.id}") Text(text = pageWithEntry.page.formatArguments()) Text(text = "Entry size: ${pageWithEntry.entries.size}") Preference(model = object : PreferenceModel { @@ -206,7 +206,7 @@ open class DebugActivity( override val title = entry.displayTitle() override val summary = "${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}".toState() - override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id()}") + 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 33f1eb5b723d5..d923c1ce83e8a 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 @@ -38,8 +38,9 @@ import com.android.settingslib.spa.framework.common.SettingsPage * For gallery, AuthorityPath = com.android.spa.gallery.provider * For SettingsGoogle, AuthorityPath = com.android.settings.spa.provider * Some examples: - * $ adb shell content query --uri content:///page_start + * $ adb shell content query --uri content:///page_debug * $ adb shell content query --uri content:///page_info + * $ adb shell content query --uri content:///entry_info */ open class EntryProvider( private val entryRepository: SettingsEntryRepository, @@ -50,6 +51,7 @@ open class EntryProvider( * Enum to define all column names in provider. */ enum class ColumnEnum(val id: String) { + // Columns related to page PAGE_ID("pageId"), PAGE_NAME("pageName"), PAGE_ROUTE("pageRoute"), @@ -57,6 +59,13 @@ open class EntryProvider( PAGE_ENTRY_COUNT("entryCount"), HAS_RUNTIME_PARAM("hasRuntimeParam"), PAGE_START_ADB("pageStartAdb"), + + // Columns related to entry + ENTRY_ID("entryId"), + ENTRY_NAME("entryName"), + ENTRY_ROUTE("entryRoute"), + ENTRY_TITLE("entryTitle"), + ENTRY_SEARCH_KEYWORD("entrySearchKw"), } /** @@ -67,12 +76,15 @@ open class EntryProvider( val queryMatchCode: Int, val columnNames: List ) { - PAGE_START_COMMAND( - "page_start", 1, + // For debug + PAGE_DEBUG_QUERY( + "page_debug", 1, listOf(ColumnEnum.PAGE_START_ADB) ), + + // page related queries. PAGE_INFO_QUERY( - "page_info", 2, + "page_info", 100, listOf( ColumnEnum.PAGE_ID, ColumnEnum.PAGE_NAME, @@ -82,9 +94,24 @@ open class EntryProvider( ColumnEnum.HAS_RUNTIME_PARAM, ) ), + + // entry related queries + ENTRY_INFO_QUERY( + "entry_info", 200, + listOf( + ColumnEnum.ENTRY_ID, + ColumnEnum.ENTRY_NAME, + ColumnEnum.ENTRY_ROUTE, + ColumnEnum.ENTRY_TITLE, + ColumnEnum.ENTRY_SEARCH_KEYWORD, + ) + ) } - private var uriMatcher: UriMatcher? = null + private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH) + private fun addUri(authority: String, query: QueryEnum) { + uriMatcher.addURI(authority, query.queryPath, query.queryMatchCode) + } override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int { TODO("Implement this to handle requests to delete one or more rows") @@ -115,18 +142,10 @@ open class EntryProvider( } override fun attachInfo(context: Context?, info: ProviderInfo?) { - uriMatcher = UriMatcher(UriMatcher.NO_MATCH) if (info != null) { - uriMatcher!!.addURI( - info.authority, - QueryEnum.PAGE_START_COMMAND.queryPath, - QueryEnum.PAGE_START_COMMAND.queryMatchCode - ) - uriMatcher!!.addURI( - info.authority, - QueryEnum.PAGE_INFO_QUERY.queryPath, - QueryEnum.PAGE_INFO_QUERY.queryMatchCode - ) + addUri(info.authority, QueryEnum.PAGE_DEBUG_QUERY) + addUri(info.authority, QueryEnum.PAGE_INFO_QUERY) + addUri(info.authority, QueryEnum.ENTRY_INFO_QUERY) } super.attachInfo(context, info) } @@ -139,9 +158,10 @@ open class EntryProvider( sortOrder: String? ): Cursor? { return try { - when (uriMatcher!!.match(uri)) { - QueryEnum.PAGE_START_COMMAND.queryMatchCode -> queryPageStartCommand() + when (uriMatcher.match(uri)) { + QueryEnum.PAGE_DEBUG_QUERY.queryMatchCode -> queryPageDebug() QueryEnum.PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo() + QueryEnum.ENTRY_INFO_QUERY.queryMatchCode -> queryEntryInfo() else -> throw UnsupportedOperationException("Unknown Uri $uri") } } catch (e: UnsupportedOperationException) { @@ -152,8 +172,8 @@ open class EntryProvider( } } - private fun queryPageStartCommand(): Cursor { - val cursor = MatrixCursor(QueryEnum.PAGE_START_COMMAND.getColumns()) + private fun queryPageDebug(): Cursor { + val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns()) for (pageWithEntry in entryRepository.getAllPageWithEntry()) { val command = createBrowsePageAdbCommand(pageWithEntry.page) if (command != null) { @@ -168,7 +188,7 @@ open class EntryProvider( for (pageWithEntry in entryRepository.getAllPageWithEntry()) { val page = pageWithEntry.page cursor.newRow() - .add(ColumnEnum.PAGE_ID.id, page.id()) + .add(ColumnEnum.PAGE_ID.id, page.id) .add(ColumnEnum.PAGE_NAME.id, page.displayName) .add(ColumnEnum.PAGE_ROUTE.id, page.buildRoute()) .add(ColumnEnum.PAGE_ENTRY_COUNT.id, pageWithEntry.entries.size) @@ -181,6 +201,24 @@ open class EntryProvider( return cursor } + private fun queryEntryInfo(): Cursor { + val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns()) + for (entry in entryRepository.getAllEntries()) { + // We can add runtime arguments if necessary + val searchData = entry.getSearchData() + cursor.newRow() + .add(ColumnEnum.ENTRY_ID.id, entry.id) + .add(ColumnEnum.ENTRY_NAME.id, entry.displayName) + .add(ColumnEnum.ENTRY_ROUTE.id, entry.buildRoute()) + .add(ColumnEnum.ENTRY_TITLE.id, searchData?.title ?: "") + .add( + ColumnEnum.ENTRY_SEARCH_KEYWORD.id, + searchData?.keyword ?: emptyList() + ) + } + return cursor + } + private fun createBrowsePageIntent(page: SettingsPage): Intent { if (context == null || browseActivityClass == null || page.hasRuntimeParam()) return Intent() 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 b28b8786fde0e..07a6d6cab6fd2 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 @@ -32,6 +32,9 @@ const val ROOT_ENTRY_NAME = "ROOT" * 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: String, + // The name of the page, which is used to compute the unique id, and need to be stable. private val name: String, @@ -72,14 +75,9 @@ data class SettingsEntry( */ private val uiLayoutImpl: (@Composable (arguments: Bundle?) -> Unit) = {}, ) { - // 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( - "id = ${id()}", + "id = $id", "owner = ${owner.formatDisplayTitle()}", "linkFrom = ${fromPage?.formatDisplayTitle()}", "linkTo = ${toPage?.formatDisplayTitle()}", @@ -99,7 +97,7 @@ data class SettingsEntry( } fun buildRoute(): String { - return containerPage().buildRoute(id()) + return containerPage().buildRoute(id) } fun hasRuntimeParam(): Boolean { @@ -121,14 +119,13 @@ data class SettingsEntry( @Composable fun UiLayout(runtimeArguments: Bundle? = null) { val context = LocalContext.current - val entryId = remember { id() } val highlight = rememberSaveable { - mutableStateOf(runtimeArguments?.getString(HIGHLIGHT_ENTRY_PARAM_NAME) == entryId) + mutableStateOf(runtimeArguments?.getString(HIGHLIGHT_ENTRY_PARAM_NAME) == id) } if (highlight.value) { highlight.value = false // TODO: Add highlight entry logic - Toast.makeText(context, "entry $entryId highlighted", Toast.LENGTH_SHORT).show() + Toast.makeText(context, "entry $id highlighted", Toast.LENGTH_SHORT).show() } uiLayoutImpl(fullArgument(runtimeArguments)) } @@ -151,6 +148,7 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings fun build(): SettingsEntry { return SettingsEntry( + id = id(), name = name, owner = owner, displayName = displayName, @@ -206,6 +204,11 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings return this } + // The unique id of this entry, which is computed by name + owner + fromPage + toPage. + private fun id(): String { + return "$name:${owner.id}(${fromPage?.id}-${toPage?.id})".toHashId() + } + companion object { fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder { return SettingsEntryBuilder(entryName, owner) 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 9b3fb94d8ddf0..b6f6203209b16 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 @@ -44,26 +44,23 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) { val entryQueue = LinkedList() for (page in sppRepository.getAllRootPages()) { val rootEntry = SettingsEntryBuilder.createRoot(owner = page).build() - val rootEntryId = rootEntry.id() - if (!entryMap.containsKey(rootEntryId)) { + if (!entryMap.containsKey(rootEntry.id)) { entryQueue.push(rootEntry) - entryMap.put(rootEntryId, rootEntry) + entryMap.put(rootEntry.id, rootEntry) } } while (entryQueue.isNotEmpty() && entryMap.size < MAX_ENTRY_SIZE) { val entry = entryQueue.pop() val page = entry.toPage - val pageId = page?.id() - if (pageId == null || pageWithEntryMap.containsKey(pageId)) continue + if (page == null || pageWithEntryMap.containsKey(page.id)) continue val spp = sppRepository.getProviderOrNull(page.name) ?: continue val newEntries = spp.buildEntry(page.arguments) - pageWithEntryMap[pageId] = SettingsPageWithEntry(page, newEntries) + pageWithEntryMap[page.id] = SettingsPageWithEntry(page, newEntries) for (newEntry in newEntries) { - val newEntryId = newEntry.id() - if (!entryMap.containsKey(newEntryId)) { + if (!entryMap.containsKey(newEntry.id)) { entryQueue.push(newEntry) - entryMap.put(newEntryId, newEntry) + entryMap.put(newEntry.id, newEntry) } } } 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 8ff162b467114..0c301b937cce7 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 @@ -26,6 +26,9 @@ import com.android.settingslib.spa.framework.util.navLink * 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: String, + // The name of the page, which is used to compute the unique id, and need to be stable. val name: String, @@ -41,17 +44,28 @@ data class SettingsPage( companion object { fun create( name: String, + displayName: String? = null, parameter: List = emptyList(), arguments: Bundle? = null ): SettingsPage { - return SettingsPage(name, name, parameter, arguments) + return SettingsPage( + id = id(name, parameter, arguments), + name = name, + displayName = displayName ?: name, + parameter = parameter, + arguments = 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() + // The unique id of this page, which is computed by name + normalized(arguments) + private fun id( + name: String, + parameter: List = emptyList(), + arguments: Bundle? = null + ): String { + val normArguments = parameter.normalize(arguments) + return "$name:${normArguments?.toString()}".toHashId() + } } // Returns if this Settings Page is created by the given Spp. @@ -114,5 +128,5 @@ private fun List.hasRuntimeParam(arguments: Bundle? = null): B } fun String.toHashId(): String { - return this.hashCode().toString(36) + return this.hashCode().toUInt().toString(36) } 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 80b2364ce68d6..883eddff8054c 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 @@ -63,7 +63,7 @@ internal class TogglePermissionAppInfoPageProvider( override val parameter = PAGE_PARAMETER override fun buildEntry(arguments: Bundle?): List { - val owner = SettingsPage.create(name, parameter, arguments) + val owner = SettingsPage.create(name, parameter = parameter, arguments = arguments) val entryList = mutableListOf() entryList.add( SettingsEntryBuilder.create(ENTRY_NAME, owner).setIsAllowSearch(false).build() @@ -108,7 +108,10 @@ internal class TogglePermissionAppInfoPageProvider( fun buildPageData(permissionType: String): SettingsPage { return SettingsPage.create( - PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType)) + name = PAGE_NAME, + parameter = PAGE_PARAMETER, + arguments = 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 bd60bd3699c1f..ec7d75e969dfd 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 @@ -63,7 +63,7 @@ internal class TogglePermissionAppListPageProvider( override fun buildEntry(arguments: Bundle?): List { val permissionType = parameter.getStringArg(PERMISSION, arguments)!! - val appListPage = SettingsPage.create(name, parameter, arguments) + val appListPage = SettingsPage.create(name, parameter = parameter, arguments = arguments) val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageData(permissionType) val entryList = mutableListOf() // TODO: add more categories, such as personal, work, cloned, etc. @@ -117,7 +117,10 @@ internal class TogglePermissionAppListPageProvider( listModelSupplier: (Context) -> TogglePermissionAppListModel, ): SettingsEntryBuilder { val appListPage = SettingsPage.create( - PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType)) + name = PAGE_NAME, + parameter = PAGE_PARAMETER, + arguments = bundleOf(PERMISSION to permissionType) + ) return SettingsEntryBuilder.createInject(owner = appListPage).setIsAllowSearch(false) .setUiLayoutFn { val listModel = rememberContext(listModelSupplier)