diff options
3 files changed, 176 insertions, 102 deletions
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt index 088bef230374..7aece5185800 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt @@ -42,7 +42,7 @@ abstract class GetPreferenceGraphApiHandler( callingUid: Int, request: GetPreferenceGraphRequest, ): PreferenceGraphProto { - val builder = PreferenceGraphBuilder.of(application, request) + val builder = PreferenceGraphBuilder.of(application, myUid, callingUid, request) if (request.screenKeys.isEmpty()) { for (key in PreferenceScreenRegistry.preferenceScreens.keys) { builder.addPreferenceScreenFromRegistry(key) @@ -68,16 +68,18 @@ constructor( val screenKeys: Set<String> = setOf(), val visitedScreens: Set<String> = setOf(), val locale: Locale? = null, - val includeValue: Boolean = true, + val flags: Int = PreferenceGetterFlags.ALL, + val includeValue: Boolean = true, // TODO: clean up val includeValueDescriptor: Boolean = true, ) object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> { override fun encode(data: GetPreferenceGraphRequest): Bundle = - Bundle(3).apply { + Bundle(4).apply { putStringArray(KEY_SCREEN_KEYS, data.screenKeys.toTypedArray()) putStringArray(KEY_VISITED_KEYS, data.visitedScreens.toTypedArray()) putString(KEY_LOCALE, data.locale?.toLanguageTag()) + putInt(KEY_FLAGS, data.flags) } override fun decode(data: Bundle): GetPreferenceGraphRequest { @@ -88,12 +90,14 @@ object GetPreferenceGraphRequestCodec : MessageCodec<GetPreferenceGraphRequest> screenKeys.toSet(), visitedScreens.toSet(), data.getString(KEY_LOCALE).toLocale(), + data.getInt(KEY_FLAGS), ) } private const val KEY_SCREEN_KEYS = "k" private const val KEY_VISITED_KEYS = "v" private const val KEY_LOCALE = "l" + private const val KEY_FLAGS = "f" } object PreferenceGraphProtoCodec : MessageCodec<PreferenceGraphProto> { diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt new file mode 100644 index 000000000000..632f154b72ab --- /dev/null +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterFlags.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2024 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.graph + +/** Flags for preference getter operation. */ +object PreferenceGetterFlags { + const val VALUE = 1 shl 0 + const val VALUE_DESCRIPTOR = 1 shl 1 + const val METADATA = 1 shl 2 + const val ALL = (1 shl 3) - 1 + + fun Int.includeValue() = (this and VALUE) != 0 + + fun Int.includeValueDescriptor() = (this and VALUE_DESCRIPTOR) != 0 + + fun Int.includeMetadata() = (this and METADATA) != 0 +} diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt index 6760e72be4a6..a65d24cf3d6f 100644 --- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt +++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt @@ -31,6 +31,9 @@ import androidx.preference.Preference import androidx.preference.PreferenceGroup import androidx.preference.PreferenceScreen import androidx.preference.TwoStatePreference +import com.android.settingslib.graph.PreferenceGetterFlags.includeMetadata +import com.android.settingslib.graph.PreferenceGetterFlags.includeValue +import com.android.settingslib.graph.PreferenceGetterFlags.includeValueDescriptor import com.android.settingslib.graph.proto.PreferenceGraphProto import com.android.settingslib.graph.proto.PreferenceGroupProto import com.android.settingslib.graph.proto.PreferenceProto @@ -41,7 +44,6 @@ import com.android.settingslib.metadata.BooleanValue import com.android.settingslib.metadata.PersistentPreference import com.android.settingslib.metadata.PreferenceAvailabilityProvider import com.android.settingslib.metadata.PreferenceHierarchy -import com.android.settingslib.metadata.PreferenceHierarchyNode import com.android.settingslib.metadata.PreferenceMetadata import com.android.settingslib.metadata.PreferenceRestrictionProvider import com.android.settingslib.metadata.PreferenceScreenBindingKeyProvider @@ -50,6 +52,7 @@ import com.android.settingslib.metadata.PreferenceScreenRegistry import com.android.settingslib.metadata.PreferenceSummaryProvider import com.android.settingslib.metadata.PreferenceTitleProvider import com.android.settingslib.metadata.RangeValue +import com.android.settingslib.metadata.ReadWritePermit import com.android.settingslib.preference.PreferenceScreenFactory import com.android.settingslib.preference.PreferenceScreenProvider import java.util.Locale @@ -60,14 +63,17 @@ private const val TAG = "PreferenceGraphBuilder" /** Builder of preference graph. */ class PreferenceGraphBuilder -private constructor(private val context: Context, private val request: GetPreferenceGraphRequest) { +private constructor( + private val context: Context, + private val myUid: Int, + private val callingUid: Int, + private val request: GetPreferenceGraphRequest, +) { private val preferenceScreenFactory by lazy { PreferenceScreenFactory(context.ofLocale(request.locale)) } private val builder by lazy { PreferenceGraphProto.newBuilder() } private val visitedScreens = mutableSetOf<String>().apply { addAll(request.visitedScreens) } - private val includeValue = request.includeValue - private val includeValueDescriptor = request.includeValueDescriptor private suspend fun init() { for (key in request.screenKeys) { @@ -229,7 +235,7 @@ private constructor(private val context: Context, private val request: GetPrefer enabled = isEnabled available = isVisible persistent = isPersistent - if (includeValue && isPersistent && this@toProto is TwoStatePreference) { + if (request.flags.includeValue() && isPersistent && this@toProto is TwoStatePreference) { value = preferenceValueProto { booleanValue = this@toProto.isChecked } } this@toProto.fragment.toActionTarget(preferenceExtras)?.let { @@ -243,14 +249,14 @@ private constructor(private val context: Context, private val request: GetPrefer screenMetadata: PreferenceScreenMetadata, isRoot: Boolean, ): PreferenceGroupProto = preferenceGroupProto { - preference = toProto(screenMetadata, this@toProto, isRoot) + preference = toProto(screenMetadata, this@toProto.metadata, isRoot) forEachAsync { addPreferences( preferenceOrGroupProto { if (it is PreferenceHierarchy) { group = it.toProto(screenMetadata, false) } else { - preference = toProto(screenMetadata, it, false) + preference = toProto(screenMetadata, it.metadata, false) } } ) @@ -259,93 +265,19 @@ private constructor(private val context: Context, private val request: GetPrefer private suspend fun toProto( screenMetadata: PreferenceScreenMetadata, - node: PreferenceHierarchyNode, + metadata: PreferenceMetadata, isRoot: Boolean, - ) = preferenceProto { - val metadata = node.metadata - key = metadata.key - metadata.getTitleTextProto(isRoot)?.let { title = it } - if (metadata.summary != 0) { - summary = textProto { resourceId = metadata.summary } - } else { - (metadata as? PreferenceSummaryProvider)?.getSummary(context)?.let { - summary = textProto { string = it.toString() } - } - } - val metadataIcon = metadata.getPreferenceIcon(context) - if (metadataIcon != 0) icon = metadataIcon - if (metadata.keywords != 0) keywords = metadata.keywords - val preferenceExtras = metadata.extras(context) - preferenceExtras?.let { extras = it.toProto() } - indexable = metadata.isIndexable(context) - enabled = metadata.isEnabled(context) - if (metadata is PreferenceAvailabilityProvider) { - available = metadata.isAvailable(context) - } - if (metadata is PreferenceRestrictionProvider) { - restricted = metadata.isRestricted(context) - } - persistent = metadata.isPersistent(context) - if (persistent) { - if (includeValue && metadata is PersistentPreference<*>) { - value = preferenceValueProto { - when (metadata) { - is BooleanValue -> - metadata - .storage(context) - .getValue(metadata.key, Boolean::class.javaObjectType) - ?.let { booleanValue = it } - is RangeValue -> { - metadata - .storage(context) - .getValue(metadata.key, Int::class.javaObjectType) - ?.let { intValue = it } - } - else -> {} - } - } + ) = + metadata.toProto(context, myUid, callingUid, screenMetadata, isRoot, request.flags).also { + if (metadata is PreferenceScreenMetadata) { + @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata) } - if (includeValueDescriptor) { - valueDescriptor = preferenceValueDescriptorProto { - when (metadata) { - is BooleanValue -> booleanType = true - is RangeValue -> rangeValue = rangeValueProto { - min = metadata.getMinValue(context) - max = metadata.getMaxValue(context) - step = metadata.getIncrementStep(context) - } - else -> {} - } + metadata.intent(context)?.resolveActivity(context.packageManager)?.let { + if (it.packageName == context.packageName) { + add(it.className) } } } - if (metadata is PreferenceScreenMetadata) { - @Suppress("CheckReturnValue") addPreferenceScreenMetadata(metadata) - } - metadata.intent(context)?.let { actionTarget = it.toActionTarget() } - screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() } - } - - private fun PreferenceMetadata.getTitleTextProto(isRoot: Boolean): TextProto? { - if (isRoot && this is PreferenceScreenMetadata) { - val titleRes = screenTitle - if (titleRes != 0) { - return textProto { resourceId = titleRes } - } else { - getScreenTitle(context)?.let { - return textProto { string = it.toString() } - } - } - } else { - val titleRes = title - if (titleRes != 0) { - return textProto { resourceId = titleRes } - } - } - return (this as? PreferenceTitleProvider)?.getTitle(context)?.let { - textProto { string = it.toString() } - } - } private suspend fun String?.toActionTarget(extras: Bundle?): ActionTarget? { if (this.isNullOrEmpty()) return null @@ -399,22 +331,129 @@ private constructor(private val context: Context, private val request: GetPrefer return null } - private suspend fun Intent.toActionTarget(): ActionTarget { - if (component?.packageName == "") { - setClassName(context, component!!.className) + private suspend fun Intent.toActionTarget() = + toActionTarget(context).also { + resolveActivity(context.packageManager)?.let { + if (it.packageName == context.packageName) { + add(it.className) + } + } } - resolveActivity(context.packageManager)?.let { - if (it.packageName == context.packageName) { - add(it.className) + + companion object { + suspend fun of( + context: Context, + myUid: Int, + callingUid: Int, + request: GetPreferenceGraphRequest, + ) = PreferenceGraphBuilder(context, myUid, callingUid, request).also { it.init() } + } +} + +fun PreferenceMetadata.toProto( + context: Context, + myUid: Int, + callingUid: Int, + screenMetadata: PreferenceScreenMetadata, + isRoot: Boolean, + flags: Int, +) = preferenceProto { + val metadata = this@toProto + key = metadata.key + if (flags.includeMetadata()) { + metadata.getTitleTextProto(context, isRoot)?.let { title = it } + if (metadata.summary != 0) { + summary = textProto { resourceId = metadata.summary } + } else { + (metadata as? PreferenceSummaryProvider)?.getSummary(context)?.let { + summary = textProto { string = it.toString() } } } - return actionTargetProto { intent = toProto() } + val metadataIcon = metadata.getPreferenceIcon(context) + if (metadataIcon != 0) icon = metadataIcon + if (metadata.keywords != 0) keywords = metadata.keywords + val preferenceExtras = metadata.extras(context) + preferenceExtras?.let { extras = it.toProto() } + indexable = metadata.isIndexable(context) + enabled = metadata.isEnabled(context) + if (metadata is PreferenceAvailabilityProvider) { + available = metadata.isAvailable(context) + } + if (metadata is PreferenceRestrictionProvider) { + restricted = metadata.isRestricted(context) + } + metadata.intent(context)?.let { actionTarget = it.toActionTarget(context) } + screenMetadata.getLaunchIntent(context, metadata)?.let { launchIntent = it.toProto() } } + persistent = metadata.isPersistent(context) + if (persistent) { + if ( + flags.includeValue() && + enabled && + (!hasAvailable() || available) && + (!hasRestricted() || !restricted) && + metadata is PersistentPreference<*> && + metadata.getReadPermit(context, myUid, callingUid) == ReadWritePermit.ALLOW + ) { + value = preferenceValueProto { + when (metadata) { + is BooleanValue -> + metadata + .storage(context) + .getValue(metadata.key, Boolean::class.javaObjectType) + ?.let { booleanValue = it } + is RangeValue -> { + metadata + .storage(context) + .getValue(metadata.key, Int::class.javaObjectType) + ?.let { intValue = it } + } + else -> {} + } + } + } + if (flags.includeValueDescriptor()) { + valueDescriptor = preferenceValueDescriptorProto { + when (metadata) { + is BooleanValue -> booleanType = true + is RangeValue -> rangeValue = rangeValueProto { + min = metadata.getMinValue(context) + max = metadata.getMaxValue(context) + step = metadata.getIncrementStep(context) + } + else -> {} + } + } + } + } +} - companion object { - suspend fun of(context: Context, request: GetPreferenceGraphRequest) = - PreferenceGraphBuilder(context, request).also { it.init() } +private fun PreferenceMetadata.getTitleTextProto(context: Context, isRoot: Boolean): TextProto? { + if (isRoot && this is PreferenceScreenMetadata) { + val titleRes = screenTitle + if (titleRes != 0) { + return textProto { resourceId = titleRes } + } else { + getScreenTitle(context)?.let { + return textProto { string = it.toString() } + } + } + } else { + val titleRes = title + if (titleRes != 0) { + return textProto { resourceId = titleRes } + } + } + return (this as? PreferenceTitleProvider)?.getTitle(context)?.let { + textProto { string = it.toString() } + } +} + +private fun Intent.toActionTarget(context: Context): ActionTarget { + if (component?.packageName == "") { + setClassName(context, component!!.className) } + return actionTargetProto { intent = toProto() } } @SuppressLint("AppBundleLocaleChanges") |