diff options
48 files changed, 625 insertions, 419 deletions
diff --git a/PermissionController/res/xml/roles.xml b/PermissionController/res/xml/roles.xml index fb12ed0d0..69ea7b1b7 100644 --- a/PermissionController/res/xml/roles.xml +++ b/PermissionController/res/xml/roles.xml @@ -724,10 +724,6 @@ featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> - <permission name="android.permission.COPY_ACCOUNTS" - featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> - <permission name="android.permission.REMOVE_ACCOUNTS" - featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> </permissions> </role> @@ -1495,10 +1491,8 @@ <permission name="android.permission.MANAGE_DEVICE_POLICY_SMS" minSdkVersion="35" /> <permission name="android.permission.MANAGE_DEVICE_POLICY_APP_FUNCTIONS" featureFlag="android.app.appfunctions.flags.Flags.enableAppFunctionManager" /> - <permission name="android.permission.COPY_ACCOUNTS" - featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> - <permission name="android.permission.REMOVE_ACCOUNTS" - featureFlag="android.app.admin.flags.Flags.splitCreateManagedProfileEnabled" /> + <permission name="android.permission.MANAGE_DEFAULT_APPLICATIONS" minSdkVersion="36" + featureFlag="com.android.permission.flags.Flags.crossUserRoleEnabled" /> </permissions> </role> diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java index 56c4944a0..99145c747 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/AppOp.java @@ -26,6 +26,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.modules.utils.build.SdkLevel; +import com.android.role.controller.util.RoleFlags; import com.android.role.controller.util.PackageUtils; import java.util.Objects; @@ -137,8 +138,8 @@ public class AppOp { return false; } return Build.VERSION.SDK_INT >= mMinSdkVersion - // Workaround to match the value 35 for V in roles.xml before SDK finalization. - || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV()); + // Workaround to match the value 36 for B in roles.xml before SDK finalization. + || (mMinSdkVersion == 36 && RoleFlags.isAtLeastB()); } private boolean isAvailableAsUser(@NonNull String packageName, diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java b/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java index 05b19ff94..889f5263d 100644 --- a/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java +++ b/PermissionController/role-controller/java/com/android/role/controller/model/Permission.java @@ -26,6 +26,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.modules.utils.build.SdkLevel; +import com.android.role.controller.util.RoleFlags; import com.android.role.controller.util.UserUtils; import java.util.Objects; @@ -97,8 +98,8 @@ public class Permission { return false; } if (Build.VERSION.SDK_INT >= mMinSdkVersion - // Workaround to match the value 35 for V in roles.xml before SDK finalization. - || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV())) { + // Workaround to match the value 36 for B in roles.xml before SDK finalization. + || (mMinSdkVersion == 36 && RoleFlags.isAtLeastB())) { return true; } if (Build.VERSION.SDK_INT >= mOptionalMinSdkVersion) { 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 1d49b3c1a..9773b93a9 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 @@ -502,8 +502,8 @@ public class Role { return false; } return (Build.VERSION.SDK_INT >= mMinSdkVersion - // Workaround to match the value 35 for V in roles.xml before SDK finalization. - || (mMinSdkVersion == 35 && SdkLevel.isAtLeastV())) + // Workaround to match the value 36 for B in roles.xml before SDK finalization. + || (mMinSdkVersion == 36 && RoleFlags.isAtLeastB())) && Build.VERSION.SDK_INT <= mMaxSdkVersion; } diff --git a/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt b/PermissionController/src/com/android/permissioncontroller/incident/wear/WearConfirmationScreen.kt index 5f9f58221..116b52cfb 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.DialogButtonContent +import com.android.permissioncontroller.permission.ui.wear.elements.material2.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/LocationProviderDialogScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt index 510d19706..691ceae25 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/LocationProviderDialogScreen.kt @@ -26,7 +26,7 @@ import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState import androidx.wear.compose.material.ChipDefaults import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.SwipeToDismissBox -import com.android.permissioncontroller.permission.ui.wear.elements.Chip +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold import com.android.permissioncontroller.permission.ui.wear.model.LocationProviderInterceptDialogArgs import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/TEST_MAPPING b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/TEST_MAPPING index da2dcd839..3cc91855d 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/TEST_MAPPING +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/TEST_MAPPING @@ -15,10 +15,6 @@ { // Flaky "exclude-filter": "android.permissionui.cts.EnhancedConfirmationManagerTest" - }, - { - // Currently failing (b/387705174) - "exclude-filter": "android.permissionui.cts.PermissionTapjackingTest" } ] } 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 13fb30d73..686dd1b62 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionGroupsScreen.kt @@ -25,11 +25,11 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.res.stringResource import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.wear.elements.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl +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.WearPermissionConfirmationDialog import com.android.permissioncontroller.permission.ui.wear.model.RevokeDialogArgs import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper 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 0b96214a2..55db66d41 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearAppPermissionScreen.kt @@ -30,16 +30,16 @@ import com.android.permissioncontroller.permission.ui.model.AppPermissionViewMod import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState 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.DialogButtonContent -import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl +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.WearPermissionConfirmationDialog 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 -import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipDisabledColors import com.android.permissioncontroller.permission.ui.wear.model.AppPermissionConfirmDialogViewModel import com.android.permissioncontroller.permission.ui.wear.model.ConfirmDialogArgs import com.android.permissioncontroller.permission.ui.wear.theme.ResourceHelper 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 0f37511de..a0e41b579 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearEnhancedConfirmationScreen.kt @@ -43,11 +43,11 @@ import com.android.permissioncontroller.permission.ui.wear.elements.CheckYourPho import com.android.permissioncontroller.permission.ui.wear.elements.CheckYourPhoneState import com.android.permissioncontroller.permission.ui.wear.elements.CheckYourPhoneState.InProgress import com.android.permissioncontroller.permission.ui.wear.elements.CheckYourPhoneState.Success -import com.android.permissioncontroller.permission.ui.wear.elements.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.DialogButtonContent import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen 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.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 4670cfb78..35c2ab046 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt @@ -42,7 +42,7 @@ 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.ToggleChipToggleControl +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.model.WearGrantPermissionsViewModel 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 1563f6a57..15d4cd370 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageCustomPermissionScreen.kt @@ -25,13 +25,13 @@ import androidx.compose.runtime.setValue 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.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip @Composable fun WearManageCustomPermissionScreen( viewModel: ManageCustomPermissionsViewModel, - onPermGroupClick: (String) -> Unit + onPermGroupClick: (String) -> Unit, ) { val permissionGroups = viewModel.uiDataLiveData.observeAsState(emptyMap()) var isLoading by remember { mutableStateOf(true) } @@ -39,7 +39,7 @@ fun WearManageCustomPermissionScreen( WearManageCustomPermissionContent( isLoading, getPermGroupChipParams(permissionGroups.value), - onPermGroupClick + onPermGroupClick, ) if (isLoading && permissionGroups.value.isNotEmpty()) { @@ -51,11 +51,11 @@ fun WearManageCustomPermissionScreen( internal fun WearManageCustomPermissionContent( isLoading: Boolean, permGroupChipParams: List<PermGroupChipParam>, - onPermGroupClick: (String) -> Unit + onPermGroupClick: (String) -> Unit, ) { ScrollableScreen( title = stringResource(R.string.additional_permissions), - isLoading = isLoading + isLoading = isLoading, ) { for (params in permGroupChipParams) { item { @@ -65,7 +65,7 @@ internal fun WearManageCustomPermissionContent( icon = params.icon, secondaryLabel = params.secondaryLabel, secondaryLabelMaxLines = 3, - onClick = { onPermGroupClick(params.permGroupName) } + 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 9aacd65d3..20f87f6ba 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearManageStandardPermissionScreen.kt @@ -29,8 +29,8 @@ import androidx.compose.ui.res.stringResource 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.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupIcon import com.android.permissioncontroller.permission.utils.KotlinUtils.getPermGroupLabel import com.android.permissioncontroller.permission.utils.StringUtils @@ -42,7 +42,7 @@ fun WearManageStandardPermissionScreen( viewModel: ManageStandardPermissionsViewModel, onPermGroupClick: (String) -> Unit, onCustomPermissionsClick: () -> Unit, - onAutoRevokedClick: () -> Unit + onAutoRevokedClick: () -> Unit, ) { val permissionGroups = viewModel.uiDataLiveData.observeAsState(emptyMap()) val numCustomPermGroups = viewModel.numCustomPermGroups.observeAsState(0) @@ -56,7 +56,7 @@ fun WearManageStandardPermissionScreen( numAutoRevoked.value, onPermGroupClick, onCustomPermissionsClick, - onAutoRevokedClick + onAutoRevokedClick, ) if (isLoading && permissionGroups.value.isNotEmpty()) { @@ -92,7 +92,7 @@ internal fun getPermGroupChipParams( label = getPermGroupLabel(context, it.key).toString(), icon = getPermGroupIcon(context, it.key), secondaryLabel = - stringResource(summary, uiInfo.nonSystemGranted, uiInfo.nonSystemTotal) + stringResource(summary, uiInfo.nonSystemGranted, uiInfo.nonSystemTotal), ) } .sortedWith { lhs, rhs -> collator.compare(lhs.label, rhs.label) } @@ -107,11 +107,11 @@ internal fun WearManageStandardPermissionContent( numAutoRevoked: Int, onPermGroupClick: (String) -> Unit, onCustomPermissionsClick: () -> Unit, - onAutoRevokedClick: () -> Unit + onAutoRevokedClick: () -> Unit, ) { ScrollableScreen( title = stringResource(R.string.app_permission_manager), - isLoading = isLoading + isLoading = isLoading, ) { for (params in permGroupChipParams) { item { @@ -121,7 +121,7 @@ internal fun WearManageStandardPermissionContent( icon = params.icon, secondaryLabel = params.secondaryLabel, secondaryLabelMaxLines = 3, - onClick = { onPermGroupClick(params.permGroupName) } + onClick = { onPermGroupClick(params.permGroupName) }, ) } } @@ -136,10 +136,10 @@ internal fun WearManageStandardPermissionContent( StringUtils.getIcuPluralsString( LocalContext.current, R.string.additional_permissions_more, - numCustomPermGroups + numCustomPermGroups, ), secondaryLabelMaxLines = 3, - onClick = onCustomPermissionsClick + onClick = onCustomPermissionsClick, ) } } @@ -152,7 +152,7 @@ internal fun WearManageStandardPermissionContent( icon = R.drawable.ic_info, secondaryLabel = stringResource(R.string.auto_revoke_setting_subtitle), secondaryLabelMaxLines = 3, - onClick = onAutoRevokedClick + 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 8e779cb8c..00ebf2f34 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionAppsScreen.kt @@ -32,9 +32,9 @@ 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.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.ListSubheader 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 /** Compose the screen associated to a [WearPermissionAppsFragment]. */ @Composable @@ -65,7 +65,7 @@ fun WearPermissionAppsScreen(helper: WearPermissionAppsHelper) { subtitle = subTitle, showAlways = showAlways, isLoading = isLoading, - onShowSystemClick = helper.onShowSystemClick + onShowSystemClick = helper.onShowSystemClick, ) } } @@ -84,7 +84,7 @@ internal fun WearPermissionAppsContent( subtitle: String, showAlways: Boolean, isLoading: Boolean, - onShowSystemClick: (showSystem: Boolean) -> Unit + onShowSystemClick: (showSystem: Boolean) -> Unit, ) { ScrollableScreen(title = title, subtitle = subtitle, isLoading = isLoading) { val firstItemIndex = categoryOrder.indexOfFirst { !chipsByCategory[it].isNullOrEmpty() } @@ -100,7 +100,7 @@ internal fun WearPermissionAppsContent( top = if (index == firstItemIndex) 0.dp else 12.dp, bottom = 4.dp, start = 14.dp, - end = 14.dp + end = 14.dp, ) ) { Text(text = stringResource(getCategoryString(category, showAlways))) @@ -116,7 +116,7 @@ internal fun WearPermissionAppsContent( icon = it.icon, enabled = it.enabled, onClick = { it.onClick() }, - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) } } @@ -163,5 +163,5 @@ internal val categoryOrder = Category.ALLOWED.categoryName, Category.ALLOWED_FOREGROUND.categoryName, Category.ASK.categoryName, - Category.DENIED.categoryName + Category.DENIED.categoryName, ) diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt index 1259c1ab5..63a6cd5a5 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageDetailsScreen.kt @@ -37,15 +37,15 @@ import com.android.permissioncontroller.permission.ui.model.v31.BasePermissionUs import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.AppPermissionAccessUiInfo import com.android.permissioncontroller.permission.ui.model.v31.PermissionUsageDetailsViewModel.PermissionUsageDetailsUiState -import com.android.permissioncontroller.permission.ui.wear.elements.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip import com.android.permissioncontroller.permission.utils.KotlinUtils @RequiresApi(Build.VERSION_CODES.S) @Composable fun WearPermissionUsageDetailsScreen( permissionGroup: String, - viewModel: BasePermissionUsageDetailsViewModel + viewModel: BasePermissionUsageDetailsViewModel, ) { val context = LocalContext.current val uiData = viewModel.getPermissionUsagesDetailsInfoUiLiveData().observeAsState(null) @@ -56,7 +56,7 @@ fun WearPermissionUsageDetailsScreen( val subtitle = stringResource( R.string.permission_group_usage_title, - KotlinUtils.getPermGroupLabel(context, permissionGroup) + KotlinUtils.getPermGroupLabel(context, permissionGroup), ) val hasSystemApps: Boolean = @@ -80,7 +80,7 @@ fun WearPermissionUsageDetailsScreen( uiInfo.accessStartTime, uiInfo.accessEndTime, uiInfo.showingAttribution, - uiInfo.attributionTags + uiInfo.attributionTags, ) context.startActivityAsUser(intent, uiInfo.userHandle) } @@ -108,7 +108,7 @@ fun WearPermissionUsageDetailsScreen( onShowSystemClick, appPermissionAccessUiInfoList, onChipClick, - onManagePermissionClick + onManagePermissionClick, ) if (isLoading && uiData.value != null) { @@ -126,7 +126,7 @@ internal fun WearPermissionUsageDetailsContent( onShowSystemClick: (Boolean) -> Unit, appPermissionAccessUiInfoList: List<AppPermissionAccessUiInfo>, onChipClick: (AppPermissionAccessUiInfo) -> Unit, - onManagePermissionClick: () -> Unit + onManagePermissionClick: () -> Unit, ) { ScrollableScreen(title = title, subtitle = subtitle, isLoading = isLoading) { if (appPermissionAccessUiInfoList.isEmpty()) { @@ -142,7 +142,7 @@ internal fun WearPermissionUsageDetailsContent( .format(uiInfo.accessEndTime), secondaryLabelMaxLines = Int.MAX_VALUE, icon = uiInfo.badgedPackageIcon, - onClick = { onChipClick(uiInfo) } + onClick = { onChipClick(uiInfo) }, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt index f83d3338d..20e0dd69b 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearPermissionUsageScreen.kt @@ -32,17 +32,14 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.handheld.v31.PermissionUsageControlPreference import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsageViewModel import com.android.permissioncontroller.permission.ui.viewmodel.v31.PermissionUsagesUiState -import com.android.permissioncontroller.permission.ui.wear.elements.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip import com.android.permissioncontroller.permission.utils.Utils import java.text.Collator @RequiresApi(Build.VERSION_CODES.S) @Composable -fun WearPermissionUsageScreen( - sessionId: Long, - viewModel: PermissionUsageViewModel, -) { +fun WearPermissionUsageScreen(sessionId: Long, viewModel: PermissionUsageViewModel) { val context = LocalContext.current val permissionUsagesUiData = viewModel.permissionUsagesUiLiveData.observeAsState(null) val showSystem = viewModel.showSystemAppsLiveData.observeAsState(false) @@ -97,7 +94,7 @@ fun WearPermissionUsageScreen( hasSystemApps, showSystem.value, onShowSystemClick, - permissionGroupPreferences + permissionGroupPreferences, ) if (isLoading && isDataLoaded) { @@ -111,11 +108,11 @@ internal fun WearPermissionUsageContent( hasSystemApps: Boolean, showSystem: Boolean, onShowSystemClick: (Boolean) -> Unit, - permissionGroupPreferences: List<PermissionUsageControlPreference> + permissionGroupPreferences: List<PermissionUsageControlPreference>, ) { ScrollableScreen( title = stringResource(R.string.permission_usage_title), - isLoading = isLoading + isLoading = isLoading, ) { if (permissionGroupPreferences.isEmpty()) { item { Chip(label = stringResource(R.string.no_permissions), onClick = {}) } @@ -129,7 +126,7 @@ internal fun WearPermissionUsageContent( secondaryLabelMaxLines = Int.MAX_VALUE, icon = preference.icon, enabled = preference.isEnabled, - onClick = { preference.performClick() } + onClick = { preference.performClick() }, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt index 423fa7759..9170b7d20 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearUnusedAppsScreen.kt @@ -25,9 +25,9 @@ import com.android.permissioncontroller.R import com.android.permissioncontroller.hibernation.isHibernationEnabled import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod import com.android.permissioncontroller.permission.ui.model.UnusedAppsViewModel.UnusedPeriod.Companion.allPeriods -import com.android.permissioncontroller.permission.ui.wear.elements.Chip -import com.android.permissioncontroller.permission.ui.wear.elements.Icon 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.Icon import com.android.permissioncontroller.permission.ui.wear.model.WearUnusedAppsViewModel @Composable @@ -43,7 +43,7 @@ fun WearUnusedAppsScreen(viewModel: WearUnusedAppsViewModel) { showTimeText = true, title = getScreenTitle(), isLoading = loading.value, - subtitle = getSubTitle(!infoMsgCategoryVisibility.value) + subtitle = getSubTitle(!infoMsgCategoryVisibility.value), ) { for (period in allPeriods) { if (!unusedAppChips.value.containsKey(period)) { @@ -62,7 +62,7 @@ fun WearUnusedAppsScreen(viewModel: WearUnusedAppsViewModel) { secondaryLabel = unusedAppChip.summary, icon = unusedAppChip.icon, iconContentDescription = unusedAppChip.contentDescription, - onClick = unusedAppChip.onClick + onClick = unusedAppChip.onClick, ) } } @@ -108,5 +108,5 @@ private fun posByPeriod(period: UnusedPeriod) = private fun categoryTitleByPeriod(period: UnusedPeriod) = MessageFormat.format( stringResource(R.string.last_opened_category_title), - mapOf("count" to period.months) + mapOf("count" to period.months), ) 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 b84f840cf..d01692159 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 @@ -19,56 +19,20 @@ package com.android.permissioncontroller.permission.ui.wear.elements import android.app.Activity import android.content.Context import android.content.ContextWrapper -import android.graphics.drawable.Drawable -import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp import androidx.fragment.app.FragmentActivity -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.compose.LocalLifecycleOwner -import androidx.lifecycle.repeatOnLifecycle import androidx.wear.compose.foundation.SwipeToDismissValue -import androidx.wear.compose.foundation.lazy.ScalingLazyColumn -import androidx.wear.compose.foundation.lazy.ScalingLazyListScope -import androidx.wear.compose.foundation.lazy.ScalingLazyListState import androidx.wear.compose.foundation.rememberSwipeToDismissBoxState -import androidx.wear.compose.material.CircularProgressIndicator -import androidx.wear.compose.material.MaterialTheme -import androidx.wear.compose.material.PositionIndicator -import androidx.wear.compose.material.Scaffold import androidx.wear.compose.material.SwipeToDismissBox -import androidx.wear.compose.material.Text -import androidx.wear.compose.material.TimeText -import androidx.wear.compose.material.Vignette -import androidx.wear.compose.material.VignettePosition -import androidx.wear.compose.material.scrollAway import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionScaffold import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 -import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme /** * Screen that contains a list of items defined using the [content] parameter, adds the time text @@ -135,201 +99,6 @@ fun ScrollableScreen( } } -@Composable -internal fun Wear2Scaffold( - showTimeText: Boolean, - title: String?, - subtitle: CharSequence?, - image: Any?, - isLoading: Boolean, - content: ScalingLazyListScope.() -> Unit, - titleTestTag: String? = null, - subtitleTestTag: String? = null, -) { - val itemsSpacedBy = 4.dp - val screenWidth = LocalConfiguration.current.screenWidthDp - val screenHeight = LocalConfiguration.current.screenHeightDp - val scrollContentHorizontalPadding = (screenWidth * 0.052).dp - val titleHorizontalPadding = (screenWidth * 0.0884).dp - val subtitleHorizontalPadding = (screenWidth * 0.0416).dp - val scrollContentTopPadding = (screenHeight * 0.1456).dp - itemsSpacedBy - val scrollContentBottomPadding = (screenHeight * 0.3636).dp - val titleBottomPadding = - if (subtitle == null) { - 8.dp - } else { - 4.dp - } - val subtitleBottomPadding = 8.dp - val timeTextTopPadding = - if (showTimeText) { - 1.dp - } else { - 0.dp - } - val titlePaddingValues = - PaddingValues( - start = titleHorizontalPadding, - top = 4.dp, - bottom = titleBottomPadding, - end = titleHorizontalPadding, - ) - val subTitlePaddingValues = - PaddingValues( - start = subtitleHorizontalPadding, - top = 4.dp, - bottom = subtitleBottomPadding, - end = subtitleHorizontalPadding, - ) - val initialCenterIndex = 0 - val centerHeightDp = Dp(LocalConfiguration.current.screenHeightDp / 2.0f) - // We are adding TimeText's padding to create a smooth scrolling - val initialCenterItemScrollOffset = scrollContentTopPadding + timeTextTopPadding - val scrollAwayOffset = centerHeightDp - initialCenterItemScrollOffset - val focusRequester = remember { FocusRequester() } - val listState = remember { ScalingLazyListState(initialCenterItemIndex = initialCenterIndex) } - LaunchedEffect(title) { - listState.animateScrollToItem(index = 0) // Scroll to the top when triggerValue changes - } - WearPermissionTheme { - Scaffold( - modifier = Modifier.focusRequester(focusRequester), - timeText = { - if (showTimeText && !isLoading) { - TimeText( - modifier = - Modifier.scrollAway(listState, initialCenterIndex, scrollAwayOffset) - .padding(top = timeTextTopPadding) - ) - } - }, - vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }, - positionIndicator = - if (!isLoading) { - { PositionIndicator(scalingLazyListState = listState) } - } else { - null - }, - ) { - Box(modifier = Modifier.fillMaxSize()) { - if (isLoading) { - CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) - } else { - val iconColor = chipDefaultColors().iconColor(true).value - ScalingLazyColumn( - modifier = Modifier.fillMaxWidth(), - state = listState, - // Set autoCentering to null to avoid adding extra padding based on the - // content. - autoCentering = null, - contentPadding = - PaddingValues( - start = scrollContentHorizontalPadding, - end = scrollContentHorizontalPadding, - top = scrollContentTopPadding, - bottom = scrollContentBottomPadding, - ), - ) { - staticItem() - image?.let { - val imageModifier = Modifier.size(24.dp) - when (image) { - is Int -> - item { - Image( - painter = painterResource(id = image), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = imageModifier, - colorFilter = ColorFilter.tint(iconColor), - ) - } - is Drawable -> - item { - Image( - painter = rememberDrawablePainter(image), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = imageModifier, - colorFilter = ColorFilter.tint(iconColor), - ) - } - else -> {} - } - } - if (title != null) { - item { - var modifier: Modifier = Modifier - if (titleTestTag != null) { - modifier = modifier.testTag(titleTestTag) - } - ListHeader(modifier = Modifier.padding(titlePaddingValues)) { - Text( - text = title, - textAlign = TextAlign.Center, - modifier = modifier, - ) - } - } - } - if (subtitle != null) { - item { - var modifier: Modifier = - Modifier.align(Alignment.Center).padding(subTitlePaddingValues) - if (subtitleTestTag != null) { - modifier = modifier.testTag(subtitleTestTag) - } - AnnotatedText( - text = subtitle, - style = - MaterialTheme.typography.body2.copy( - color = MaterialTheme.colors.onSurfaceVariant - ), - modifier = modifier, - shouldCapitalize = true, - ) - } - } - - content() - } - RequestFocusOnResume(focusRequester = focusRequester) - } - } - } - } -} - -private fun ScalingLazyListScope.staticItem() { - /* - This empty item helps to ensure accurate scroll offset calculation. If auto centering is enabled - initial item's(first item for us) center matches the center of the screen. Scroll offset is 0 at - that point. - - if auto centering is not enabled, initial item will start at the top of the screen with the - scroll offset equal to ScreenHeight/2 - scrollContentTopPadding - firstItemHeight/2. - - We need to this offset value to properly move time text.That is the scroll-away offset of the - Time Text is equal to the scroll offset of the list at initial position. - - It is easier to calculate if we know the values of ScreenHeight, ScrollContentTopPadding and - FirstItem's height. ScreenHeight and ScrollContentPadding are constants but height of the - FirstItem depends on the content. Instead of measuring the height, we can simplify the - calculation with an empty item with 0dp height. - */ - item {} -} - -@Composable -private fun RequestFocusOnResume(focusRequester: FocusRequester) { - val lifecycleOwner = LocalLifecycleOwner.current - LaunchedEffect(Unit) { - lifecycleOwner.repeatOnLifecycle(state = Lifecycle.State.RESUMED) { - focusRequester.requestFocus() - } - } -} - internal fun dismiss(activity: Activity) { if (activity is FragmentActivity) { if (!activity.supportFragmentManager.popBackStackImmediate()) { diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/AlertDialog.kt index a700c02e2..2bd72624f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/AlertDialog.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/AlertDialog.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable @@ -34,9 +34,9 @@ import androidx.wear.compose.material.LocalTextStyle import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.Text import androidx.wear.compose.material.dialog.Dialog -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState -import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberColumnState +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.WearPermissionIconBuilder data class DialogButtonContent( diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Chip.kt index 40f097c67..15542ec20 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Chip.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Chip.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import android.graphics.drawable.Drawable import androidx.annotation.StringRes @@ -46,6 +46,7 @@ import androidx.wear.compose.material.Icon import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.Text import androidx.wear.compose.material.contentColorFor +import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter /** * This component is an alternative to [Chip], providing the following: @@ -67,7 +68,7 @@ fun Chip( textColor: Color = MaterialTheme.colors.onSurface, iconColor: Color = Color.Unspecified, colors: ChipColors = chipDefaultColors(), - enabled: Boolean = true + enabled: Boolean = true, ) { val iconParam: (@Composable BoxScope.() -> Unit)? = icon?.let { @@ -87,21 +88,21 @@ fun Chip( imageVector = icon, tint = iconColor, contentDescription = iconContentDescription, - modifier = iconModifier + modifier = iconModifier, ) is Int -> Icon( painter = painterResource(id = icon), tint = iconColor, contentDescription = iconContentDescription, - modifier = iconModifier + modifier = iconModifier, ) is Drawable -> Icon( painter = rememberDrawablePainter(icon), tint = iconColor, contentDescription = iconContentDescription, - modifier = iconModifier + modifier = iconModifier, ) else -> {} } @@ -120,7 +121,7 @@ fun Chip( largeIcon = largeIcon, textColor = textColor, colors = colors, - enabled = enabled + enabled = enabled, ) } @@ -143,7 +144,7 @@ fun Chip( textColor: Color = MaterialTheme.colors.onSurface, iconColor: Color = Color.Unspecified, colors: ChipColors = chipDefaultColors(), - enabled: Boolean = true + enabled: Boolean = true, ) { Chip( label = stringResource(id = labelId), @@ -157,7 +158,7 @@ fun Chip( textColor = textColor, iconColor = iconColor, colors = colors, - enabled = enabled + enabled = enabled, ) } @@ -180,7 +181,7 @@ fun Chip( textColor: Color = MaterialTheme.colors.onSurface, secondaryTextColor: Color = MaterialTheme.colors.primary, colors: ChipColors = chipDefaultColors(), - enabled: Boolean = true + enabled: Boolean = true, ) { val hasSecondaryLabel = secondaryLabel != null val hasIcon = icon != null @@ -196,8 +197,8 @@ fun Chip( style = MaterialTheme.typography.button.copy( fontWeight = FontWeight.W600, - hyphens = Hyphens.Auto - ) + hyphens = Hyphens.Auto, + ), ) } @@ -209,7 +210,7 @@ fun Chip( color = secondaryTextColor, overflow = TextOverflow.Ellipsis, maxLines = secondaryLabelMaxLines ?: 1, - style = MaterialTheme.typography.caption2 + style = MaterialTheme.typography.caption2, ) } } @@ -221,7 +222,7 @@ fun Chip( start = 10.dp, top = verticalPadding, end = ChipDefaults.ChipHorizontalPadding, - bottom = verticalPadding + bottom = verticalPadding, ) } else { ChipDefaults.ContentPadding @@ -236,7 +237,7 @@ fun Chip( colors = colors, enabled = enabled, contentPadding = contentPadding, - shape = RoundedCornerShape(26.dp) + shape = RoundedCornerShape(26.dp), ) } @@ -258,6 +259,6 @@ fun chipDisabledColors(): ChipColors { backgroundColor = backgroundColor.copy(alpha = ContentAlpha.disabled), contentColor = contentColor.copy(alpha = ContentAlpha.disabled), secondaryContentColor = secondaryContentColor.copy(alpha = ContentAlpha.disabled), - iconColor = iconColor.copy(alpha = ContentAlpha.disabled) + iconColor = iconColor.copy(alpha = ContentAlpha.disabled), ) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Icon.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Icon.kt index 1a304b37e..3cfac7eef 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/Icon.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Icon.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import android.graphics.drawable.Drawable import androidx.annotation.DrawableRes @@ -29,6 +29,7 @@ import androidx.compose.ui.unit.LayoutDirection import androidx.wear.compose.material.Icon import androidx.wear.compose.material.LocalContentAlpha import androidx.wear.compose.material.LocalContentColor +import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter /** * This component is an alternative to [Icon], providing the following: @@ -40,7 +41,7 @@ public fun Icon( contentDescription: String?, modifier: Modifier = Modifier, tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), - rtlMode: IconRtlMode = IconRtlMode.Default + rtlMode: IconRtlMode = IconRtlMode.Default, ) { val shouldMirror = rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl @@ -48,7 +49,7 @@ public fun Icon( modifier = modifier.scale(scaleX = if (shouldMirror) -1f else 1f, scaleY = 1f), imageVector = imageVector, contentDescription = contentDescription, - tint = tint + tint = tint, ) } @@ -62,7 +63,7 @@ public fun Icon( contentDescription: String?, modifier: Modifier = Modifier, tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), - rtlMode: IconRtlMode = IconRtlMode.Default + rtlMode: IconRtlMode = IconRtlMode.Default, ) { val shouldMirror = rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl @@ -71,7 +72,7 @@ public fun Icon( painter = painterResource(id = id), contentDescription = contentDescription, modifier = modifier.scale(scaleX = if (shouldMirror) -1f else 1f, scaleY = 1f), - tint = tint + tint = tint, ) } @@ -86,7 +87,7 @@ fun Icon( contentDescription: String?, modifier: Modifier = Modifier, tint: Color = LocalContentColor.current.copy(alpha = LocalContentAlpha.current), - rtlMode: IconRtlMode = IconRtlMode.Default + rtlMode: IconRtlMode = IconRtlMode.Default, ) { val shouldMirror = rtlMode == IconRtlMode.Mirrored && LocalLayoutDirection.current == LayoutDirection.Rtl @@ -98,7 +99,7 @@ fun Icon( imageVector = icon, modifier = iconModifier, contentDescription = contentDescription, - tint = tint + tint = tint, ) } is Int -> { @@ -106,7 +107,7 @@ fun Icon( painter = painterResource(id = icon), contentDescription = contentDescription, modifier = iconModifier, - tint = tint + tint = tint, ) } is Drawable -> { @@ -114,7 +115,7 @@ fun Icon( painter = rememberDrawablePainter(icon), contentDescription = contentDescription, modifier = iconModifier, - tint = tint + tint = tint, ) } else -> throw IllegalArgumentException("Type not supported.") @@ -123,5 +124,5 @@ fun Icon( public enum class IconRtlMode { Default, - Mirrored + Mirrored, } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListFooter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ListFooter.kt index 5ed912ec6..4f6d47faf 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListFooter.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ListFooter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row @@ -52,7 +52,7 @@ fun ListFooter(description: String, iconRes: Int? = null, onClick: (() -> Unit)? contentDescription = null, modifier = Modifier.size(LeadingIconSize, LeadingIconSize) - .align(Alignment.CenterVertically) + .align(Alignment.CenterVertically), ) Spacer(modifier = Modifier.width(LeadingIconEndSpacing)) } @@ -62,7 +62,7 @@ fun ListFooter(description: String, iconRes: Int? = null, onClick: (() -> Unit)? textAlign = TextAlign.Start, overflow = TextOverflow.Ellipsis, color = MaterialTheme.colors.onSurfaceVariant, - style = MaterialTheme.typography.caption2 + style = MaterialTheme.typography.caption2, ) } } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ListHeader.kt index 0a2a3937c..2d3eb0d52 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ListHeader.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ListHeader.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement @@ -60,7 +60,7 @@ fun ListHeader( modifier: Modifier = Modifier, backgroundColor: Color = Color.Transparent, contentColor: Color = MaterialTheme.colors.onBackground, - content: @Composable RowScope.() -> Unit + content: @Composable RowScope.() -> Unit, ) { Row( horizontalArrangement = Arrangement.Center, @@ -69,14 +69,14 @@ fun ListHeader( mergeDescendants = true ) { heading() - } + }, ) { CompositionLocalProvider( LocalContentColor provides contentColor, LocalTextStyle provides MaterialTheme.typography.title3.copy( fontWeight = FontWeight.W600, - hyphens = Hyphens.Auto + hyphens = Hyphens.Auto, ), ) { content() @@ -111,7 +111,7 @@ fun ListSubheader( .fillMaxWidth() .wrapContentSize(align = Alignment.CenterStart) .background(backgroundColor) - .semantics(mergeDescendants = true) { heading() } + .semantics(mergeDescendants = true) { heading() }, ) { CompositionLocalProvider( LocalContentColor provides contentColor, @@ -120,7 +120,7 @@ fun ListSubheader( if (icon != null) { Box( modifier = Modifier.wrapContentSize(align = Alignment.CenterStart), - content = icon + content = icon, ) Spacer(modifier = Modifier.width(6.dp)) } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ResponsiveDialog.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ResponsiveDialog.kt index 361a6c925..c43c45358 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ResponsiveDialog.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ResponsiveDialog.kt @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement.spacedBy @@ -44,10 +44,10 @@ import androidx.wear.compose.material.LocalTextStyle import androidx.wear.compose.material.MaterialTheme import androidx.wear.compose.material.PositionIndicator import androidx.wear.compose.material.Scaffold -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumn -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults.responsive -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState -import com.android.permissioncontroller.permission.ui.wear.elements.layout.rememberColumnState +import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumn +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.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/ToggleChip.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChip.kt index 2e89586c9..421d5ca4f 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChip.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChip.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.BoxScope diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChipToggleControl.kt index b6f6db4d3..56fbf3d61 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/ToggleChipToggleControl.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/ToggleChipToggleControl.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.permissioncontroller.permission.ui.wear.elements +package com.android.permissioncontroller.permission.ui.wear.elements.material2 import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Wear2Scaffold.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Wear2Scaffold.kt new file mode 100644 index 000000000..3575b3cff --- /dev/null +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/Wear2Scaffold.kt @@ -0,0 +1,264 @@ +/* + * 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.material2 + +import android.graphics.drawable.Drawable +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.graphics.ColorFilter +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.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.compose.LocalLifecycleOwner +import androidx.lifecycle.repeatOnLifecycle +import androidx.wear.compose.foundation.lazy.ScalingLazyColumn +import androidx.wear.compose.foundation.lazy.ScalingLazyListScope +import androidx.wear.compose.foundation.lazy.ScalingLazyListState +import androidx.wear.compose.material.CircularProgressIndicator +import androidx.wear.compose.material.MaterialTheme +import androidx.wear.compose.material.PositionIndicator +import androidx.wear.compose.material.Scaffold +import androidx.wear.compose.material.Text +import androidx.wear.compose.material.TimeText +import androidx.wear.compose.material.Vignette +import androidx.wear.compose.material.VignettePosition +import androidx.wear.compose.material.scrollAway +import com.android.permissioncontroller.permission.ui.wear.elements.AnnotatedText +import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter +import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionTheme + +/** + * This component is wrapper on material 2 scaffold component. It helps with time text, scroll + * indicator and standard list elements like title, icon and subtitle. + */ +@Composable +fun Wear2Scaffold( + showTimeText: Boolean, + title: String?, + subtitle: CharSequence?, + image: Any?, + isLoading: Boolean, + content: ScalingLazyListScope.() -> Unit, + titleTestTag: String? = null, + subtitleTestTag: String? = null, +) { + val itemsSpacedBy = 4.dp + val screenWidth = LocalConfiguration.current.screenWidthDp + val screenHeight = LocalConfiguration.current.screenHeightDp + val scrollContentHorizontalPadding = (screenWidth * 0.052).dp + val titleHorizontalPadding = (screenWidth * 0.0884).dp + val subtitleHorizontalPadding = (screenWidth * 0.0416).dp + val scrollContentTopPadding = (screenHeight * 0.1456).dp - itemsSpacedBy + val scrollContentBottomPadding = (screenHeight * 0.3636).dp + val titleBottomPadding = + if (subtitle == null) { + 8.dp + } else { + 4.dp + } + val subtitleBottomPadding = 8.dp + val timeTextTopPadding = + if (showTimeText) { + 1.dp + } else { + 0.dp + } + val titlePaddingValues = + PaddingValues( + start = titleHorizontalPadding, + top = 4.dp, + bottom = titleBottomPadding, + end = titleHorizontalPadding, + ) + val subTitlePaddingValues = + PaddingValues( + start = subtitleHorizontalPadding, + top = 4.dp, + bottom = subtitleBottomPadding, + end = subtitleHorizontalPadding, + ) + val initialCenterIndex = 0 + val centerHeightDp = Dp(LocalConfiguration.current.screenHeightDp / 2.0f) + // We are adding TimeText's padding to create a smooth scrolling + val initialCenterItemScrollOffset = scrollContentTopPadding + timeTextTopPadding + val scrollAwayOffset = centerHeightDp - initialCenterItemScrollOffset + val focusRequester = remember { FocusRequester() } + val listState = remember { ScalingLazyListState(initialCenterItemIndex = initialCenterIndex) } + LaunchedEffect(title) { + listState.animateScrollToItem(index = 0) // Scroll to the top when triggerValue changes + } + WearPermissionTheme { + Scaffold( + modifier = Modifier.focusRequester(focusRequester), + timeText = { + if (showTimeText && !isLoading) { + TimeText( + modifier = + Modifier.scrollAway(listState, initialCenterIndex, scrollAwayOffset) + .padding(top = timeTextTopPadding) + ) + } + }, + vignette = { Vignette(vignettePosition = VignettePosition.TopAndBottom) }, + positionIndicator = + if (!isLoading) { + { PositionIndicator(scalingLazyListState = listState) } + } else { + null + }, + ) { + Box(modifier = Modifier.fillMaxSize()) { + if (isLoading) { + CircularProgressIndicator(modifier = Modifier.align(Alignment.Center)) + } else { + val iconColor = + com.android.permissioncontroller.permission.ui.wear.elements.material2 + .chipDefaultColors() + .iconColor(true) + .value + ScalingLazyColumn( + modifier = Modifier.fillMaxWidth(), + state = listState, + // Set autoCentering to null to avoid adding extra padding based on the + // content. + autoCentering = null, + contentPadding = + PaddingValues( + start = scrollContentHorizontalPadding, + end = scrollContentHorizontalPadding, + top = scrollContentTopPadding, + bottom = scrollContentBottomPadding, + ), + ) { + staticItem() + image?.let { + val imageModifier = Modifier.size(24.dp) + when (image) { + is Int -> + item { + Image( + painter = painterResource(id = image), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = imageModifier, + colorFilter = ColorFilter.tint(iconColor), + ) + } + is Drawable -> + item { + Image( + painter = rememberDrawablePainter(image), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = imageModifier, + colorFilter = ColorFilter.tint(iconColor), + ) + } + else -> {} + } + } + if (title != null) { + item { + var modifier: Modifier = Modifier + if (titleTestTag != null) { + modifier = modifier.testTag(titleTestTag) + } + com.android.permissioncontroller.permission.ui.wear.elements + .material2 + .ListHeader(modifier = Modifier.padding(titlePaddingValues)) { + Text( + text = title, + textAlign = TextAlign.Center, + modifier = modifier, + ) + } + } + } + if (subtitle != null) { + item { + var modifier: Modifier = + Modifier.align(Alignment.Center).padding(subTitlePaddingValues) + if (subtitleTestTag != null) { + modifier = modifier.testTag(subtitleTestTag) + } + AnnotatedText( + text = subtitle, + style = + MaterialTheme.typography.body2.copy( + color = MaterialTheme.colors.onSurfaceVariant + ), + modifier = modifier, + shouldCapitalize = true, + ) + } + } + + content() + } + RequestFocusOnResume(focusRequester = focusRequester) + } + } + } + } +} + +private fun ScalingLazyListScope.staticItem() { + /* + This empty item helps to ensure accurate scroll offset calculation. If auto centering is enabled + initial item's(first item for us) center matches the center of the screen. Scroll offset is 0 at + that point. + + if auto centering is not enabled, initial item will start at the top of the screen with the + scroll offset equal to ScreenHeight/2 - scrollContentTopPadding - firstItemHeight/2. + + We need to this offset value to properly move time text.That is the scroll-away offset of the + Time Text is equal to the scroll offset of the list at initial position. + + It is easier to calculate if we know the values of ScreenHeight, ScrollContentTopPadding and + FirstItem's height. ScreenHeight and ScrollContentPadding are constants but height of the + FirstItem depends on the content. Instead of measuring the height, we can simplify the + calculation with an empty item with 0dp height. + */ + item {} +} + +@Composable +private fun RequestFocusOnResume(focusRequester: FocusRequester) { + val lifecycleOwner = LocalLifecycleOwner.current + LaunchedEffect(Unit) { + lifecycleOwner.repeatOnLifecycle(state = Lifecycle.State.RESUMED) { + focusRequester.requestFocus() + } + } +} diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnDefaults.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/layout/ScalingLazyColumnDefaults.kt index 550f1dc24..c06fdaf14 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnDefaults.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/layout/ScalingLazyColumnDefaults.kt @@ -16,7 +16,7 @@ @file:Suppress("ObjectLiteralToLambda") -package com.android.permissioncontroller.permission.ui.wear.elements.layout +package com.android.permissioncontroller.permission.ui.wear.elements.material2.layout import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.PaddingValues @@ -33,7 +33,7 @@ import androidx.wear.compose.foundation.lazy.ScalingLazyColumnDefaults import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType import androidx.wear.compose.foundation.lazy.ScalingParams import androidx.wear.compose.material.ChipDefaults -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState.RotaryMode +import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnState.RotaryMode import kotlin.math.sqrt // This file's content is copied from ScalingLazyColumnDefaults.kt from Horologist (go/horologist), @@ -63,10 +63,7 @@ object ScalingLazyColumnDefaults { firstItemIsFullWidth: Boolean = true, additionalPaddingAtBottom: Dp = 10.dp, verticalArrangement: Arrangement.Vertical = - Arrangement.spacedBy( - space = 4.dp, - alignment = Alignment.Top, - ), + Arrangement.spacedBy(space = 4.dp, alignment = Alignment.Top), horizontalPaddingPercent: Float = 0.052f, rotaryMode: RotaryMode? = RotaryMode.Scroll, hapticsEnabled: Boolean = true, @@ -145,7 +142,7 @@ object ScalingLazyColumnDefaults { return (radius - sqrt( (radius - childViewHeight + childViewWidth * 0.5f) * - (radius - childViewWidth * 0.5f), + (radius - childViewWidth * 0.5f) ) - childViewHeight * 0.5f) .dp @@ -225,10 +222,7 @@ object ScalingLazyColumnDefaults { last.bottomPaddingDp * height + first.paddingCorrection } else { if (configuration.isScreenRound) { - calculateVerticalOffsetForChip( - screenWidthDp, - horizontalPercent, - ) + 10.dp + calculateVerticalOffsetForChip(screenWidthDp, horizontalPercent) + 10.dp } else { 0.dp } diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnState.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/layout/ScalingLazyColumnState.kt index b3698a60c..0e669f6ff 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/layout/ScalingLazyColumnState.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material2/layout/ScalingLazyColumnState.kt @@ -17,7 +17,7 @@ @file:Suppress("ObjectLiteralToLambda") @file:OptIn(ExperimentalWearFoundationApi::class) -package com.android.permissioncontroller.permission.ui.wear.elements.layout +package com.android.permissioncontroller.permission.ui.wear.elements.material2.layout import androidx.compose.foundation.MutatePriority import androidx.compose.foundation.gestures.FlingBehavior @@ -42,8 +42,8 @@ import androidx.wear.compose.foundation.lazy.ScalingLazyListAnchorType import androidx.wear.compose.foundation.lazy.ScalingLazyListScope import androidx.wear.compose.foundation.lazy.ScalingLazyListState import androidx.wear.compose.foundation.lazy.ScalingParams -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnDefaults.responsiveScalingParams -import com.android.permissioncontroller.permission.ui.wear.elements.layout.ScalingLazyColumnState.RotaryMode +import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnDefaults.responsiveScalingParams +import com.android.permissioncontroller.permission.ui.wear.elements.material2.layout.ScalingLazyColumnState.RotaryMode // This file is a copy of ScalingLazyColumnState.kt from Horologist (go/horologist), // remove it once after wear compose supports large screen dialogs. diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt index 7aa165b0f..942a420a8 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButton.kt @@ -33,7 +33,7 @@ import androidx.wear.compose.material3.ButtonDefaults import androidx.wear.compose.material3.LocalTextConfiguration import androidx.wear.compose.material3.LocalTextStyle import androidx.wear.compose.material3.Text -import com.android.permissioncontroller.permission.ui.wear.elements.Chip +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion /** diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt index 504c69bb0..36d3f9f33 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionButtonStyle.kt @@ -21,8 +21,8 @@ import androidx.wear.compose.material.ChipColors import androidx.wear.compose.material.ChipDefaults import androidx.wear.compose.material3.ButtonColors import androidx.wear.compose.material3.ButtonDefaults -import com.android.permissioncontroller.permission.ui.wear.elements.chipDefaultColors -import com.android.permissioncontroller.permission.ui.wear.elements.chipDisabledColors +import com.android.permissioncontroller.permission.ui.wear.elements.material2.chipDefaultColors +import com.android.permissioncontroller.permission.ui.wear.elements.material2.chipDisabledColors import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.DisabledLike import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Primary import com.android.permissioncontroller.permission.ui.wear.elements.material3.WearPermissionButtonStyle.Secondary 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 4647d6eae..430831248 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 @@ -25,8 +25,8 @@ import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState 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.AlertDialog -import com.android.permissioncontroller.permission.ui.wear.elements.DialogButtonContent +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 @Composable @@ -91,7 +91,7 @@ private fun WearPermissionConfirmationDialogInternal( } ?: AlertDialogDefaults.ConfirmIcon Material3AlertDialog( - show = show, + visible = show, onDismissRequest = edgeButtonContent.onClick, edgeButton = { AlertDialogDefaults.EdgeButton(onClick = edgeButtonContent.onClick, content = edgeIcon) @@ -148,7 +148,7 @@ private fun WearPermissionConfirmationDialogInternal( } Material3AlertDialog( - show = show, + visible = show, onDismissRequest = negativeButtonContent?.onClick ?: {}, confirmButton = positiveButton ?: {}, dismissButton = negativeButton ?: {}, diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt index cd18b5b09..35efe5db1 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionListFooter.kt @@ -21,7 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.unit.dp import androidx.wear.compose.material3.ButtonDefaults -import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter +import com.android.permissioncontroller.permission.ui.wear.elements.material2.ListFooter import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion /** This component is creates a transparent styled button to use as a list footer. */ 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 babd7fb6f..98b8facf7 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 @@ -54,7 +54,7 @@ import androidx.wear.compose.material3.TimeText import androidx.wear.compose.material3.lazy.scrollTransform import com.android.permissioncontroller.permission.ui.wear.elements.AnnotatedText import com.android.permissioncontroller.permission.ui.wear.elements.ListScopeWrapper -import com.android.permissioncontroller.permission.ui.wear.elements.Wear2Scaffold +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Wear2Scaffold import com.android.permissioncontroller.permission.ui.wear.elements.rememberDrawablePainter import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion import com.android.permissioncontroller.permission.ui.wear.theme.WearPermissionMaterialUIVersion.MATERIAL2_5 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 9841ca521..6fea14082 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 @@ -20,14 +20,17 @@ import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.semantics.stateDescription import androidx.wear.compose.material3.CheckboxButton import androidx.wear.compose.material3.LocalTextConfiguration import androidx.wear.compose.material3.RadioButton import androidx.wear.compose.material3.SwitchButton import androidx.wear.compose.material3.Text -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl -import com.android.permissioncontroller.permission.ui.wear.elements.toggleControlSemantics +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.WearPermissionMaterialUIVersion /** @@ -118,12 +121,16 @@ private fun WearPermissionToggleControlInternal( } val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } } - + val toggleControlStateDescription = + stringResource( + if (checked) { + R.string.on + } else { + R.string.off + } + ) val updatedModifier = - modifier - .fillMaxWidth() - // .heightIn(min = 58.dp) // TODO(b/370783358): This should be a overlaid value - .toggleControlSemantics(toggleControl, checked) + modifier.fillMaxWidth().semantics { stateDescription = toggleControlStateDescription } when (toggleControl) { ToggleChipToggleControl.Radio -> diff --git a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt index b5746f019..048a06861 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/elements/material3/WearPermissionToggleControlStyle.kt @@ -25,8 +25,8 @@ import androidx.wear.compose.material3.RadioButtonColors import androidx.wear.compose.material3.RadioButtonDefaults.radioButtonColors import androidx.wear.compose.material3.SwitchButtonColors import androidx.wear.compose.material3.SwitchButtonDefaults.switchButtonColors -import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipBackgroundColors -import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipDisabledColors +import com.android.permissioncontroller.permission.ui.wear.elements.material2.toggleChipBackgroundColors +import com.android.permissioncontroller.permission.ui.wear.elements.material2.toggleChipDisabledColors /** * Defines toggle control styles, It helps in setting the right colors scheme to a toggle control. diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt index afee50389..c322b2bef 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppListScreen.kt @@ -29,10 +29,10 @@ import androidx.compose.ui.res.stringResource import androidx.lifecycle.LiveData import androidx.wear.compose.material.Text import com.android.permissioncontroller.R -import com.android.permissioncontroller.permission.ui.wear.elements.Chip import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.chipDefaultColors -import com.android.permissioncontroller.permission.ui.wear.elements.chipDisabledColors +import com.android.permissioncontroller.permission.ui.wear.elements.material2.Chip +import com.android.permissioncontroller.permission.ui.wear.elements.material2.chipDefaultColors +import com.android.permissioncontroller.permission.ui.wear.elements.material2.chipDisabledColors import com.android.permissioncontroller.role.ui.RoleItem @Composable @@ -65,7 +65,7 @@ fun WearDefaultAppListScreen( onClick = pref.getOnClicked(), modifier = Modifier.fillMaxWidth(), labelMaxLines = Int.MAX_VALUE, - secondaryLabelMaxLines = Integer.MAX_VALUE + secondaryLabelMaxLines = Integer.MAX_VALUE, ) } } 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 5a90380e0..50b109248 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt @@ -26,13 +26,13 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.wear.compose.material.ToggleChipDefaults -import com.android.permissioncontroller.permission.ui.wear.elements.DialogButtonContent -import com.android.permissioncontroller.permission.ui.wear.elements.ListFooter import com.android.permissioncontroller.permission.ui.wear.elements.ScrollableScreen -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChip -import com.android.permissioncontroller.permission.ui.wear.elements.ToggleChipToggleControl +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.WearPermissionConfirmationDialog -import com.android.permissioncontroller.permission.ui.wear.elements.toggleChipDisabledColors 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 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 4a00efa1a..f891fc25f 100644 --- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt @@ -32,7 +32,7 @@ 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.ToggleChipToggleControl +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 diff --git a/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml b/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml index de033ac44..cb6323fff 100644 --- a/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml +++ b/SafetyCenter/Resources/res/raw-v36/safety_center_config.xml @@ -30,6 +30,36 @@ initialDisplayState="disabled" notificationsAllowed="true"/> <dynamic-safety-source + id="AndroidFaceUnlock" + packageName="com.android.settings" + profile="all_profiles" + title="@com.android.safetycenter.resources:string/face_unlock_title" + titleForWork="@com.android.safetycenter.resources:string/face_unlock_title_for_work" + titleForPrivateProfile="@com.android.safetycenter.resources:string/face_unlock_title_for_private_profile" + searchTerms="@com.android.safetycenter.resources:string/face_unlock_search_terms" + refreshOnPageOpenAllowed="true" + initialDisplayState="hidden"/> + <dynamic-safety-source + id="AndroidFingerprintUnlock" + packageName="com.android.settings" + profile="all_profiles" + title="@com.android.safetycenter.resources:string/fingerprint_unlock_title" + titleForWork="@com.android.safetycenter.resources:string/fingerprint_unlock_title_for_work" + titleForPrivateProfile="@com.android.safetycenter.resources:string/fingerprint_unlock_title_for_private_profile" + searchTerms="@com.android.safetycenter.resources:string/fingerprint_unlock_search_terms" + refreshOnPageOpenAllowed="true" + initialDisplayState="hidden"/> + <dynamic-safety-source + id="AndroidWearUnlock" + packageName="com.android.settings" + profile="all_profiles" + title="@com.android.safetycenter.resources:string/wear_unlock_title" + titleForWork="@com.android.safetycenter.resources:string/wear_unlock_title_for_work" + titleForPrivateProfile="@com.android.safetycenter.resources:string/wear_unlock_title_for_private_profile" + searchTerms="@com.android.safetycenter.resources:string/wear_unlock_search_terms" + refreshOnPageOpenAllowed="true" + initialDisplayState="hidden"/> + <dynamic-safety-source id="AndroidBiometrics" packageName="com.android.settings" profile="all_profiles" diff --git a/SafetyCenter/Resources/res/values-v36/config.xml b/SafetyCenter/Resources/res/values-v36/config.xml new file mode 100644 index 000000000..6fa28a340 --- /dev/null +++ b/SafetyCenter/Resources/res/values-v36/config.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 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 + ~ + ~ 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. + --> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Comma separated list of safety source IDs to show in the same task as the safety center --> + <string name="config_same_task_safety_source_ids" translatable="false">AndroidAccessibility,AndroidBackgroundLocation,AndroidBiometrics,AndroidFaceUnlock,AndroidFingerprintUnlock,AndroidHealthConnect,AndroidLockScreen,AndroidPrivateSpace,AndroidMoreSettings,AndroidNotificationListener,AndroidPermissionAutoRevoke,AndroidPermissionManager,AndroidPermissionUsage,AndroidPrivacyAppDataSharingUpdates,AndroidPrivacyControls,AndroidWearUnlock,AndroidWorkPolicyInfo</string> +</resources> diff --git a/SafetyCenter/Resources/res/values-v36/strings.xml b/SafetyCenter/Resources/res/values-v36/strings.xml new file mode 100644 index 000000000..f452e045a --- /dev/null +++ b/SafetyCenter/Resources/res/values-v36/strings.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 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 + ~ + ~ 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. + --> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="face_unlock_title" description="The default title of the setting for managing face unlock options on the device">Face</string> + <string name="face_unlock_title_for_work" description="The default title of the setting for managing face unlock options for work on the device">Face for work</string> + <string name="face_unlock_title_for_private_profile" description="The default title of the setting for managing face unlock options for private profile on the device"><!-- Empty placeholder--></string> + <string name="face_unlock_search_terms" description="Search keywords of the setting for managing face unlock options on the device">Face unlock, Face</string> + + <string name="fingerprint_unlock_title" description="The default title of the setting for managing fingerprint unlock options on the device">Fingerprint</string> + <string name="fingerprint_unlock_title_for_work" description="The default title of the setting for managing fingerprint unlock options for work on the device">Fingerprint for work</string> + <string name="fingerprint_unlock_title_for_private_profile" description="The default title of the setting for managing fingerprint unlock options for private profile on the device"><!-- Empty placeholder--></string> + <string name="fingerprint_unlock_search_terms" description="Search keywords of the setting for managing fingerprint unlock options on the device">Fingerprint, Finger, Add fingerprint</string> + + <string name="wear_unlock_title" description="The default title of the setting for managing wear unlock options on the device">Watch</string> + <string name="wear_unlock_title_for_work" description="The default title of the setting for managing wear unlock options for work on the device">Watch for work</string> + <string name="wear_unlock_title_for_private_profile" description="The default title of the setting for managing wear unlock options for private profile on the device"><!-- Empty placeholder--></string> + <string name="wear_unlock_search_terms" description="Search keywords of the setting for managing wear unlock options on the device">Watch, Watch unlock</string> +</resources> diff --git a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt index 687234582..44eef2144 100644 --- a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt +++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt @@ -93,7 +93,7 @@ class DeviceAwarePermissionGrantTest { val displayConfigBuilder = VirtualDeviceRule.createDefaultVirtualDisplayConfigBuilder( DISPLAY_WIDTH, - DISPLAY_HEIGHT + DISPLAY_HEIGHT, ) .setFlags( DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC or @@ -114,7 +114,7 @@ class DeviceAwarePermissionGrantTest { @RequiresFlagsEnabled( Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, - Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED + Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, ) @Test fun onHostDevice_requestPermissionForHostDevice_shouldGrantPermission() { @@ -124,13 +124,13 @@ class DeviceAwarePermissionGrantTest { false, "", expectPermissionGrantedOnDefaultDevice = true, - expectPermissionGrantedOnRemoteDevice = false + expectPermissionGrantedOnRemoteDevice = false, ) } @RequiresFlagsEnabled( Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, - Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED + Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, ) @Test fun onHostDevice_requestPermissionForRemoteDevice_shouldGrantPermission() { @@ -140,13 +140,13 @@ class DeviceAwarePermissionGrantTest { true, deviceDisplayName, expectPermissionGrantedOnDefaultDevice = false, - expectPermissionGrantedOnRemoteDevice = true + expectPermissionGrantedOnRemoteDevice = true, ) } @RequiresFlagsEnabled( Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, - Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED + Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, ) @RequiresFlagsDisabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test @@ -160,8 +160,9 @@ class DeviceAwarePermissionGrantTest { @RequiresFlagsEnabled( Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, - Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES + Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES, ) + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @Test fun onRemoteDevice_requestPermissionForHostDevice_shouldGrantPermission() { // Create a virtual device with default policy, so that camera permission request will @@ -176,16 +177,18 @@ class DeviceAwarePermissionGrantTest { virtualDisplay.display.displayId, virtualDevice.deviceId, true, - Settings.Global.getString(defaultDeviceContext.contentResolver, - Settings.Global.DEVICE_NAME), + Settings.Global.getString( + defaultDeviceContext.contentResolver, + Settings.Global.DEVICE_NAME, + ), expectPermissionGrantedOnDefaultDevice = true, - expectPermissionGrantedOnRemoteDevice = false + expectPermissionGrantedOnRemoteDevice = false, ) } @RequiresFlagsEnabled( Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, - Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED + Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, ) @Test fun onRemoteDevice_requestPermissionForRemoteDevice_shouldGrantPermission() { @@ -195,7 +198,7 @@ class DeviceAwarePermissionGrantTest { true, deviceDisplayName, expectPermissionGrantedOnDefaultDevice = false, - expectPermissionGrantedOnRemoteDevice = true + expectPermissionGrantedOnRemoteDevice = true, ) } @@ -205,7 +208,7 @@ class DeviceAwarePermissionGrantTest { showDeviceName: Boolean, expectedDeviceNameInDialog: String, expectPermissionGrantedOnDefaultDevice: Boolean, - expectPermissionGrantedOnRemoteDevice: Boolean + expectPermissionGrantedOnRemoteDevice: Boolean, ) { // Assert no permission granted to either default device or virtual device at the beginning assertAppHasPermissionForDevice(DEVICE_ID_DEFAULT, false) @@ -240,13 +243,13 @@ class DeviceAwarePermissionGrantTest { assertAppHasPermissionForDevice(DEVICE_ID_DEFAULT, expectPermissionGrantedOnDefaultDevice) assertAppHasPermissionForDevice( virtualDevice.deviceId, - expectPermissionGrantedOnRemoteDevice + expectPermissionGrantedOnRemoteDevice, ) } private fun requestPermissionOnDevice( displayId: Int, - targetDeviceId: Int + targetDeviceId: Int, ): CompletableFuture<Bundle> { val future = CompletableFuture<Bundle>() val callback = RemoteCallback { result: Bundle? -> future.complete(result) } diff --git a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt index 8e91a00ce..9ec09dab7 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/EnhancedConfirmationManagerTest.kt @@ -78,20 +78,14 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun installedAppStartsWithModeDefault() { installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST) - eventually { - runWithShellPermissionIdentity { - assertEquals( - getAppEcmState(context, appOpsManager, APP_PACKAGE_NAME), - AppOpsManager.MODE_DEFAULT - ) - } - } + waitForModeDefault() } @RequiresFlagsEnabled(Flags.FLAG_ENHANCED_CONFIRMATION_MODE_APIS_ENABLED) @Test fun givenStoreAppThenIsNotRestrictedFromProtectedSetting() { installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST) + waitForModeDefault() runWithShellPermissionIdentity { eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } } @@ -101,6 +95,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun givenLocalAppThenIsRestrictedFromProtectedSetting() { installPackageWithInstallSourceAndMetadataFromLocalFile(APP_APK_NAME_LATEST) + waitForModeDefault() runWithShellPermissionIdentity { eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } } @@ -110,6 +105,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun givenDownloadedThenAppIsRestrictedFromProtectedSetting() { installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + waitForModeDefault() runWithShellPermissionIdentity { eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } } @@ -119,6 +115,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun givenExplicitlyRestrictedAppThenIsRestrictedFromProtectedSetting() { installPackageWithInstallSourceAndMetadataFromStore(APP_APK_NAME_LATEST) + waitForModeDefault() eventually { runWithShellPermissionIdentity { assertEquals( @@ -138,6 +135,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun givenRestrictedAppThenIsNotRestrictedFromNonProtectedSetting() { installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + waitForModeDefault() runWithShellPermissionIdentity { eventually { assertFalse(ecm.isRestricted(APP_PACKAGE_NAME, NON_PROTECTED_SETTING)) } } @@ -147,6 +145,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun givenRestrictedAppThenClearRestrictionNotAllowedByDefault() { installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + waitForModeDefault() runWithShellPermissionIdentity { eventually { assertFalse(ecm.isClearRestrictionAllowed(APP_PACKAGE_NAME)) } } @@ -156,6 +155,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun givenRestrictedAppWhenClearRestrictionThenNotRestrictedFromProtectedSetting() { installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + waitForModeDefault() runWithShellPermissionIdentity { eventually { assertTrue(ecm.isRestricted(APP_PACKAGE_NAME, PROTECTED_SETTING)) } ecm.setClearRestrictionAllowed(APP_PACKAGE_NAME) @@ -169,6 +169,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { @Test fun createRestrictedSettingDialogIntentReturnsIntent() { installPackageWithInstallSourceAndMetadataFromDownloadedFile(APP_APK_NAME_LATEST) + waitForModeDefault() val intent = ecm.createRestrictedSettingDialogIntent(APP_PACKAGE_NAME, PROTECTED_SETTING) @@ -181,6 +182,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( APP_APK_NAME_LATEST ) + waitForModeDefault() val permissionAndExpectedGrantResults = arrayOf( GROUP_2_PERMISSION_1_RESTRICTED to false, @@ -207,6 +209,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( APP_APK_NAME_LATEST ) + waitForModeDefault() requestAppPermissionsAndAssertResult( GROUP_3_PERMISSION_1_UNRESTRICTED to false, @@ -236,6 +239,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( APP_APK_NAME_LATEST ) + waitForModeDefault() requestAppPermissionsAndAssertResult( GROUP_3_PERMISSION_1_UNRESTRICTED to true, @@ -254,6 +258,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( APP_APK_NAME_LATEST ) + waitForModeDefault() requestAppPermissionsAndAssertResult( GROUP_4_PERMISSION_1_UNRESTRICTED to true, @@ -287,6 +292,18 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { ) } + private fun waitForModeDefault() { + eventually { + runWithShellPermissionIdentity { + assertEquals( + "Timed out waiting for package mode to change to MODE_DEFAULT", + getAppEcmState(context, appOpsManager, APP_PACKAGE_NAME), + AppOpsManager.MODE_DEFAULT + ) + } + } + } + companion object { private const val GROUP_2_PERMISSION_1_RESTRICTED = Manifest.permission.SEND_SMS private const val GROUP_2_PERMISSION_2_RESTRICTED = Manifest.permission.READ_SMS @@ -294,7 +311,7 @@ class EnhancedConfirmationManagerTest : BaseUsePermissionTest() { Manifest.permission.ACCESS_FINE_LOCATION private const val GROUP_3_PERMISSION_2_UNRESTRICTED = Manifest.permission.ACCESS_COARSE_LOCATION - private const val GROUP_4_PERMISSION_1_UNRESTRICTED = Manifest.permission.BODY_SENSORS + private const val GROUP_4_PERMISSION_1_UNRESTRICTED = Manifest.permission.CAMERA private const val NON_PROTECTED_SETTING = "example_setting_which_is_not_protected" private const val PROTECTED_SETTING = "android:bind_accessibility_service" diff --git a/tests/cts/role/Android.bp b/tests/cts/role/Android.bp index 665662951..ea9af5d8e 100644 --- a/tests/cts/role/Android.bp +++ b/tests/cts/role/Android.bp @@ -37,7 +37,9 @@ android_test { "bedstead-multiuser", "flag-junit", "platform-test-annotations", + "platform-test-rules", "truth", + "uiautomator-helpers", ], test_suites: [ @@ -48,6 +50,7 @@ android_test { ], data: [ + ":CtsDefaultNotesApp", ":CtsRoleTestApp", ":CtsRoleTestApp28", ":CtsRoleTestApp33WithoutInCallService", diff --git a/tests/cts/role/AndroidManifest.xml b/tests/cts/role/AndroidManifest.xml index a8c8c8e3d..7ea4287dc 100644 --- a/tests/cts/role/AndroidManifest.xml +++ b/tests/cts/role/AndroidManifest.xml @@ -22,6 +22,7 @@ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" /> + <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <application> diff --git a/tests/cts/role/AndroidTest.xml b/tests/cts/role/AndroidTest.xml index 73f23dd1b..9a60b09e3 100644 --- a/tests/cts/role/AndroidTest.xml +++ b/tests/cts/role/AndroidTest.xml @@ -32,6 +32,8 @@ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsRoleTestCases.apk" /> + <option name="install-arg" value="-t" /> + <option name="test-file-name" value="CtsDefaultNotesApp.apk" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> @@ -40,6 +42,7 @@ </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher"> <option name="cleanup" value="true" /> + <option name="push" value="CtsDefaultNotesApp.apk->/data/local/tmp/cts-role/CtsDefaultNotesApp.apk" /> <option name="push" value="CtsRoleTestApp.apk->/data/local/tmp/cts-role/CtsRoleTestApp.apk" /> <option name="push" value="CtsRoleTestApp28.apk->/data/local/tmp/cts-role/CtsRoleTestApp28.apk" /> <option name="push" value="CtsRoleTestApp33WithoutInCallService.apk->/data/local/tmp/cts-role/CtsRoleTestApp33WithoutInCallService.apk" /> diff --git a/tests/cts/role/src/android/app/role/cts/ChooseNoteRoleAppTest.kt b/tests/cts/role/src/android/app/role/cts/ChooseNoteRoleAppTest.kt new file mode 100644 index 000000000..18003d1d9 --- /dev/null +++ b/tests/cts/role/src/android/app/role/cts/ChooseNoteRoleAppTest.kt @@ -0,0 +1,70 @@ +/* + * Copyright (C) 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 + * + * 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 android.app.role.cts + +import android.content.Intent +import android.platform.test.rule.NotesRoleManagerRule +import android.platform.uiautomatorhelpers.WaitUtils.ensureThat +import android.provider.Settings +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By.text +import com.android.compatibility.common.util.UiAutomatorUtils2.getUiDevice +import com.android.compatibility.common.util.UiAutomatorUtils2.waitFindObject +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +class ChooseNoteRoleAppTest { + + @[Rule JvmField] + val rule = NotesRoleManagerRule(requiredNotesRoleHolderPackage = NOTES_APP_PACKAGE_NAME) + + @Before + fun setUp() { + rule.utils.clearRoleHolder() + InstrumentationRegistry.getInstrumentation() + .context + .startActivity( + Intent(Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS) + .addCategory(Intent.CATEGORY_DEFAULT) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + ) + } + + @After + fun after() { + getUiDevice().pressHome() + } + + @Test + fun chooseNoteRoleHolderApp() { + ensureThat { rule.utils.getRoleHolderPackageName().isEmpty() } + + // Scroll to "Notes app" item in Default apps screen and click on it + waitFindObject(text("Notes app")).click() + // Scroll to "CtsDefaultNotesApp" item and click on it + waitFindObject(text("CtsDefaultNotesApp")).click() + + assertEquals(rule.utils.getRoleHolderPackageName(), NOTES_APP_PACKAGE_NAME) + } + + private companion object { + const val NOTES_APP_PACKAGE_NAME = "com.android.cts.notesapp" + } +} |