Merge "Support entry_info query in EntryProvider; Change id of page & entry as a val instead of a function."

This commit is contained in:
Zekan Qian
2022-09-22 07:18:03 +00:00
committed by Android (Google) Code Review
8 changed files with 113 additions and 55 deletions

View File

@@ -54,7 +54,7 @@ fun createSettingsPage(
parameter: List<NamedNavArgument> = emptyList(),
arguments: Bundle? = null
): SettingsPage {
return SettingsPage(
return SettingsPage.create(
name = SppName.name,
displayName = SppName.displayName,
parameter = parameter,

View File

@@ -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}")
})
}
}

View File

@@ -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://<AuthorityPath>/page_start
* $ adb shell content query --uri content://<AuthorityPath>/page_debug
* $ adb shell content query --uri content://<AuthorityPath>/page_info
* $ adb shell content query --uri content://<AuthorityPath>/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<ColumnEnum>
) {
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<String>?): 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<String>()
)
}
return cursor
}
private fun createBrowsePageIntent(page: SettingsPage): Intent {
if (context == null || browseActivityClass == null || page.hasRuntimeParam())
return Intent()

View File

@@ -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)

View File

@@ -44,26 +44,23 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
val entryQueue = LinkedList<SettingsEntry>()
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)
}
}
}

View File

@@ -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<NamedNavArgument> = 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<NamedNavArgument> = 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<NamedNavArgument>.hasRuntimeParam(arguments: Bundle? = null): B
}
fun String.toHashId(): String {
return this.hashCode().toString(36)
return this.hashCode().toUInt().toString(36)
}

View File

@@ -63,7 +63,7 @@ internal class TogglePermissionAppInfoPageProvider(
override val parameter = PAGE_PARAMETER
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val owner = SettingsPage.create(name, parameter, arguments)
val owner = SettingsPage.create(name, parameter = parameter, arguments = arguments)
val entryList = mutableListOf<SettingsEntry>()
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)
)
}
}
}

View File

@@ -63,7 +63,7 @@ internal class TogglePermissionAppListPageProvider(
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
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<SettingsEntry>()
// TODO: add more categories, such as personal, work, cloned, etc.
@@ -117,7 +117,10 @@ internal class TogglePermissionAppListPageProvider(
listModelSupplier: (Context) -> TogglePermissionAppListModel<out AppRecord>,
): 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)