diff options
36 files changed, 356 insertions, 224 deletions
diff --git a/PermissionController/res/values-watch/donottranslate.xml b/PermissionController/res/values-watch/donottranslate.xml index 43830a93c..03038ddd1 100644 --- a/PermissionController/res/values-watch/donottranslate.xml +++ b/PermissionController/res/values-watch/donottranslate.xml @@ -71,7 +71,7 @@ <dimen name="wear_compose_material3_numeral_extra_large_font_size">60sp</dimen> <dimen name="wear_compose_material3_title_small_font_size">14sp</dimen> <dimen name="wear_compose_material3_title_medium_font_size">16sp</dimen> - <dimen name="wear_compose_material3_title_large_font_size">20sp</dimen> + <dimen name="wear_compose_material3_title_large_font_size">18sp</dimen> <dimen name="wear_compose_material3_shape_corner_extra_small_size">4dp</dimen> <dimen name="wear_compose_material3_shape_corner_small_size">8dp</dimen> diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index 69ea7b1b7..2fd5d6ac5 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -1885,7 +1885,7 @@ shortLabel="@string/role_for_testing_profile_group_exclusivity_short_label" showNone="true" uiBehavior="ReservedForTestingProfileGroupExclusivityRoleUiBehavior" - visible="true"/> + visible="false"/> <!--- ~ A role for the vendor package that provides privacy-preserving intelligent processor for diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java index d025d1b03..1dc0aea48 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/AssistantRoleBehavior.java @@ -230,7 +230,7 @@ public class AssistantRoleBehavior implements RoleBehavior { } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { return VisibilityMixin.isVisible("config_showDefaultAssistant", false, user, context); } diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java index 0261e1eee..95b86f0ca 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/BrowserRoleBehavior.java @@ -157,7 +157,7 @@ public class BrowserRoleBehavior implements RoleBehavior { } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { return VisibilityMixin.isVisible("config_showBrowserRole", true, user, context); } diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java index 153f4a6b4..aa0902136 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/DialerRoleBehavior.java @@ -80,7 +80,7 @@ public class DialerRoleBehavior implements RoleBehavior { } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { return VisibilityMixin.isVisible("config_showDialerRole", true, user, context); } diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java index f19c86596..4a52b0b03 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/EmergencyRoleBehavior.java @@ -71,7 +71,7 @@ public class EmergencyRoleBehavior implements RoleBehavior { } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { return VisibilityMixin.isVisible("config_showDefaultEmergency", false, user, context); } diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java index 8c1446b50..b226674a0 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/HomeRoleBehavior.java @@ -186,7 +186,7 @@ public class HomeRoleBehavior implements RoleBehavior { } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { return VisibilityMixin.isVisible("config_showDefaultHome", false, user, context); } diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java index f02b4d90c..5299886f6 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/ReservedForTestingProfileGroupExclusivityRoleBehavior.java @@ -49,7 +49,7 @@ public class ReservedForTestingProfileGroupExclusivityRoleBehavior implements Ro } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { if (RoleFlags.isProfileGroupExclusivityAvailable()) { Context userContext = UserUtils.getUserContext(context, user); diff --git a/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java index 4aff8a163..ee429c10e 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/behavior/SmsRoleBehavior.java @@ -133,7 +133,7 @@ public class SmsRoleBehavior implements RoleBehavior { } @Override - public boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + public Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { return VisibilityMixin.isVisible("config_showSmsRole", true, user, context); } diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java index 9773b93a9..5109e505b 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java @@ -1098,11 +1098,23 @@ public class Role { * @return whether this role should be visible to user */ public boolean isVisibleAsUser(@NonNull UserHandle user, @NonNull Context context) { - RoleBehavior behavior = getBehavior(); - if (behavior == null) { - return isVisible(); + if (mBehavior != null) { + Boolean isVisibleAsUser = mBehavior.isVisibleAsUser(this, user, context); + if (isVisibleAsUser != null) { + if (isVisibleAsUser && mStatic) { + throw new IllegalArgumentException("static=\"true\" is invalid for a visible " + + "role: " + mName); + } + if (isVisibleAsUser && (mDescriptionResource == 0 + || mLabelResource == 0 + || mShortLabelResource == 0)) { + throw new IllegalArgumentException("description, label, and shortLabel are " + + "required for a visible role: " + mName); + } + return isVisibleAsUser; + } } - return isVisible() && behavior.isVisibleAsUser(this, user, context); + return isVisible(); } /** diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java index 86ca8e2ce..3b08265d1 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleBehavior.java @@ -23,7 +23,6 @@ import android.os.UserHandle; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import java.util.Collections; import java.util.List; /** @@ -129,11 +128,12 @@ public interface RoleBehavior { * @param user the user to check for * @param context the `Context` to retrieve system services * - * @return whether this role should be visible to user + * @return whether this role should be visible to user, or {@code null} if not overridden */ - default boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, + @Nullable + default Boolean isVisibleAsUser(@NonNull Role role, @NonNull UserHandle user, @NonNull Context context) { - return true; + return null; } /** diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java index 4b05554e3..9c3746b79 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/RoleParser.java @@ -413,6 +413,9 @@ public class RoleParser { skipCurrentTag(parser); return null; } + } else if (behavior != null) { + labelResource = getAttributeResourceValue(parser, ATTRIBUTE_LABEL, 0); + shortLabelResource = getAttributeResourceValue(parser, ATTRIBUTE_SHORT_LABEL, 0); } else { labelResource = 0; shortLabelResource = 0; diff --git a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt index e2d46e519..952274d4a 100644 --- a/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt +++ b/PermissionController/src/com/android/permissioncontroller/ecm/EnhancedConfirmationDialogActivity.kt @@ -55,7 +55,6 @@ class EnhancedConfirmationDialogActivity : FragmentActivity() { companion object { private const val KEY_WAS_CLEAR_RESTRICTION_ALLOWED = "KEY_WAS_CLEAR_RESTRICTION_ALLOWED" private const val REASON_PHONE_STATE = "phone_state" - private const val REASON_APP_OP_RESTRICTED = "app_op_restricted" } private var wasClearRestrictionAllowed: Boolean = false diff --git a/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt index 116b52cfb..4c698cda3 100644 --- a/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt @@ -30,7 +30,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.ComposeView import androidx.wear.compose.material.CircularProgressIndicator -import com.android.permissioncontroller.permission.ui.wear.elements.material2.DialogButtonContent +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionConfirmationDialog import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt index f21cb7ac2..5919fad0d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt @@ -26,11 +26,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.DialogButtonContent -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChipToggleControl +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionConfirmationDialog +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlType import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion @@ -83,20 +83,22 @@ internal fun WearAppPermissionGroupsContent( ) { ScrollableScreen(title = stringResource(R.string.app_permissions), isLoading = isLoading) { if (permissionGroupChipParams.isEmpty()) { - item { Chip(label = stringResource(R.string.no_permissions), onClick = {}) } + item { + WearPermissionButton(label = stringResource(R.string.no_permissions), onClick = {}) + } } else { for (info in permissionGroupChipParams) { item { if (info.checked != null) { - ToggleChip( - checked = info.checked, + WearPermissionToggleControl( + toggleControl = WearPermissionToggleControlType.Switch, label = info.label, + checked = info.checked, enabled = info.enabled, - toggleControl = ToggleChipToggleControl.Switch, onCheckedChanged = info.onCheckedChanged, ) } else { - Chip( + WearPermissionButton( label = info.label, labelMaxLines = Integer.MAX_VALUE, secondaryLabel = info.summary?.let { info.summary }, @@ -110,11 +112,11 @@ internal fun WearAppPermissionGroupsContent( autoRevokeChipParam?.let { if (it.visible) { item { - ToggleChip( + WearPermissionToggleControl( checked = it.checked, label = stringResource(it.labelRes), - labelMaxLine = 3, - toggleControl = ToggleChipToggleControl.Switch, + labelMaxLines = 3, + toggleControl = WearPermissionToggleControlType.Switch, onCheckedChanged = it.onCheckedChanged, ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt index 55db66d41..8815e7905 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt @@ -31,13 +31,13 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material2.ListFooter import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChipToggleControl import com.android.permissioncontroller.permission.ui.wear.elements.material2.toggleChipDisabledColors +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionConfirmationDialog import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlType import com.android.permissioncontroller.permission.ui.wear.elements.material3.defaultAlertConfirmIcon import com.android.permissioncontroller.permission.ui.wear.elements.material3.defaultAlertDismissIcon import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionConfirmDialogViewModel @@ -121,7 +121,7 @@ internal fun WearAppPermissionContent( checked = it.isChecked, enabled = it.isEnabled, label = stringResource(R.string.app_permission_location_accuracy), - toggleControl = ToggleChipToggleControl.Switch, + toggleControl = WearPermissionToggleControlType.Switch, onCheckedChanged = onLocationSwitchChanged, labelMaxLine = Integer.MAX_VALUE, ) @@ -141,7 +141,7 @@ internal fun WearAppPermissionContent( toggleChipDisabledColors() }, label = labelsByButton(buttonType), - toggleControl = ToggleChipToggleControl.Radio, + toggleControl = WearPermissionToggleControlType.Radio, onCheckedChanged = { checked -> if (it.isEnabled) { onGrantedStateChanged(buttonType, checked) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt index a0e41b579..ab19a9665 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt @@ -47,7 +47,7 @@ import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableSc import com.android.permissioncontroller.permission.ui.wear.elements.dismiss import com.android.permissioncontroller.permission.ui.wear.elements.findActivity import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.DialogButtonContent +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionConfirmationDialog import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder import com.android.permissioncontroller.permission.ui.wear.model.WearEnhancedConfirmationViewModel diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt index 35c2ab046..8287c5e94 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt @@ -42,9 +42,9 @@ import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.N import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON import com.android.permissioncontroller.permission.ui.wear.GrantPermissionsWearViewHandler.BUTTON_RES_ID_TO_NUM import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChipToggleControl import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlType import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModel import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL3 @@ -81,7 +81,7 @@ fun WearGrantPermissionsScreen( checked = preciseLocationChecked.value, onCheckedChanged = onLocationSwitchChanged, label = stringResource(R.string.app_permission_location_accuracy), - toggleControl = ToggleChipToggleControl.Switch, + toggleControl = WearPermissionToggleControlType.Switch, modifier = Modifier.fillMaxWidth(), labelMaxLines = Integer.MAX_VALUE, materialUIVersion = materialUIVersion, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt index 15d4cd370..d826e501e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt @@ -26,7 +26,8 @@ import androidx.compose.ui.res.stringResource import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.model.ManageCustomPermissionsViewModel import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder @Composable fun WearManageCustomPermissionScreen( @@ -59,10 +60,10 @@ internal fun WearManageCustomPermissionContent( ) { for (params in permGroupChipParams) { item { - Chip( + WearPermissionButton( label = params.label, labelMaxLines = 3, - icon = params.icon, + iconBuilder = params.icon?.let { WearPermissionIconBuilder.builder(it) }, secondaryLabel = params.secondaryLabel, secondaryLabelMaxLines = 3, onClick = { onPermGroupClick(params.permGroupName) }, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt index 20f87f6ba..3e347e19c 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt @@ -30,7 +30,8 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupIcon import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel import com.android.permissioncontroller.permission.utils.StringUtils @@ -115,10 +116,10 @@ internal fun WearManageStandardPermissionContent( ) { for (params in permGroupChipParams) { item { - Chip( + WearPermissionButton( label = params.label, labelMaxLines = 3, - icon = params.icon, + iconBuilder = params.icon?.let { WearPermissionIconBuilder.builder(it) }, secondaryLabel = params.secondaryLabel, secondaryLabelMaxLines = 3, onClick = { onPermGroupClick(params.permGroupName) }, @@ -128,10 +129,10 @@ internal fun WearManageStandardPermissionContent( if (numCustomPermGroups > 0) { item { - Chip( + WearPermissionButton( label = stringResource(R.string.additional_permissions), labelMaxLines = 3, - icon = R.drawable.ic_more_horizontal, + iconBuilder = WearPermissionIconBuilder.builder(R.drawable.ic_more_horizontal), secondaryLabel = StringUtils.getIcuPluralsString( LocalContext.current, @@ -146,10 +147,10 @@ internal fun WearManageStandardPermissionContent( if (numAutoRevoked > 0) { item { - Chip( + WearPermissionButton( label = stringResource(R.string.auto_revoke_permission_notification_title), labelMaxLines = 3, - icon = R.drawable.ic_info, + iconBuilder = WearPermissionIconBuilder.builder(R.drawable.ic_info), secondaryLabel = stringResource(R.string.auto_revoke_setting_subtitle), secondaryLabelMaxLines = 3, onClick = onAutoRevokedClick, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt index 99dffb866..2fa6aa7f3 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt @@ -19,7 +19,6 @@ package com.android.permissioncontroller.permission.ui.wear import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -28,13 +27,13 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.wear.compose.material.Text import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.Category import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ListSubheader +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionListSubHeader /** Compose the screen associated to a [WearPermissionAppsFragment]. */ @Composable @@ -98,26 +97,19 @@ internal fun WearPermissionAppsContent( continue } item { - ListSubheader( - modifier = - Modifier.padding( - top = if (index == firstItemIndex) 0.dp else 12.dp, - bottom = 4.dp, - start = 14.dp, - end = 14.dp, - ) - ) { + WearPermissionListSubHeader(isFirstItemInAList = index == firstItemIndex) { Text(text = stringResource(getCategoryString(category, showAlways))) } } chips.forEach { item { - Chip( + WearPermissionButton( label = it.title, labelMaxLines = Int.MAX_VALUE, secondaryLabel = it.summary, secondaryLabelMaxLines = Int.MAX_VALUE, - icon = it.icon, + iconBuilder = + it.icon?.let { icon -> WearPermissionIconBuilder.builder(icon) }, enabled = it.enabled, onClick = { it.onClick() }, modifier = Modifier.fillMaxWidth(), @@ -128,7 +120,7 @@ internal fun WearPermissionAppsContent( if (hasSystemApps) { item { - Chip( + WearPermissionButton( label = if (showSystem) { stringResource(R.string.menu_hide_system) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt index d01692159..bfa46ae55 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ScrollableScreen.kt @@ -31,8 +31,8 @@ import androidx.wear.compose.foundation.SwipeToDismissValue import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState import androidx.wear.compose.material.SwipeToDismissBox import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold +import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion -import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 /** * Screen that contains a list of items defined using the [content] parameter, adds the time text @@ -42,7 +42,7 @@ import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionM */ @Composable fun ScrollableScreen( - materialUIVersion: WearPermissionMaterialUIVersion = MATERIAL2_5, + materialUIVersion: WearPermissionMaterialUIVersion = ResourceHelper.materialUIVersionInSettings, showTimeText: Boolean = true, title: String? = null, subtitle: CharSequence? = null, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/AlertDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/AlertDialog.kt index 192446039..a49d8822e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/AlertDialog.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/AlertDialog.kt @@ -39,13 +39,9 @@ import androidx.wear.compose.material.dialog.Dialog import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnDefaults import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnState import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.rememberColumnState +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder -data class DialogButtonContent( - val icon: WearPermissionIconBuilder? = null, - val onClick: (() -> Unit), -) - /** * This component is an alternative to [AlertContent], providing the following: * - a convenient way of passing a title and a message; diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ResponsiveDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ResponsiveDialog.kt index c43c45358..4cb3ab615 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ResponsiveDialog.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ResponsiveDialog.kt @@ -48,6 +48,7 @@ import com.android.permissioncontroller.permission.ui.wear.elements.material2.la import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnDefaults.responsive import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnState import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.rememberColumnState +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder import com.android.permissioncontroller.permission.ui.wear.elements.material3.defaultAlertConfirmIcon import com.android.permissioncontroller.permission.ui.wear.elements.material3.defaultAlertDismissIcon diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChip.kt index 421d5ca4f..bfb5d114d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChip.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChip.kt @@ -29,6 +29,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.compositeOver +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.wear.compose.material.ChipDefaults @@ -39,6 +44,8 @@ import androidx.wear.compose.material.ToggleChip import androidx.wear.compose.material.ToggleChipColors import androidx.wear.compose.material.ToggleChipDefaults import androidx.wear.compose.material.contentColorFor +import com.android.permissioncontroller.R +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlType /** * This component is an alternative to [ToggleChip], providing the following: @@ -52,7 +59,7 @@ fun ToggleChip( onCheckedChanged: (Boolean) -> Unit, label: String, labelMaxLine: Int? = null, - toggleControl: ToggleChipToggleControl, + toggleControl: WearPermissionToggleControlType, modifier: Modifier = Modifier, icon: Any? = null, iconColor: Color = Color.Unspecified, @@ -92,15 +99,16 @@ fun ToggleChip( Icon( imageVector = when (toggleControl) { - ToggleChipToggleControl.Switch -> ToggleChipDefaults.switchIcon(checked) - ToggleChipToggleControl.Radio -> ToggleChipDefaults.radioIcon(checked) - ToggleChipToggleControl.Checkbox -> ToggleChipDefaults.checkboxIcon(checked) + WearPermissionToggleControlType.Switch -> ToggleChipDefaults.switchIcon(checked) + WearPermissionToggleControlType.Radio -> ToggleChipDefaults.radioIcon(checked) + WearPermissionToggleControlType.Checkbox -> + ToggleChipDefaults.checkboxIcon(checked) }, contentDescription = null, // This potentially be removed once this issue is addressed: // https://issuetracker.google.com/issues/287087138 rtlMode = - if (toggleControl == ToggleChipToggleControl.Switch) { + if (toggleControl == WearPermissionToggleControlType.Switch) { IconRtlMode.Mirrored } else { IconRtlMode.Default @@ -127,7 +135,7 @@ fun ToggleChip( checked = checked, onCheckedChange = { newChecked -> // Radio buttons cannot be toggled off by tapping on it again. - if (toggleControl != ToggleChipToggleControl.Radio || newChecked) { + if (toggleControl != WearPermissionToggleControlType.Radio || newChecked) { onCheckedChanged.invoke(newChecked) } }, @@ -219,3 +227,29 @@ fun toggleChipBackgroundColors(): ToggleChipColors { uncheckedToggleControlColor = uncheckedToggleControlColor, ) } + +@Composable +fun Modifier.toggleControlSemantics( + toggleControl: WearPermissionToggleControlType, + checked: Boolean, +): Modifier { + val semanticsRole = + when (toggleControl) { + WearPermissionToggleControlType.Switch -> Role.Switch + WearPermissionToggleControlType.Radio -> Role.RadioButton + WearPermissionToggleControlType.Checkbox -> Role.Checkbox + } + val stateDescriptionSemantics = + stringResource( + if (checked) { + R.string.on + } else { + R.string.off + } + ) + + return semantics { + role = semanticsRole + stateDescription = stateDescriptionSemantics + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChipToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChipToggleControl.kt deleted file mode 100644 index 56fbf3d61..000000000 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChipToggleControl.kt +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (C) 2023 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.permissioncontroller.permission.ui.wear.elements.material2 - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.semantics.role -import androidx.compose.ui.semantics.semantics -import androidx.compose.ui.semantics.stateDescription -import com.android.permissioncontroller.R - -enum class ToggleChipToggleControl { - Switch, - Radio, - Checkbox, -} - -@Composable -fun Modifier.toggleControlSemantics( - toggleControl: ToggleChipToggleControl, - checked: Boolean, -): Modifier { - val semanticsRole = - when (toggleControl) { - ToggleChipToggleControl.Switch -> Role.Switch - ToggleChipToggleControl.Radio -> Role.RadioButton - ToggleChipToggleControl.Checkbox -> Role.Checkbox - } - val stateDescriptionSemantics = - stringResource( - if (checked) { - R.string.on - } else { - R.string.off - } - ) - - return semantics { - role = semanticsRole - stateDescription = stateDescriptionSemantics - } -} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionConfirmationDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionConfirmationDialog.kt index 430831248..0e1bf1fbe 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionConfirmationDialog.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionConfirmationDialog.kt @@ -26,9 +26,13 @@ import androidx.wear.compose.material3.AlertDialog as Material3AlertDialog import androidx.wear.compose.material3.AlertDialogDefaults import androidx.wear.compose.material3.Text import com.android.permissioncontroller.permission.ui.wear.elements.material2.AlertDialog -import com.android.permissioncontroller.permission.ui.wear.elements.material2.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion +data class DialogButtonContent( + val icon: WearPermissionIconBuilder? = null, + val onClick: (() -> Unit), +) + @Composable fun WearPermissionConfirmationDialog( materialUIVersion: WearPermissionMaterialUIVersion = diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListSubHeader.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListSubHeader.kt new file mode 100644 index 000000000..ddcd93326 --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListSubHeader.kt @@ -0,0 +1,58 @@ +/* + * 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.permissioncontroller.permission.ui.wear.elements.material3 + +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeightIn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.unit.dp +import androidx.wear.compose.material3.ListSubHeader +import com.android.permissioncontroller.permission.ui.wear.elements.material2.ListSubheader +import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion + +/* +This component is simplified wrapper over ListSubHeader with quick padding adjustments + */ +@Composable +fun WearPermissionListSubHeader( + wearPermissionMaterialUIVersion: WearPermissionMaterialUIVersion = + ResourceHelper.materialUIVersionInSettings, + isFirstItemInAList: Boolean, + label: @Composable RowScope.() -> Unit, +) { + val screenWidth = LocalConfiguration.current.screenWidthDp + val screenHeight = LocalConfiguration.current.screenHeightDp + val subtitlePaddingDefaults = + WearPermissionScaffoldPaddingDefaults( + screenWidth = screenWidth, + screenHeight = screenHeight, + ) + .subHeaderPaddingValues(needsLargePadding = !isFirstItemInAList) + + if (wearPermissionMaterialUIVersion == WearPermissionMaterialUIVersion.MATERIAL3) { + ListSubHeader( + modifier = Modifier.requiredHeightIn(1.dp), // We do not want default min height + contentPadding = subtitlePaddingDefaults, + label = label, + ) + } else { + ListSubheader(modifier = Modifier.padding(subtitlePaddingDefaults), label = label) + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt index 58a14ad5d..35bdf583e 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffold.kt @@ -34,6 +34,7 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.style.Hyphens import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.wear.compose.foundation.ScrollInfoProvider @@ -64,6 +65,7 @@ import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionT private class TransformingScopeConverter(private val scope: TransformingLazyColumnScope) : ListScopeWrapper { override fun item(key: Any?, contentType: Any?, content: @Composable () -> Unit) { + // TODO:https://buganizer.corp.google.com/issues/389093588. scope.item { Box(modifier = Modifier.scrollTransform(this)) { content() } } } } @@ -133,7 +135,6 @@ private fun WearPermissionScaffoldInternal( WearPermissionScaffoldPaddingDefaults( screenWidth = screenWidth, screenHeight = screenHeight, - titleNeedsLargePadding = subtitle == null, ) val columnState = rememberTransformingLazyColumnState() WearPermissionTheme(version = WearPermissionMaterialUIVersion.MATERIAL3) { @@ -152,7 +153,8 @@ private fun WearPermissionScaffoldInternal( icon = painterFromImage(image), title = title, titleTestTag = titleTestTag, - titlePaddingValues = paddingDefaults.titlePaddingValues, + titlePaddingValues = + paddingDefaults.titlePaddingValues(subtitle == null), subtitle = subtitle, subtitleTestTag = subtitleTestTag, subTitlePaddingValues = paddingDefaults.subTitlePaddingValues, @@ -165,42 +167,6 @@ private fun WearPermissionScaffoldInternal( } } -private class WearPermissionScaffoldPaddingDefaults( - screenWidth: Int, - screenHeight: Int, - titleNeedsLargePadding: Boolean, -) { - private val firstSpacerItemHeight = 0.dp - private val scrollContentHorizontalPadding = (screenWidth * 0.052).dp - private val titleHorizontalPadding = (screenWidth * 0.0884).dp - private val subtitleHorizontalPadding = (screenWidth * 0.0416).dp - private val scrollContentTopPadding = (screenHeight * 0.1456).dp - firstSpacerItemHeight - private val scrollContentBottomPadding = (screenHeight * 0.3636).dp - private val defaultItemPadding = 4.dp - private val largeItemPadding = 8.dp - val titlePaddingValues = - PaddingValues( - start = titleHorizontalPadding, - top = defaultItemPadding, - bottom = if (titleNeedsLargePadding) largeItemPadding else defaultItemPadding, - end = titleHorizontalPadding, - ) - val subTitlePaddingValues = - PaddingValues( - start = subtitleHorizontalPadding, - top = defaultItemPadding, - bottom = largeItemPadding, - end = subtitleHorizontalPadding, - ) - val scrollContentPadding = - PaddingValues( - start = scrollContentHorizontalPadding, - end = scrollContentHorizontalPadding, - top = scrollContentTopPadding, - bottom = scrollContentBottomPadding, - ) -} - @Composable private fun BoxScope.ScrollingView( contentPadding: PaddingValues, @@ -301,6 +267,7 @@ private fun ListScopeWrapper.titleItem( text = it, textAlign = TextAlign.Center, modifier = Modifier.optionalTestTag(testTag), + style = MaterialTheme.typography.titleLarge.copy(hyphens = Hyphens.Auto), ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffoldPaddingDefaults.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffoldPaddingDefaults.kt new file mode 100644 index 000000000..14eaec4bf --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionScaffoldPaddingDefaults.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2025 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 + * + * https://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.permissioncontroller.permission.ui.wear.elements.material3 + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.ui.unit.dp + +/* A common class for calculating padding for list items as per the latest design. +https://www.figma.com/design/nb1atBKcK3luF8AXWLUe0X/BC25-Settings-on-Wear?node-id=2336-3304&t=n35PgTUC2O8hGSI0-0 */ +data class WearPermissionScaffoldPaddingDefaults( + private val screenWidth: Int, + private val screenHeight: Int, +) { + private val scrollContentHorizontalPadding = (screenWidth * 0.052).dp + private val titleHorizontalPadding = (screenWidth * 0.0520).dp + private val subtitleHorizontalPadding = (screenWidth * 0.0624).dp + private val scrollContentTopPadding = (screenHeight * 0.1664).dp + private val scrollContentBottomPadding = (screenHeight * 0.3646).dp + private val noPadding = 0.dp + private val defaultItemPadding = 4.dp + private val largeItemPadding = 8.dp + private val extraLargePadding = 12.dp + + fun titlePaddingValues(needsLargePadding: Boolean): PaddingValues = + PaddingValues( + start = titleHorizontalPadding, + top = defaultItemPadding, + bottom = if (needsLargePadding) largeItemPadding else defaultItemPadding, + end = titleHorizontalPadding, + ) + + fun subHeaderPaddingValues(needsLargePadding: Boolean): PaddingValues = + PaddingValues( + start = subtitleHorizontalPadding, + top = if (needsLargePadding) extraLargePadding else noPadding, + bottom = largeItemPadding, + end = subtitleHorizontalPadding, + ) + + val subTitlePaddingValues = + PaddingValues( + start = subtitleHorizontalPadding, + top = defaultItemPadding, + bottom = largeItemPadding, + end = subtitleHorizontalPadding, + ) + val scrollContentPadding = + PaddingValues( + start = scrollContentHorizontalPadding, + end = scrollContentHorizontalPadding, + top = scrollContentTopPadding, + bottom = scrollContentBottomPadding, + ) +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt index 6fea14082..d37d17a84 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControl.kt @@ -30,9 +30,16 @@ import androidx.wear.compose.material3.SwitchButton import androidx.wear.compose.material3.Text import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChipToggleControl +import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion +/** Defines various toggle control types. */ +enum class WearPermissionToggleControlType { + Switch, + Radio, + Checkbox, +} + /** * The custom component is a wrapper on different material3 toggle controls. * 1. It provides an unified interface for RadioButton,CheckButton and SwitchButton. @@ -42,14 +49,13 @@ import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionM */ @Composable fun WearPermissionToggleControl( - toggleControl: ToggleChipToggleControl, + toggleControl: WearPermissionToggleControlType, label: String, checked: Boolean, onCheckedChanged: (Boolean) -> Unit, modifier: Modifier = Modifier, labelMaxLines: Int? = null, - materialUIVersion: WearPermissionMaterialUIVersion = - WearPermissionMaterialUIVersion.MATERIAL2_5, + materialUIVersion: WearPermissionMaterialUIVersion = ResourceHelper.materialUIVersionInSettings, iconBuilder: WearPermissionIconBuilder? = null, secondaryLabel: String? = null, secondaryLabelMaxLines: Int? = null, @@ -90,7 +96,7 @@ fun WearPermissionToggleControl( @Composable private fun WearPermissionToggleControlInternal( label: String, - toggleControl: ToggleChipToggleControl, + toggleControl: WearPermissionToggleControlType, checked: Boolean, onCheckedChanged: (Boolean) -> Unit, modifier: Modifier = Modifier, @@ -133,7 +139,7 @@ private fun WearPermissionToggleControlInternal( modifier.fillMaxWidth().semantics { stateDescription = toggleControlStateDescription } when (toggleControl) { - ToggleChipToggleControl.Radio -> + WearPermissionToggleControlType.Radio -> RadioButton( selected = checked, onSelect = { @@ -151,7 +157,7 @@ private fun WearPermissionToggleControlInternal( colors = style.radioButtonColorScheme(), ) - ToggleChipToggleControl.Checkbox -> + WearPermissionToggleControlType.Checkbox -> CheckboxButton( checked = checked, onCheckedChange = onCheckedChanged, @@ -163,7 +169,7 @@ private fun WearPermissionToggleControlInternal( colors = style.checkboxColorScheme(), ) - ToggleChipToggleControl.Switch -> + WearPermissionToggleControlType.Switch -> SwitchButton( checked = checked, onCheckedChange = onCheckedChanged, diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt index 50b109248..0c39ca8c4 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt @@ -27,12 +27,12 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.wear.compose.material.ToggleChipDefaults import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material2.ListFooter import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChipToggleControl import com.android.permissioncontroller.permission.ui.wear.elements.material2.toggleChipDisabledColors +import com.android.permissioncontroller.permission.ui.wear.elements.material3.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionConfirmationDialog +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlType import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion import com.android.permissioncontroller.role.ui.wear.model.ConfirmDialogArgs @@ -71,7 +71,7 @@ private fun WearDefaultAppContent( icon = it.icon, checked = it.checked, onCheckedChanged = it.onDefaultCheckChanged, - toggleControl = ToggleChipToggleControl.Radio, + toggleControl = WearPermissionToggleControlType.Radio, labelMaxLine = Integer.MAX_VALUE, ) } @@ -90,7 +90,7 @@ private fun WearDefaultAppContent( secondaryLabel = pref.summary?.toString(), checked = pref.checked, onCheckedChanged = pref.getOnCheckChanged(), - toggleControl = ToggleChipToggleControl.Radio, + toggleControl = WearPermissionToggleControlType.Radio, labelMaxLine = Integer.MAX_VALUE, secondaryLabelMaxLine = Integer.MAX_VALUE, ) diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt index f891fc25f..b4758c88b 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt @@ -32,13 +32,13 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.material2.ToggleChipToggleControl import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButton import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionIconBuilder import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionListFooter import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControl import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlStyle +import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionToggleControlType import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion import com.android.permissioncontroller.role.UserPackage @@ -129,7 +129,7 @@ internal fun WearRequestRoleContent( onCheckedChanged = { checked -> onCheckedChanged(checked, pref.userPackage, pref.isHolder) }, - toggleControl = ToggleChipToggleControl.Radio, + toggleControl = WearPermissionToggleControlType.Radio, labelMaxLines = Integer.MAX_VALUE, ) } @@ -154,7 +154,7 @@ internal fun WearRequestRoleContent( onCheckedChanged = { checked -> onCheckedChanged(checked, pref.userPackage, pref.isHolder) }, - toggleControl = ToggleChipToggleControl.Radio, + toggleControl = WearPermissionToggleControlType.Radio, ) } pref.subTitle?.let { subTitle -> @@ -175,7 +175,7 @@ internal fun WearRequestRoleContent( enabled = enabled, onCheckedChanged = { checked -> run { onDontAskAgainCheckedChanged(checked) } }, label = stringResource(R.string.request_role_dont_ask_again), - toggleControl = ToggleChipToggleControl.Checkbox, + toggleControl = WearPermissionToggleControlType.Checkbox, style = WearPermissionToggleControlStyle.Transparent, modifier = Modifier.testTag("com.android.permissioncontroller:id/dont_ask_again"), diff --git a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java index 290388558..4248a429c 100644 --- a/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java +++ b/framework-s/java/android/app/ecm/EnhancedConfirmationManager.java @@ -213,7 +213,7 @@ public final class EnhancedConfirmationManager { * The setting is restricted because the restricted app op is set for the given package * @hide */ - public static final String REASON_APP_OP_RESTRICTED = "app_op_restricted"; + public static final String REASON_PACKAGE_RESTRICTED = "package_restricted"; /** A map of ECM states to their corresponding app op states */ @@ -367,8 +367,9 @@ public final class EnhancedConfirmationManager { intent.putExtra(Intent.EXTRA_UID, uid); intent.putExtra(Intent.EXTRA_SUBJECT, settingIdentifier); try { - intent.putExtra(Intent.EXTRA_REASON, mService.getRestrictionReason(packageName, - settingIdentifier, UserHandle.getUserHandleForUid(uid).getIdentifier())); + String restrictionReason = mService.getRestrictionReason(packageName, + settingIdentifier, UserHandle.getUserHandleForUid(uid).getIdentifier()); + intent.putExtra(Intent.EXTRA_REASON, restrictionReason); } catch (SecurityException | RemoteException e) { // The caller of this method does not have permission to read the ECM state, so we // won't include it in the return diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java index dde5404a4..46b5eedbc 100644 --- a/service/java/com/android/ecm/EnhancedConfirmationService.java +++ b/service/java/com/android/ecm/EnhancedConfirmationService.java @@ -16,7 +16,7 @@ package com.android.ecm; -import static android.app.ecm.EnhancedConfirmationManager.REASON_APP_OP_RESTRICTED; +import static android.app.ecm.EnhancedConfirmationManager.REASON_PACKAGE_RESTRICTED; import static android.app.ecm.EnhancedConfirmationManager.REASON_PHONE_STATE; import android.Manifest; @@ -240,7 +240,7 @@ public class EnhancedConfirmationService extends SystemService { int ECM_STATE_IMPLICIT = AppOpsManager.MODE_DEFAULT; } - private static final ArraySet<String> PROTECTED_SETTINGS = new ArraySet<>(); + private static final ArraySet<String> PER_PACKAGE_PROTECTED_SETTINGS = new ArraySet<>(); // Settings restricted when an untrusted call is ongoing. These must also be added to // PROTECTED_SETTINGS @@ -248,28 +248,27 @@ public class EnhancedConfirmationService extends SystemService { static { // Runtime permissions - PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS); - PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_SMS); - PROTECTED_SETTINGS.add(Manifest.permission.READ_SMS); - PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_MMS); - PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_WAP_PUSH); - PROTECTED_SETTINGS.add(Manifest.permission.READ_CELL_BROADCASTS); - PROTECTED_SETTINGS.add(Manifest.permission_group.SMS); - - PROTECTED_SETTINGS.add(Manifest.permission.BIND_DEVICE_ADMIN); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.SEND_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.READ_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_MMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.RECEIVE_WAP_PUSH); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.READ_CELL_BROADCASTS); + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission_group.SMS); + + PER_PACKAGE_PROTECTED_SETTINGS.add(Manifest.permission.BIND_DEVICE_ADMIN); // App ops - PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); - PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); - PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); - PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_GET_USAGE_STATS); - PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_BIND_ACCESSIBILITY_SERVICE); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_ACCESS_NOTIFICATIONS); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_GET_USAGE_STATS); + PER_PACKAGE_PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_LOADER_USAGE_STATS); // Default application roles. - PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER); - PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS); + PER_PACKAGE_PROTECTED_SETTINGS.add(RoleManager.ROLE_DIALER); + PER_PACKAGE_PROTECTED_SETTINGS.add(RoleManager.ROLE_SMS); if (Flags.unknownCallPackageInstallBlockingEnabled()) { // Requesting package installs, limited during phone calls - PROTECTED_SETTINGS.add(AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES); UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES); UNTRUSTED_CALL_RESTRICTED_SETTINGS.add( @@ -312,11 +311,14 @@ public class EnhancedConfirmationService extends SystemService { if (!isSettingEcmProtected(settingIdentifier)) { return null; } + if (isSettingEcmGuardedForPackage(settingIdentifier, packageName, userId)) { + return REASON_PACKAGE_RESTRICTED; + } String globalProtectionReason = getGlobalProtectionReason(settingIdentifier); if (globalProtectionReason != null) { return globalProtectionReason; } - return isPackageEcmGuarded(packageName, userId) ? REASON_APP_OP_RESTRICTED : null; + return null; } catch (NameNotFoundException e) { throw new IllegalArgumentException(e); } @@ -448,6 +450,14 @@ public class EnhancedConfirmationService extends SystemService { || isAllowlistedInstaller(installingPackageName)); } + private boolean isSettingEcmGuardedForPackage(@NonNull String settingIdentifier, + @NonNull String packageName, @UserIdInt int userId) throws NameNotFoundException { + if (!PER_PACKAGE_PROTECTED_SETTINGS.contains(settingIdentifier)) { + return false; + } + return isPackageEcmGuarded(packageName, userId); + } + private boolean isAllowlistedPackage(String packageName) { return isPackageSignedWithAnyOf(packageName, mTrustedPackageCertDigests.get(packageName)); @@ -518,7 +528,10 @@ public class EnhancedConfirmationService extends SystemService { return false; } - if (PROTECTED_SETTINGS.contains(settingIdentifier)) { + if (PER_PACKAGE_PROTECTED_SETTINGS.contains(settingIdentifier)) { + return true; + } + if (UNTRUSTED_CALL_RESTRICTED_SETTINGS.contains(settingIdentifier)) { return true; } // TODO(b/310218979): Add role selections as protected settings diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt index 16a27c9a8..9a4908c79 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationInCallTest.kt @@ -24,6 +24,7 @@ import android.content.Context import android.content.pm.PackageManager import android.net.Uri import android.os.Build +import android.os.Process import android.permission.flags.Flags import android.platform.test.annotations.AppModeFull import android.platform.test.annotations.RequiresFlagsEnabled @@ -56,8 +57,11 @@ import org.junit.Test // @CddTest(requirement = "TBD") class EnhancedConfirmationInCallTest { private val ecm = context.getSystemService(EnhancedConfirmationManager::class.java)!! + private val aom = context.getSystemService(AppOpsManager::class.java)!! private val packageManager = context.packageManager private val addedContacts = mutableMapOf<String, List<Uri>>() + private val phoneOnlyRestrictedSetting = AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES + private val phoneAndEcmRestrictedSetting = AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES @JvmField @Rule @@ -149,20 +153,32 @@ class EnhancedConfirmationInCallTest { fun tearDown() { voipService.endCallAndWaitForInactive() addedContacts.keys.forEach { removeContact(it) } + runWithShellPermissionIdentity { + aom.setUidMode( + AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, + Process.myUid(), + AppOpsManager.MODE_ALLOWED, + ) + } } - private fun isSettingRestricted(): Boolean { + private fun isSettingRestricted(settingsIdentifier: String): Boolean { return callWithShellPermissionIdentity { - ecm.isRestricted(context.packageName, AppOpsManager.OPSTR_REQUEST_INSTALL_PACKAGES) + ecm.isRestricted(context.packageName, settingsIdentifier) } } + private fun areSettingsRestricted(): Boolean { + return isSettingRestricted(phoneOnlyRestrictedSetting) && + isSettingRestricted(phoneAndEcmRestrictedSetting) + } + @Test fun testIncomingCall_NonContact() { voipService.createCallAndWaitForActive(NON_CONTACT_DISPLAY_NAME, NON_CONTACT_PHONE_NUMBER) - Assert.assertTrue(isSettingRestricted()) + Assert.assertTrue(areSettingsRestricted()) voipService.endCallAndWaitForInactive() - Assert.assertFalse(isSettingRestricted()) + Assert.assertFalse(areSettingsRestricted()) } @Test @@ -170,9 +186,9 @@ class EnhancedConfirmationInCallTest { addContact(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) // If no phone number is given, the display name will be checked voipService.createCallAndWaitForActive(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) - Assert.assertFalse(isSettingRestricted()) + Assert.assertFalse(areSettingsRestricted()) voipService.endCallAndWaitForInactive() - Assert.assertFalse(isSettingRestricted()) + Assert.assertFalse(areSettingsRestricted()) } @Test @@ -180,9 +196,9 @@ class EnhancedConfirmationInCallTest { addContact(CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) // If the phone number matches, the display name is not checked voipService.createCallAndWaitForActive(NON_CONTACT_DISPLAY_NAME, CONTACT_PHONE_NUMBER) - Assert.assertFalse(isSettingRestricted()) + Assert.assertFalse(areSettingsRestricted()) voipService.endCallAndWaitForInactive() - Assert.assertFalse(isSettingRestricted()) + Assert.assertFalse(areSettingsRestricted()) } @Test @@ -192,10 +208,26 @@ class EnhancedConfirmationInCallTest { voipService.createCallAndWaitForActive(tempContactDisplay, tempContactPhone) addContact(tempContactDisplay, tempContactPhone) // State should not be recomputed just because the contact is newly added - Assert.assertTrue(isSettingRestricted()) + Assert.assertTrue(areSettingsRestricted()) voipService.endCallAndWaitForInactive() voipService.createCallAndWaitForActive(tempContactDisplay, tempContactPhone) // A new call should recognize our contact, and mark the call as trusted - Assert.assertFalse(isSettingRestricted()) + Assert.assertFalse(areSettingsRestricted()) + } + + @Test + fun testCallOnlyRestrictedSetting_notRestrictedIfEcmSet() { + // Set the current app to be restricted by ECM + runWithShellPermissionIdentity { + aom.setUidMode( + AppOpsManager.OPSTR_ACCESS_RESTRICTED_SETTINGS, + Process.myUid(), + AppOpsManager.MODE_ERRORED, + ) + } + // The ecm and phone restricted setting is restricted + Assert.assertFalse(isSettingRestricted(phoneOnlyRestrictedSetting)) + // But the phone only restriction is not + Assert.assertFalse(isSettingRestricted(phoneAndEcmRestrictedSetting)) } } |