Move debug related activity & provider to debug folder.

Add debug related activity & provider in Gallery.

Bug: 244122804
Test: manual - build gallery
Change-Id: Ie1b005b26eca5b7dd6472ebf6841531c2bc7a071
This commit is contained in:
Zekan Qian
2022-10-19 15:24:15 +08:00
parent 9c246c8d75
commit 285690dcc3
7 changed files with 391 additions and 315 deletions

View File

@@ -34,11 +34,6 @@
</intent-filter>
</activity>
<activity
android:name=".GalleryDebugActivity"
android:exported="true">
</activity>
<provider
android:name=".GalleryEntryProvider"
android:authorities="com.android.spa.gallery.provider"
@@ -46,5 +41,20 @@
android:exported="false">
</provider>
<activity
android:name="com.android.settingslib.spa.framework.debug.BlankActivity"
android:exported="true">
</activity>
<activity
android:name="com.android.settingslib.spa.framework.debug.DebugActivity"
android:exported="true">
</activity>
<provider
android:name="com.android.settingslib.spa.framework.debug.DebugProvider"
android:authorities="com.android.spa.gallery.debug"
android:enabled="true"
android:exported="false">
</provider>
</application>
</manifest>

View File

@@ -1,21 +0,0 @@
/*
* 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()

View File

@@ -16,21 +16,22 @@
package com.android.settingslib.spa.framework
import android.content.ComponentName
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.Intent.URI_INTENT_SCHEME
import android.content.UriMatcher
import android.content.pm.ProviderInfo
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.util.Log
import com.android.settingslib.spa.framework.common.ColumnEnum
import com.android.settingslib.spa.framework.common.QueryEnum
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.addUri
import com.android.settingslib.spa.framework.common.getColumns
private const val TAG = "EntryProvider"
@@ -39,117 +40,15 @@ private const val TAG = "EntryProvider"
* One can query the provider result by:
* $ adb shell content query --uri content://<AuthorityPath>/<QueryPath>
* For gallery, AuthorityPath = com.android.spa.gallery.provider
* For SettingsGoogle, AuthorityPath = com.android.settings.spa.provider
* For Settings, AuthorityPath = com.android.settings.spa.provider
* Some examples:
* $ adb shell content query --uri content://<AuthorityPath>/page_debug
* $ adb shell content query --uri content://<AuthorityPath>/entry_debug
* $ adb shell content query --uri content://<AuthorityPath>/page_info
* $ adb shell content query --uri content://<AuthorityPath>/entry_info
* $ adb shell content query --uri content://<AuthorityPath>/search_sitemap
* $ adb shell content query --uri content://<AuthorityPath>/search_static
* $ adb shell content query --uri content://<AuthorityPath>/search_dynamic
*/
open class EntryProvider : ContentProvider() {
private val spaEnvironment get() = SpaEnvironmentFactory.instance
/**
* 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"),
PAGE_INTENT_URI("pageIntent"),
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_INTENT_URI("entryIntent"),
ENTRY_HIERARCHY_PATH("entryPath"),
ENTRY_START_ADB("entryStartAdb"),
// Columns related to search
ENTRY_TITLE("entryTitle"),
ENTRY_SEARCH_KEYWORD("entrySearchKw"),
}
/**
* Enum to define all queries supported in the provider.
*/
enum class QueryEnum(
val queryPath: String,
val queryMatchCode: Int,
val columnNames: List<ColumnEnum>
) {
// For debug
PAGE_DEBUG_QUERY(
"page_debug", 1,
listOf(ColumnEnum.PAGE_START_ADB)
),
ENTRY_DEBUG_QUERY(
"entry_debug", 2,
listOf(ColumnEnum.ENTRY_START_ADB)
),
// page related queries.
PAGE_INFO_QUERY(
"page_info", 100,
listOf(
ColumnEnum.PAGE_ID,
ColumnEnum.PAGE_NAME,
ColumnEnum.PAGE_ROUTE,
ColumnEnum.PAGE_INTENT_URI,
ColumnEnum.PAGE_ENTRY_COUNT,
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_INTENT_URI,
)
),
// Search related queries
SEARCH_SITEMAP_QUERY(
"search_sitemap", 300,
listOf(
ColumnEnum.ENTRY_ID,
ColumnEnum.ENTRY_HIERARCHY_PATH,
)
),
SEARCH_STATIC_DATA_QUERY(
"search_static", 301,
listOf(
ColumnEnum.ENTRY_ID,
ColumnEnum.ENTRY_TITLE,
ColumnEnum.ENTRY_SEARCH_KEYWORD,
)
),
SEARCH_DYNAMIC_DATA_QUERY(
"search_dynamic", 302,
listOf(
ColumnEnum.ENTRY_ID,
ColumnEnum.ENTRY_TITLE,
ColumnEnum.ENTRY_SEARCH_KEYWORD,
)
),
}
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")
@@ -182,13 +81,9 @@ open class EntryProvider : ContentProvider() {
override fun attachInfo(context: Context?, info: ProviderInfo?) {
if (info != null) {
addUri(info.authority, QueryEnum.PAGE_DEBUG_QUERY)
addUri(info.authority, QueryEnum.ENTRY_DEBUG_QUERY)
addUri(info.authority, QueryEnum.PAGE_INFO_QUERY)
addUri(info.authority, QueryEnum.ENTRY_INFO_QUERY)
addUri(info.authority, QueryEnum.SEARCH_SITEMAP_QUERY)
addUri(info.authority, QueryEnum.SEARCH_STATIC_DATA_QUERY)
addUri(info.authority, QueryEnum.SEARCH_DYNAMIC_DATA_QUERY)
QueryEnum.SEARCH_SITEMAP_QUERY.addUri(uriMatcher, info.authority)
QueryEnum.SEARCH_STATIC_DATA_QUERY.addUri(uriMatcher, info.authority)
QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.addUri(uriMatcher, info.authority)
}
super.attachInfo(context, info)
}
@@ -202,10 +97,6 @@ open class EntryProvider : ContentProvider() {
): Cursor? {
return try {
when (uriMatcher.match(uri)) {
QueryEnum.PAGE_DEBUG_QUERY.queryMatchCode -> queryPageDebug()
QueryEnum.ENTRY_DEBUG_QUERY.queryMatchCode -> queryEntryDebug()
QueryEnum.PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo()
QueryEnum.ENTRY_INFO_QUERY.queryMatchCode -> queryEntryInfo()
QueryEnum.SEARCH_SITEMAP_QUERY.queryMatchCode -> querySearchSitemap()
QueryEnum.SEARCH_STATIC_DATA_QUERY.queryMatchCode -> querySearchStaticData()
QueryEnum.SEARCH_DYNAMIC_DATA_QUERY.queryMatchCode -> querySearchDynamicData()
@@ -219,73 +110,18 @@ open class EntryProvider : ContentProvider() {
}
}
private fun queryPageDebug(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val command = createBrowsePageAdbCommand(pageWithEntry.page)
if (command != null) {
cursor.newRow().add(ColumnEnum.PAGE_START_ADB.id, command)
}
}
return cursor
}
private fun queryEntryDebug(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.ENTRY_DEBUG_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
val command = createBrowsePageAdbCommand(entry.containerPage(), entry.id)
if (command != null) {
cursor.newRow().add(ColumnEnum.ENTRY_START_ADB.id, command)
}
}
return cursor
}
private fun queryPageInfo(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val page = pageWithEntry.page
cursor.newRow()
.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)
.add(ColumnEnum.HAS_RUNTIME_PARAM.id, if (page.hasRuntimeParam()) 1 else 0)
.add(
ColumnEnum.PAGE_INTENT_URI.id,
createBrowsePageIntent(page).toUri(URI_INTENT_SCHEME)
)
}
return cursor
}
private fun queryEntryInfo(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
cursor.newRow()
.add(ColumnEnum.ENTRY_ID.id, entry.id)
.add(ColumnEnum.ENTRY_NAME.id, entry.displayName)
.add(ColumnEnum.ENTRY_ROUTE.id, entry.containerPage().buildRoute())
.add(
ColumnEnum.ENTRY_INTENT_URI.id,
createBrowsePageIntent(entry.containerPage(), entry.id).toUri(URI_INTENT_SCHEME)
)
}
return cursor
}
private fun querySearchSitemap(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.SEARCH_SITEMAP_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
if (!entry.isAllowSearch) continue
val intent = entry.containerPage()
.createBrowseIntent(context, spaEnvironment.browseActivityClass, entry.id)
?: Intent()
cursor.newRow()
.add(ColumnEnum.ENTRY_ID.id, entry.id)
.add(ColumnEnum.ENTRY_HIERARCHY_PATH.id, entryRepository.getEntryPath(entry.id))
.add(ColumnEnum.ENTRY_INTENT_URI.id, intent.toUri(Intent.URI_INTENT_SCHEME))
}
return cursor
}
@@ -321,54 +157,4 @@ open class EntryProvider : ContentProvider() {
searchData?.keyword ?: emptyList<String>()
)
}
private fun createBrowsePageIntent(page: SettingsPage, entryId: String? = null): Intent {
if (!isPageBrowsable(page)) return Intent()
return Intent().setComponent(ComponentName(context!!, spaEnvironment.browseActivityClass!!))
.apply {
putExtra(BrowseActivity.KEY_DESTINATION, page.buildRoute())
if (entryId != null) {
putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
}
}
}
private fun createBrowsePageAdbCommand(page: SettingsPage, entryId: String? = null): String? {
if (!isPageBrowsable(page)) return null
val packageName = context!!.packageName
val activityName = spaEnvironment.browseActivityClass!!.name.replace(packageName, "")
val destinationParam = " -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
val highlightParam =
if (entryId != null) " -e ${BrowseActivity.KEY_HIGHLIGHT_ENTRY} $entryId" else ""
return "adb shell am start -n $packageName/$activityName$destinationParam$highlightParam"
}
private fun isPageBrowsable(page: SettingsPage): Boolean {
return context != null &&
spaEnvironment.browseActivityClass != null &&
!page.hasRuntimeParam()
}
}
fun EntryProvider.QueryEnum.getColumns(): Array<String> {
return columnNames.map { it.id }.toTypedArray()
}
fun EntryProvider.QueryEnum.getIndex(name: EntryProvider.ColumnEnum): Int {
return columnNames.indexOf(name)
}
fun Cursor.getString(query: EntryProvider.QueryEnum, columnName: EntryProvider.ColumnEnum): String {
return this.getString(query.getIndex(columnName))
}
fun Cursor.getInt(query: EntryProvider.QueryEnum, columnName: EntryProvider.ColumnEnum): Int {
return this.getInt(query.getIndex(columnName))
}
fun Cursor.getBoolean(
query: EntryProvider.QueryEnum,
columnName: EntryProvider.ColumnEnum
): Boolean {
return this.getInt(query.getIndex(columnName)) == 1
}

