Merge "Improve runtime parameter support. 1. Define runtime parameter as prefixed by "rt_" 2. Move entry highlight parameter into intent as Extra, instead of a runtime parameter."

This commit is contained in:
Zekan Qian
2022-10-10 02:09:30 +00:00
committed by Android (Google) Code Review
12 changed files with 85 additions and 122 deletions

View File

@@ -18,14 +18,11 @@ package com.android.settingslib.spa.gallery.home
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.getRuntimeArguments
import com.android.settingslib.spa.framework.util.mergeArguments
import com.android.settingslib.spa.gallery.R
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
@@ -61,20 +58,12 @@ object HomePageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
val globalRuntimeArgs = remember { getRuntimeArguments(arguments) }
HomeScaffold(title = stringResource(R.string.app_name)) {
for (entry in buildEntry(arguments)) {
if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
entry.UiLayout(
mergeArguments(
listOf(
globalRuntimeArgs,
ArgumentPageModel.buildArgument(intParam = 0)
)
)
)
entry.UiLayout(ArgumentPageModel.buildArgument(intParam = 0))
} else {
entry.UiLayout(globalRuntimeArgs)
entry.UiLayout()
}
}
}

View File

@@ -18,15 +18,12 @@ package com.android.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.getRuntimeArguments
import com.android.settingslib.spa.framework.util.mergeArguments
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.createSettingsPage
import com.android.settingslib.spa.widget.preference.Preference
@@ -101,20 +98,12 @@ object ArgumentPageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
val globalRuntimeArgs = remember { getRuntimeArguments(arguments) }
RegularScaffold(title = ArgumentPageModel.create(arguments).genPageTitle()) {
for (entry in buildEntry(arguments)) {
if (entry.toPage != null) {
entry.UiLayout(
mergeArguments(
listOf(
globalRuntimeArgs,
ArgumentPageModel.buildNextArgument(arguments)
)
)
)
entry.UiLayout(ArgumentPageModel.buildNextArgument(arguments))
} else {
entry.UiLayout(globalRuntimeArgs)
entry.UiLayout()
}
}
}

View File

@@ -38,7 +38,7 @@ private const val PAGE_TITLE = "Sample page with arguments"
private const val STRING_PARAM_TITLE = "String param value"
private const val INT_PARAM_TITLE = "Int param value"
private const val STRING_PARAM_NAME = "stringParam"
private const val INT_PARAM_NAME = "intParam"
private const val INT_PARAM_NAME = "rt_intParam"
private val ARGUMENT_PAGE_KEYWORDS = listOf("argument keyword1", "argument keyword2")
class ArgumentPageModel : PageModel() {

View File

@@ -30,7 +30,6 @@ import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.getRuntimeArguments
import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.gallery.createSettingsPage
import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_TITLE
@@ -178,10 +177,9 @@ object PreferencePageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
val globalRuntimeArgs = remember { getRuntimeArguments(arguments) }
RegularScaffold(title = PAGE_TITLE) {
for (entry in buildEntry(arguments)) {
entry.UiLayout(globalRuntimeArgs)
entry.UiLayout()
}
}
}

View File

