Move debug related activity & provider to debug folder.

Add debug related activity & provider in Gallery.

Bug: 244122804
Test: manual - build gallery
Change-Id: Ie1b005b26eca5b7dd6472ebf6841531c2bc7a071
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index 2ed8bca..f1a24af 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -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>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
deleted file mode 100644
index 23072a2..0000000
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ /dev/null
@@ -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()
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 532f63b..d631708 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
@@ -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 @@
  * 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 @@
 
     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 @@
     ): 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 @@
         }
     }
 
-    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 @@
                 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
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/ProviderColumn.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/ProviderColumn.kt
new file mode 100644
index 0000000..0707429
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/ProviderColumn.kt
@@ -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)
+}
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 8f63c47..07df96e 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
@@ -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 @@
             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 {
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/debug/DebugActivity.kt
similarity index 73%
rename from packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
rename to packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugActivity.kt
index 6f96818..3015080 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/debug/DebugActivity.kt
@@ -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.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 @@
 /**
  * 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 @@
         }
     }
 
-    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 @@
                 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 @@
 
     @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 @@
             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 @@
 
     @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 @@
         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 @@
 
     @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 @@
 
     @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 @@
             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()
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
new file mode 100644
index 0000000..6c27109
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/debug/DebugProvider.kt
@@ -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
+    }
+}