View File

@@ -0,0 +1,126 @@
/*
* 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.common
import android.content.UriMatcher
/**
* 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"),
PAGE_INTENT_URI("pageIntent"),
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_INTENT_URI("entryIntent"),
ENTRY_HIERARCHY_PATH("entryPath"),
ENTRY_START_ADB("entryStartAdb"),
// Columns related to search
ENTRY_TITLE("entryTitle"),
ENTRY_SEARCH_KEYWORD("entrySearchKw"),
}
/**
* Enum to define all queries supported in the provider.
*/
enum class QueryEnum(
val queryPath: String,
val queryMatchCode: Int,
val columnNames: List<ColumnEnum>
) {
// For debug
PAGE_DEBUG_QUERY(
"page_debug", 1,
listOf(ColumnEnum.PAGE_START_ADB)
),
ENTRY_DEBUG_QUERY(
"entry_debug", 2,
listOf(ColumnEnum.ENTRY_START_ADB)
),
// page related queries.
PAGE_INFO_QUERY(
"page_info", 100,
listOf(
ColumnEnum.PAGE_ID,
ColumnEnum.PAGE_NAME,
ColumnEnum.PAGE_ROUTE,
ColumnEnum.PAGE_INTENT_URI,
ColumnEnum.PAGE_ENTRY_COUNT,
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_INTENT_URI,
)
),
// Search related queries
SEARCH_SITEMAP_QUERY(
"search_sitemap", 300,
listOf(
ColumnEnum.ENTRY_ID,
ColumnEnum.ENTRY_HIERARCHY_PATH,
ColumnEnum.ENTRY_INTENT_URI,
)
),
SEARCH_STATIC_DATA_QUERY(
"search_static", 301,
listOf(
ColumnEnum.ENTRY_ID,
ColumnEnum.ENTRY_TITLE,
ColumnEnum.ENTRY_SEARCH_KEYWORD,
)
),
SEARCH_DYNAMIC_DATA_QUERY(
"search_dynamic", 302,
listOf(
ColumnEnum.ENTRY_ID,
ColumnEnum.ENTRY_TITLE,
ColumnEnum.ENTRY_SEARCH_KEYWORD,
)
),
}
internal fun QueryEnum.getColumns(): Array<String> {
return columnNames.map { it.id }.toTypedArray()
}
internal fun QueryEnum.getIndex(name: ColumnEnum): Int {
return columnNames.indexOf(name)
}
internal fun QueryEnum.addUri(uriMatcher: UriMatcher, authority: String) {
uriMatcher.addURI(authority, queryPath, queryMatchCode)
}