@@ -25,13 +25,13 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.navigation.NavGraph.Companion.findStartDestination
import androidx.navigation.NavHostController
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.SpaEnvironment
import com.android.settingslib.spa.framework.compose.LocalNavController
import com.android.settingslib.spa.framework.compose.NavControllerWrapperImpl
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.navRoute
@@ -70,31 +70,29 @@ open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity()
composable(NULL_PAGE_NAME) {}
for (page in sppRepository.getAllProviders()) {
composable(
route = page.name + page.parameter.navRoute() +
"?$HIGHLIGHT_ENTRY_PARAM_NAME={$HIGHLIGHT_ENTRY_PARAM_NAME}",
arguments = page.parameter + listOf(
// add optional parameters
navArgument(HIGHLIGHT_ENTRY_PARAM_NAME) { defaultValue = "null" }
),
) { navBackStackEntry ->
page.Page(navBackStackEntry.arguments)
route = page.name + page.parameter.navRoute(),
arguments = page.parameter,
) { navBackStackEntry -> page.Page(navBackStackEntry.arguments)
}
}
}
InitialDestinationNavigator()
}
InitialDestinationNavigator(navController)
}
@Composable
private fun InitialDestinationNavigator(navController: NavHostController) {
private fun InitialDestinationNavigator() {
val destinationNavigated = rememberSaveable { mutableStateOf(false) }
if (destinationNavigated.value) return
destinationNavigated.value = true
val controller = LocalNavController.current as NavControllerWrapperImpl
LaunchedEffect(Unit) {
val destination =
intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPage()
val highlightEntryId = intent?.getStringExtra(KEY_HIGHLIGHT_ENTRY)
if (destination.isNotEmpty()) {
controller.highlightId = highlightEntryId
val navController = controller.navController
navController.navigate(destination) {
popUpTo(navController.graph.findStartDestination().id) {
inclusive = true
@@ -106,6 +104,6 @@ open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity()
companion object {
const val KEY_DESTINATION = "spaActivityDestination"
const val HIGHLIGHT_ENTRY_PARAM_NAME = "highlightEntry"
const val KEY_HIGHLIGHT_ENTRY = "highlightEntry"
}
}

View File

@@ -34,6 +34,7 @@ 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.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SpaEnvironment
@@ -190,7 +191,7 @@ open class DebugActivity(private val spaEnvironment: SpaEnvironment) : Component
RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"
override val enabled = (!entry.hasRuntimeParam()).toState()
override val enabled = (!entry.containerPage().hasRuntimeParam()).toState()
override val onClick = openEntry(entry)
})
Text(text = entryContent)
@@ -225,11 +226,12 @@ open class DebugActivity(private val spaEnvironment: SpaEnvironment) : Component
@Composable
private fun openEntry(entry: SettingsEntry): (() -> Unit)? {
if (entry.hasRuntimeParam()) return null
if (entry.containerPage().hasRuntimeParam()) return null
val context = LocalContext.current
val route = entry.buildRoute()
val route = entry.containerPage().buildRoute()
val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
putExtra(KEY_HIGHLIGHT_ENTRY, entry.id)
}
return {
Log.d("DEBUG ACTIVITY", "Open entry: $route")

View File

@@ -208,7 +208,7 @@ open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
cursor.newRow()
.add(ColumnEnum.ENTRY_ID.id, entry.id)
.add(ColumnEnum.ENTRY_NAME.id, entry.displayName)
.add(ColumnEnum.ENTRY_ROUTE.id, entry.buildRoute())
.add(ColumnEnum.ENTRY_ROUTE.id, entry.containerPage().buildRoute())
.add(ColumnEnum.ENTRY_TITLE.id, searchData?.title ?: "")
.add(
ColumnEnum.ENTRY_SEARCH_KEYWORD.id,

View File

@@ -23,7 +23,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.platform.LocalContext
import com.android.settingslib.spa.framework.BrowseActivity.Companion.HIGHLIGHT_ENTRY_PARAM_NAME
import com.android.settingslib.spa.framework.compose.LocalNavController
const val INJECT_ENTRY_NAME = "INJECT"
const val ROOT_ENTRY_NAME = "ROOT"
@@ -90,20 +90,12 @@ data class SettingsEntry(
return "${owner.displayName}:$displayName"
}
private fun containerPage(): SettingsPage {
fun containerPage(): SettingsPage {
// The Container page of the entry, which is the from-page or
// the owner-page if from-page is unset.
return fromPage ?: owner
}
fun buildRoute(): String {
return containerPage().buildRoute(id)
}
fun hasRuntimeParam(): Boolean {
return containerPage().hasRuntimeParam()
}
private fun fullArgument(runtimeArguments: Bundle? = null): Bundle {
val arguments = Bundle()
if (owner.arguments != null) arguments.putAll(owner.arguments)
@@ -119,8 +111,9 @@ data class SettingsEntry(
@Composable
fun UiLayout(runtimeArguments: Bundle? = null) {
val context = LocalContext.current
val controller = LocalNavController.current
val highlight = rememberSaveable {
mutableStateOf(runtimeArguments?.getString(HIGHLIGHT_ENTRY_PARAM_NAME) == id)
mutableStateOf(controller.highlightEntryId == id)
}
if (highlight.value) {
highlight.value = false

View File

@@ -18,9 +18,9 @@ package com.android.settingslib.spa.framework.common
import android.os.Bundle
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
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
/**
* Defines data to identify a Settings page.
@@ -74,57 +74,25 @@ data class SettingsPage(
}
fun formatArguments(): String {
val normalizedArguments = parameter.normalize(arguments)
if (normalizedArguments == null || normalizedArguments.isEmpty) return "[No arguments]"
return normalizedArguments.toString().removeRange(0, 6)
val normArguments = parameter.normalize(arguments)
if (normArguments == null || normArguments.isEmpty) return "[No arguments]"
return normArguments.toString().removeRange(0, 6)
}
fun formatDisplayTitle(): String {
return "$displayName ${formatArguments()}"
}
fun buildRoute(highlightEntryId: String? = null): String {
val highlightParam =
if (highlightEntryId == null)
""
else
"?${BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME}=$highlightEntryId"
return name + parameter.navLink(arguments) + highlightParam
fun buildRoute(): String {
return name + parameter.navLink(arguments)
}
fun hasRuntimeParam(): Boolean {
return parameter.hasRuntimeParam(arguments)
}
}
private fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
if (this.isEmpty()) return null
val normArgs = Bundle()
for (navArg in this) {
when (navArg.argument.type) {
NavType.StringType -> {
val value = arguments?.getString(navArg.name)
if (value != null)
normArgs.putString(navArg.name, value)
else
normArgs.putString("unset_" + navArg.name, null)
}
NavType.IntType -> {
if (arguments != null && arguments.containsKey(navArg.name))
normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
else
normArgs.putString("unset_" + navArg.name, null)
}
for (navArg in parameter) {
if (navArg.isRuntimeParam()) return true
}
return false
}
return normArgs
}
private fun List<NamedNavArgument>.hasRuntimeParam(arguments: Bundle? = null): Boolean {
for (navArg in this) {
if (arguments == null || !arguments.containsKey(navArg.name)) return true
}
return false
}
fun String.toHashId(): String {

View File

@@ -27,6 +27,9 @@ import androidx.navigation.NavHostController
interface NavControllerWrapper {
fun navigate(route: String)
fun navigateBack()
val highlightEntryId: String?
get() = null
}
@Composable
@@ -56,9 +59,11 @@ fun navigator(route: String?): () -> Unit {
}
internal class NavControllerWrapperImpl(
private val navController: NavHostController,
val navController: NavHostController,
private val onBackPressedDispatcher: OnBackPressedDispatcher?,
) : NavControllerWrapper {
var highlightId: String? = null
override fun navigate(route: String) {
navController.navigate(route)
}
@@ -66,4 +71,7 @@ internal class NavControllerWrapperImpl(
override fun navigateBack() {
onBackPressedDispatcher?.onBackPressed()
}
override val highlightEntryId: String?
get() = highlightId
}

View File

@@ -19,7 +19,10 @@ package com.android.settingslib.spa.framework.util
import android.os.Bundle
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
import com.android.settingslib.spa.framework.BrowseActivity
const val RUNTIME_PARAM_PREFIX = "rt_"
const val UNSET_PARAM_PREFIX = "unset_"
const val UNSET_PARAM_VALUE = "[unset]"
fun List<NamedNavArgument>.navRoute(): String {
return this.joinToString("") { argument -> "/{${argument.name}}" }
@@ -29,7 +32,7 @@ fun List<NamedNavArgument>.navLink(arguments: Bundle? = null): String {
val argsArray = mutableListOf<String>()
for (navArg in this) {
if (arguments == null || !arguments.containsKey(navArg.name)) {
argsArray.add("[rt]")
argsArray.add(UNSET_PARAM_VALUE)
continue
}
when (navArg.argument.type) {
@@ -44,6 +47,35 @@ fun List<NamedNavArgument>.navLink(arguments: Bundle? = null): String {
return argsArray.joinToString("") { arg -> "/$arg" }
}
fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
if (this.isEmpty()) return null
val normArgs = Bundle()
for (navArg in this) {
// Erase value of runtime parameters.
if (navArg.isRuntimeParam()) {
normArgs.putString(navArg.name, null)
continue
}
when (navArg.argument.type) {
NavType.StringType -> {
val value = arguments?.getString(navArg.name)
if (value != null)
normArgs.putString(navArg.name, value)
else
normArgs.putString(UNSET_PARAM_PREFIX + navArg.name, null)
}
NavType.IntType -> {
if (arguments != null && arguments.containsKey(navArg.name))
normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
else
normArgs.putString(UNSET_PARAM_PREFIX + navArg.name, null)
}
}
}
return normArgs
}
fun List<NamedNavArgument>.getStringArg(name: String, arguments: Bundle? = null): String? {
if (this.containsStringArg(name) && arguments != null) {
return arguments.getString(name)
@@ -72,20 +104,6 @@ fun List<NamedNavArgument>.containsIntArg(name: String): Boolean {
return false
}
fun getRuntimeArguments(arguments: Bundle? = null): Bundle {
val res = Bundle()
val highlightEntry = arguments?.getString(BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME)
if (highlightEntry != null) {
res.putString(BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME, highlightEntry)
}
// Append more general runtime arguments here
return res
}
fun mergeArguments(argsList: List<Bundle?>): Bundle {
val res = Bundle()
for (args in argsList) {
if (args != null) res.putAll(args)
}
return res
fun NamedNavArgument.isRuntimeParam(): Boolean {
return this.name.startsWith(RUNTIME_PARAM_PREFIX)
}

View File

@@ -46,8 +46,8 @@ import kotlinx.coroutines.Dispatchers
private const val ENTRY_NAME = "AllowControl"
private const val PERMISSION = "permission"
private const val PACKAGE_NAME = "packageName"
private const val USER_ID = "userId"
private const val PACKAGE_NAME = "rt_packageName"
private const val USER_ID = "rt_userId"
private const val PAGE_NAME = "TogglePermissionAppInfoPage"
private val PAGE_PARAMETER = listOf(
navArgument(PERMISSION) { type = NavType.StringType },