xref: /aosp_15_r20/frameworks/base/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/debug/DebugActivity.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
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