View File

@@ -16,8 +16,13 @@
package com.android.settingslib.spa.framework.common
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.navigation.NamedNavArgument
import com.android.settingslib.spa.framework.BrowseActivity
import com.android.settingslib.spa.framework.util.isRuntimeParam
import com.android.settingslib.spa.framework.util.navLink
import com.android.settingslib.spa.framework.util.normalize
@@ -111,6 +116,41 @@ data class SettingsPage(
details = formatDisplayTitle()
)
}
fun createBrowseIntent(
context: Context?,
browseActivityClass: Class<out Activity>?,
entryId: String? = null
): Intent? {
if (!isBrowsable(context, browseActivityClass)) return null
return Intent().setComponent(ComponentName(context!!, browseActivityClass!!))
.apply {
putExtra(BrowseActivity.KEY_DESTINATION, buildRoute())
if (entryId != null) {
putExtra(BrowseActivity.KEY_HIGHLIGHT_ENTRY, entryId)
}
}
}
fun createBrowseAdbCommand(
context: Context?,
browseActivityClass: Class<out Activity>?,
entryId: String? = null
): String? {
if (!isBrowsable(context, browseActivityClass)) return null
val packageName = context!!.packageName
val activityName = browseActivityClass!!.name.replace(packageName, "")
val destinationParam = " -e ${BrowseActivity.KEY_DESTINATION} ${buildRoute()}"
val highlightParam =
if (entryId != null) " -e ${BrowseActivity.KEY_HIGHLIGHT_ENTRY} $entryId" else ""
return "adb shell am start -n $packageName/$activityName$destinationParam$highlightParam"
}
fun isBrowsable(context: Context?, browseActivityClass: Class<out Activity>?): Boolean {
return context != null &&
browseActivityClass != null &&
!hasRuntimeParam()
}
}
fun String.toHashId(): String {

View File

@@ -14,12 +14,9 @@
* limitations under the License.
*/
package com.android.settingslib.spa.framework
package com.android.settingslib.spa.framework.debug
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text
@@ -33,8 +30,6 @@ 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.BrowseActivity.Companion.KEY_DESTINATION
import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_HIGHLIGHT_ENTRY
import com.android.settingslib.spa.framework.common.LogCategory
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsPage
@@ -60,11 +55,10 @@ private const val PARAM_NAME_ENTRY_ID = "eid"
/**
* The Debug Activity to display all Spa Pages & Entries.
* One can open the debug activity by:
* $ adb shell am start -n <Activity>
* For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
* For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
* $ adb shell am start -n <Package>/com.android.settingslib.spa.framework.debug.DebugActivity
* For gallery, Package = com.android.settingslib.spa.gallery
*/
open class DebugActivity : ComponentActivity() {
class DebugActivity : ComponentActivity() {
private val spaEnvironment get() = SpaEnvironmentFactory.instance
override fun onCreate(savedInstanceState: Bundle?) {
@@ -79,30 +73,6 @@ open class DebugActivity : ComponentActivity() {
}
}
private fun displayDebugMessage() {
val entryProviderAuthorities = spaEnvironment.entryProviderAuthorities ?: return
try {
val query = EntryProvider.QueryEnum.PAGE_INFO_QUERY
contentResolver.query(
Uri.parse("content://$entryProviderAuthorities/${query.queryPath}"),
null, null, null
).use { cursor ->
while (cursor != null && cursor.moveToNext()) {
val route = cursor.getString(query, EntryProvider.ColumnEnum.PAGE_ROUTE)
val entryCount = cursor.getInt(query, EntryProvider.ColumnEnum.PAGE_ENTRY_COUNT)
val hasRuntimeParam =
cursor.getBoolean(query, EntryProvider.ColumnEnum.HAS_RUNTIME_PARAM)
val message = "Page Info: $route ($entryCount) " +
(if (hasRuntimeParam) "with" else "no") + "-runtime-params"
spaEnvironment.logger.message(TAG, message, category = LogCategory.FRAMEWORK)
}
}
} catch (e: Exception) {
Log.e(TAG, "Provider querying exception:", e)
}
}
@Composable
private fun MainContent() {
val navController = rememberNavController()
@@ -141,11 +111,6 @@ open class DebugActivity : ComponentActivity() {
override val title = "List All Entries (${allEntry.size})"
override val onClick = navigator(route = ROUTE_All_ENTRIES)
})
Preference(object : PreferenceModel {
override val title = "Query EntryProvider"
override val enabled = isEntryProviderAvailable().toState()
override val onClick = { displayDebugMessage() }
})
}
}
@@ -177,6 +142,7 @@ open class DebugActivity : ComponentActivity() {
@Composable
fun OnePage(arguments: Bundle?) {
val context = LocalContext.current
val entryRepository by spaEnvironment.entryRepository
val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "")
val pageWithEntry = entryRepository.getPageWithEntry(id)!!
@@ -186,7 +152,9 @@ open class DebugActivity : ComponentActivity() {
Text(text = "Entry size: ${pageWithEntry.entries.size}")
Preference(model = object : PreferenceModel {
override val title = "open page"
override val enabled = isPageClickable(pageWithEntry.page).toState()
override val enabled =
pageWithEntry.page.isBrowsable(context, spaEnvironment.browseActivityClass)
.toState()
override val onClick = openPage(pageWithEntry.page)
})
EntryList(pageWithEntry.entries)
@@ -195,6 +163,7 @@ open class DebugActivity : ComponentActivity() {
@Composable
fun OneEntry(arguments: Bundle?) {
val context = LocalContext.current
val entryRepository by spaEnvironment.entryRepository
val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
val entry = entryRepository.getEntry(id)!!
@@ -202,7 +171,9 @@ open class DebugActivity : ComponentActivity() {
RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"
override val enabled = isEntryClickable(entry).toState()
override val enabled =
entry.containerPage().isBrowsable(context, spaEnvironment.browseActivityClass)
.toState()
override val onClick = openEntry(entry)
})
Text(text = entryContent)
@@ -223,12 +194,10 @@ open class DebugActivity : ComponentActivity() {
@Composable
private fun openPage(page: SettingsPage): (() -> Unit)? {
if (!isPageClickable(page)) return null
val context = LocalContext.current
val intent =
page.createBrowseIntent(context, spaEnvironment.browseActivityClass) ?: return null
val route = page.buildRoute()
val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
spaEnvironment.logger.message(
TAG, "OpenPage: $route", category = LogCategory.FRAMEWORK
@@ -239,13 +208,11 @@ open class DebugActivity : ComponentActivity() {
@Composable
private fun openEntry(entry: SettingsEntry): (() -> Unit)? {
if (!isEntryClickable(entry)) return null
val context = LocalContext.current
val intent = entry.containerPage()
.createBrowseIntent(context, spaEnvironment.browseActivityClass, entry.id)
?: return null
val route = entry.containerPage().buildRoute()
val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
putExtra(KEY_HIGHLIGHT_ENTRY, entry.id)
}
return {
spaEnvironment.logger.message(
TAG, "OpenEntry: $route", category = LogCategory.FRAMEWORK
@@ -253,17 +220,9 @@ open class DebugActivity : ComponentActivity() {
context.startActivity(intent)
}
}
private fun isEntryProviderAvailable(): Boolean {
return spaEnvironment.entryProviderAuthorities != null
}
private fun isPageClickable(page: SettingsPage): Boolean {
return spaEnvironment.browseActivityClass != null && !page.hasRuntimeParam()
}
private fun isEntryClickable(entry: SettingsEntry): Boolean {
return spaEnvironment.browseActivityClass != null &&
!entry.containerPage().hasRuntimeParam()
}
}
/**
* A blank activity without any page.
*/
class BlankActivity : ComponentActivity()

View File

@@ -0,0 +1,176 @@
/*
* 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 android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.content.Intent.URI_INTENT_SCHEME
import android.content.UriMatcher
import android.content.pm.ProviderInfo
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.util.Log
import com.android.settingslib.spa.framework.common.ColumnEnum
import com.android.settingslib.spa.framework.common.QueryEnum
import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory
import com.android.settingslib.spa.framework.common.addUri
import com.android.settingslib.spa.framework.common.getColumns
private const val TAG = "DebugProvider"
/**
* The content provider to return debug data.
* One can query the provider result by:
* $ adb shell content query --uri content://<AuthorityPath>/<QueryPath>
* For gallery, AuthorityPath = com.android.spa.gallery.debug
* Some examples:
* $ adb shell content query --uri content://<AuthorityPath>/page_debug
* $ adb shell content query --uri content://<AuthorityPath>/entry_debug
* $ adb shell content query --uri content://<AuthorityPath>/page_info
* $ adb shell content query --uri content://<AuthorityPath>/entry_info
*/
class DebugProvider : ContentProvider() {
private val spaEnvironment get() = SpaEnvironmentFactory.instance
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
TODO("Implement this to handle requests to delete one or more rows")
}
override fun getType(uri: Uri): String? {
TODO(
"Implement this to handle requests for the MIME type of the data" +
"at the given URI"
)
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
TODO("Implement this to handle requests to insert a new row.")
}
override fun update(
uri: Uri,
values: ContentValues?,
selection: String?,
selectionArgs: Array<String>?
): Int {
TODO("Implement this to handle requests to update one or more rows.")
}
override fun onCreate(): Boolean {
Log.d(TAG, "onCreate")
return true
}
override fun attachInfo(context: Context?, info: ProviderInfo?) {
if (info != null) {
QueryEnum.PAGE_DEBUG_QUERY.addUri(uriMatcher, info.authority)
QueryEnum.ENTRY_DEBUG_QUERY.addUri(uriMatcher, info.authority)
QueryEnum.PAGE_INFO_QUERY.addUri(uriMatcher, info.authority)
QueryEnum.ENTRY_INFO_QUERY.addUri(uriMatcher, info.authority)
}
super.attachInfo(context, info)
}
override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?
): Cursor? {
return try {
when (uriMatcher.match(uri)) {
QueryEnum.PAGE_DEBUG_QUERY.queryMatchCode -> queryPageDebug()
QueryEnum.ENTRY_DEBUG_QUERY.queryMatchCode -> queryEntryDebug()
QueryEnum.PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo()
QueryEnum.ENTRY_INFO_QUERY.queryMatchCode -> queryEntryInfo()
else -> throw UnsupportedOperationException("Unknown Uri $uri")
}
} catch (e: UnsupportedOperationException) {
throw e
} catch (e: Exception) {
Log.e(TAG, "Provider querying exception:", e)
null
}
}
private fun queryPageDebug(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val command = pageWithEntry.page.createBrowseAdbCommand(
context,
spaEnvironment.browseActivityClass
)
if (command != null) {
cursor.newRow().add(ColumnEnum.PAGE_START_ADB.id, command)
}
}
return cursor
}
private fun queryEntryDebug(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.ENTRY_DEBUG_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
val command = entry.containerPage()
.createBrowseAdbCommand(context, spaEnvironment.browseActivityClass, entry.id)
if (command != null) {
cursor.newRow().add(ColumnEnum.ENTRY_START_ADB.id, command)
}
}
return cursor
}
private fun queryPageInfo(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
val page = pageWithEntry.page
val intent =
page.createBrowseIntent(context, spaEnvironment.browseActivityClass) ?: Intent()
cursor.newRow()
.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)
.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
}
private fun queryEntryInfo(): Cursor {
val entryRepository by spaEnvironment.entryRepository
val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
for (entry in entryRepository.getAllEntries()) {
val intent = entry.containerPage()
.createBrowseIntent(context, spaEnvironment.browseActivityClass, entry.id)
?: Intent()
cursor.newRow()
.add(ColumnEnum.ENTRY_ID.id, entry.id)
.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))
}
return cursor
}
}