1 /* <lambda>null2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settingslib.spa.debug 18 19 import android.os.Bundle 20 import androidx.activity.ComponentActivity 21 import androidx.activity.compose.setContent 22 import androidx.compose.material3.Text 23 import androidx.compose.runtime.Composable 24 import androidx.compose.runtime.CompositionLocalProvider 25 import androidx.compose.runtime.remember 26 import androidx.compose.ui.platform.LocalContext 27 import androidx.navigation.NavType 28 import androidx.navigation.compose.NavHost 29 import androidx.navigation.compose.composable 30 import androidx.navigation.compose.rememberNavController 31 import androidx.navigation.navArgument 32 import com.android.settingslib.spa.R 33 import com.android.settingslib.spa.framework.common.LogCategory 34 import com.android.settingslib.spa.framework.common.SettingsEntry 35 import com.android.settingslib.spa.framework.common.SettingsPage 36 import com.android.settingslib.spa.framework.common.SpaEnvironmentFactory 37 import com.android.settingslib.spa.framework.compose.localNavController 38 import com.android.settingslib.spa.framework.compose.navigator 39 import com.android.settingslib.spa.framework.theme.SettingsTheme 40 import com.android.settingslib.spa.framework.util.SESSION_BROWSE 41 import com.android.settingslib.spa.framework.util.SESSION_SEARCH 42 import com.android.settingslib.spa.framework.util.createIntent 43 import com.android.settingslib.spa.widget.preference.Preference 44 import com.android.settingslib.spa.widget.preference.PreferenceModel 45 import com.android.settingslib.spa.widget.scaffold.HomeScaffold 46 import com.android.settingslib.spa.widget.scaffold.RegularScaffold 47 48 private const val TAG = "DebugActivity" 49 private const val ROUTE_ROOT = "root" 50 private const val ROUTE_All_PAGES = "pages" 51 private const val ROUTE_All_ENTRIES = "entries" 52 private const val ROUTE_PAGE = "page" 53 private const val ROUTE_ENTRY = "entry" 54 private const val PARAM_NAME_PAGE_ID = "pid" 55 private const val PARAM_NAME_ENTRY_ID = "eid" 56 57 /** 58 * The Debug Activity to display all Spa Pages & Entries. 59 * One can open the debug activity by: 60 * $ adb shell am start -n <Package>/com.android.settingslib.spa.debug.DebugActivity 61 * For gallery, Package = com.android.settingslib.spa.gallery 62 */ 63 class DebugActivity : ComponentActivity() { 64 private val spaEnvironment get() = SpaEnvironmentFactory.instance 65 66 override fun onCreate(savedInstanceState: Bundle?) { 67 setTheme(R.style.Theme_SpaLib) 68 super.onCreate(savedInstanceState) 69 spaEnvironment.logger.message(TAG, "onCreate", category = LogCategory.FRAMEWORK) 70 71 setContent { 72 SettingsTheme { 73 MainContent() 74 } 75 } 76 } 77 78 @Composable 79 private fun MainContent() { 80 val navController = rememberNavController() 81 CompositionLocalProvider(navController.localNavController()) { 82 NavHost(navController, ROUTE_ROOT) { 83 composable(route = ROUTE_ROOT) { RootPage() } 84 composable(route = ROUTE_All_PAGES) { AllPages() } 85 composable(route = ROUTE_All_ENTRIES) { AllEntries() } 86 composable( 87 route = "$ROUTE_PAGE/{$PARAM_NAME_PAGE_ID}", 88 arguments = listOf( 89 navArgument(PARAM_NAME_PAGE_ID) { type = NavType.StringType }, 90 ) 91 ) { navBackStackEntry -> OnePage(navBackStackEntry.arguments) } 92 composable( 93 route = "$ROUTE_ENTRY/{$PARAM_NAME_ENTRY_ID}", 94 arguments = listOf( 95 navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.StringType }, 96 ) 97 ) { navBackStackEntry -> OneEntry(navBackStackEntry.arguments) } 98 } 99 } 100 } 101 102 @Composable 103 fun RootPage() { 104 val entryRepository by spaEnvironment.entryRepository 105 val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() } 106 val allEntry = remember { entryRepository.getAllEntries() } 107 HomeScaffold(title = "Settings Debug") { 108 Preference(object : PreferenceModel { 109 override val title = "List All Pages (${allPageWithEntry.size})" 110 override val onClick = navigator(route = ROUTE_All_PAGES) 111 }) 112 Preference(object : PreferenceModel { 113 override val title = "List All Entries (${allEntry.size})" 114 override val onClick = navigator(route = ROUTE_All_ENTRIES) 115 }) 116 } 117 } 118 119 @Composable 120 fun AllPages() { 121 val entryRepository by spaEnvironment.entryRepository 122 val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() } 123 RegularScaffold(title = "All Pages (${allPageWithEntry.size})") { 124 for (pageWithEntry in allPageWithEntry) { 125 val page = pageWithEntry.page 126 Preference(object : PreferenceModel { 127 override val title = "${page.debugBrief()} (${pageWithEntry.entries.size})" 128 override val summary = { page.debugArguments() } 129 override val onClick = navigator(route = ROUTE_PAGE + "/${page.id}") 130 }) 131 } 132 } 133 } 134 135 @Composable 136 fun AllEntries() { 137 val entryRepository by spaEnvironment.entryRepository 138 val allEntry = remember { entryRepository.getAllEntries() } 139 RegularScaffold(title = "All Entries (${allEntry.size})") { 140 EntryList(allEntry) 141 } 142 } 143 144 145 @Composable 146 fun OnePage(arguments: Bundle?) { 147 val entryRepository by spaEnvironment.entryRepository 148 val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "") 149 val pageWithEntry = entryRepository.getPageWithEntry(id)!! 150 val page = pageWithEntry.page 151 RegularScaffold(title = "Page - ${page.debugBrief()}") { 152 Text(text = "id = ${page.id}") 153 Text(text = page.debugArguments()) 154 Text(text = "enabled = ${page.isEnabled()}") 155 Text(text = "Entry size: ${pageWithEntry.entries.size}") 156 Preference(model = object : PreferenceModel { 157 override val title = "open page" 158 override val enabled = { 159 spaEnvironment.browseActivityClass != null && page.isBrowsable() 160 } 161 override val onClick = openPage(page) 162 }) 163 EntryList(pageWithEntry.entries) 164 } 165 } 166 167 @Composable 168 fun OneEntry(arguments: Bundle?) { 169 val entryRepository by spaEnvironment.entryRepository 170 val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "") 171 val entry = entryRepository.getEntry(id)!! 172 val entryContent = remember { entry.debugContent(entryRepository) } 173 RegularScaffold(title = "Entry - ${entry.debugBrief()}") { 174 Preference(model = object : PreferenceModel { 175 override val title = "open entry" 176 override val enabled = { 177 spaEnvironment.browseActivityClass != null && 178 entry.containerPage().isBrowsable() 179 } 180 override val onClick = openEntry(entry) 181 }) 182 Text(text = entryContent) 183 } 184 } 185 186 @Composable 187 private fun EntryList(entries: Collection<SettingsEntry>) { 188 for (entry in entries) { 189 Preference(object : PreferenceModel { 190 override val title = entry.debugBrief() 191 override val summary = { 192 "${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}" 193 } 194 override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id}") 195 }) 196 } 197 } 198 199 @Composable 200 private fun openPage(page: SettingsPage): (() -> Unit)? { 201 val context = LocalContext.current 202 val intent = 203 page.createIntent(SESSION_BROWSE) ?: return null 204 val route = page.buildRoute() 205 return { 206 spaEnvironment.logger.message( 207 TAG, "OpenPage: $route", category = LogCategory.FRAMEWORK 208 ) 209 context.startActivity(intent) 210 } 211 } 212 213 @Composable 214 private fun openEntry(entry: SettingsEntry): (() -> Unit)? { 215 val context = LocalContext.current 216 val intent = entry.createIntent(SESSION_SEARCH) 217 ?: return null 218 val route = entry.containerPage().buildRoute() 219 return { 220 spaEnvironment.logger.message( 221 TAG, "OpenEntry: $route", category = LogCategory.FRAMEWORK 222 ) 223 context.startActivity(intent) 224 } 225 } 226 } 227 228 /** 229 * A blank activity without any page. 230 */ 231 class BlankActivity : ComponentActivity() 232