Merge "Add debug page in gallery to show page & entry debug information."
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
@@ -51,7 +51,7 @@ object SpaEnvironment {
|
||||
IllustrationPageProvider,
|
||||
),
|
||||
rootPages = listOf(
|
||||
SettingsPage(HomePageProvider.name)
|
||||
SettingsPage.create(HomePageProvider.name)
|
||||
) + ArgumentPageProvider.buildRootPages()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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 @@ private fun HomePage() {
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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}")
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,38 +37,57 @@ data class UiData(val title: String = "")
|
||||
/**
|
||||
* 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 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 unique id of this entry, which is computed by name + owner + fromPage + toPage.
|
||||
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 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 @@ data class SettingsEntry(
|
||||
*/
|
||||
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 @@ data class SettingsEntry(
|
||||
* 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 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
|
||||
|
||||
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 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
|
||||
toPage = toPage,
|
||||
|
||||
// attributes
|
||||
isAllowSearch = computeSearchable(),
|
||||
isAllowSearch = getIsSearchable(),
|
||||
|
||||
// functions
|
||||
searchData = searchDataFn,
|
||||
@@ -168,12 +219,7 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
|
||||
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 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.toUniqueId(): Int {
|
||||
return this.hashCode()
|
||||
}
|
||||
|
||||
@@ -26,15 +26,15 @@ private const val MAX_ENTRY_SIZE = 5000
|
||||
*/
|
||||
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 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
|
||||
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 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
|
||||
}
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ internal class TogglePermissionAppInfoPageProvider(
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -56,7 +56,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 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")) {
|
||||
|
||||
Reference in New Issue
Block a user