diff options
11 files changed, 242 insertions, 150 deletions
diff --git a/PermissionController/res/layout-v33/safety_center_toggle_button.xml b/PermissionController/res/layout-v33/safety_center_toggle_button.xml index f790e734e..52d62a7ed 100644 --- a/PermissionController/res/layout-v33/safety_center_toggle_button.xml +++ b/PermissionController/res/layout-v33/safety_center_toggle_button.xml @@ -35,8 +35,10 @@ android:text="@string/available"/> </LinearLayout> + <!-- The X scale controls the direction of the arrow, based on the language direction --> <ImageView android:id="@+id/arrow_icon" style="@style/SafetyCenterQsToggleArrow" + android:scaleX="@integer/mirror_x_scale" android:visibility="gone"/> </LinearLayout> diff --git a/PermissionController/res/values-es-rUS/strings.xml b/PermissionController/res/values-es-rUS/strings.xml index 0a8908fd7..94179a6e7 100644 --- a/PermissionController/res/values-es-rUS/strings.xml +++ b/PermissionController/res/values-es-rUS/strings.xml @@ -370,7 +370,7 @@ <string name="role_dialer_request_description" msgid="6288839625724909320">"Esta app obtendrá acceso a la cámara, los contactos, micrófono, teléfono y SMS"</string> <string name="role_dialer_search_keywords" msgid="3324448983559188087">"marcador"</string> <string name="role_sms_label" msgid="8456999857547686640">"App de SMS predeterminada"</string> - <string name="role_sms_short_label" msgid="4371444488034692243">"app de SMS"</string> + <string name="role_sms_short_label" msgid="4371444488034692243">"App de SMS"</string> <string name="role_sms_description" msgid="3424020199148153513">"Apps que te permiten usar tu número de teléfono para enviar y recibir mensajes de texto cortos, fotos, videos y mucho más"</string> <string name="role_sms_request_title" msgid="7953552109601185602">"¿Quieres establecer <xliff:g id="APP_NAME">%1$s</xliff:g> como app de SMS predeterminada?"</string> <string name="role_sms_request_description" msgid="2691004766132144886">"Se le otorgará acceso a esta app a tu cámara, contactos, archivos y contenido multimedia, micrófono, teléfono y SMS"</string> diff --git a/PermissionController/res/values-ldrtl/integers.xml b/PermissionController/res/values-ldrtl/integers.xml new file mode 100644 index 000000000..ca998ad16 --- /dev/null +++ b/PermissionController/res/values-ldrtl/integers.xml @@ -0,0 +1,18 @@ +<?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> + <integer name="mirror_x_scale">-1</integer> +</resources> diff --git a/PermissionController/res/values/integers.xml b/PermissionController/res/values/integers.xml new file mode 100644 index 000000000..c65ec99de --- /dev/null +++ b/PermissionController/res/values/integers.xml @@ -0,0 +1,18 @@ +<?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> + <integer name="mirror_x_scale">1</integer> +</resources> 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 3ed58cf4a..f918da729 100644 --- a/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt +++ b/PermissionController/src/com/android/permissioncontroller/permission/ui/wear/WearGrantPermissionsScreen.kt @@ -62,6 +62,7 @@ fun WearGrantPermissionsScreen( val materialUIVersion = ResourceHelper.materialUIVersionInApp ScrollableScreen( materialUIVersion = materialUIVersion, + asScalingList = true, showTimeText = false, image = icon.value, title = groupMessage.value, diff --git a/PermissionController/tests/permissionui/AndroidTest.xml b/PermissionController/tests/permissionui/AndroidTest.xml index 9cadbd12f..2462dc4c7 100644 --- a/PermissionController/tests/permissionui/AndroidTest.xml +++ b/PermissionController/tests/permissionui/AndroidTest.xml @@ -63,6 +63,12 @@ value="/data/local/tmp/pc-permissionui/PermissionUiUseReadHeartRatePermissionApp.apk" /> </target_preparer> + <!-- Wake the screen, and dismiss keyguard --> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> + </target_preparer> + <!-- Uninstall test-apps --> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="teardown-command" value="pm uninstall android.permission.cts.appthatrequestpermission" /> diff --git a/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffold.kt b/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffold.kt index 208d3d6ec..87ca048bc 100644 --- a/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffold.kt +++ b/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffold.kt @@ -16,7 +16,6 @@ package com.android.permissioncontroller.wear.permission.components.material3 import android.graphics.drawable.Drawable -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.gestures.ScrollableState import androidx.compose.foundation.layout.Box @@ -30,9 +29,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource @@ -53,6 +50,7 @@ import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState import androidx.wear.compose.foundation.lazy.rememberTransformingLazyColumnState import androidx.wear.compose.material3.AppScaffold import androidx.wear.compose.material3.CircularProgressIndicator +import androidx.wear.compose.material3.Icon import androidx.wear.compose.material3.IconButtonDefaults import androidx.wear.compose.material3.ListHeader import androidx.wear.compose.material3.MaterialTheme @@ -271,6 +269,7 @@ private fun BoxScope.LazyColumnView( titleItem( text = title, testTag = titleTestTag, + asScalingList = true, contentPaddingValues = paddingDefaults.titlePaddingValues(subtitle == null), ) subtitleItem( @@ -287,6 +286,7 @@ private fun BoxScope.LazyColumnView( ScalingLazyColumn( contentPadding = scrollContentPadding, state = listState as ScalingLazyListState, + autoCentering = null, modifier = Modifier.background(MaterialTheme.colorScheme.background), content = { scrollingViewContent(ScalingScopeConverter(this)) }, ) @@ -350,24 +350,25 @@ private fun ListScopeWrapper.iconItem(painter: Painter?, modifier: Modifier = Mo painter?.let { item { val iconColor = WearPermissionButtonStyle.Secondary.material3ButtonColors().iconColor - Image( - painter = it, - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = modifier, - colorFilter = ColorFilter.tint(iconColor), - ) + Icon(painter = it, contentDescription = null, modifier = modifier, tint = iconColor) } } private fun ListScopeWrapper.titleItem( text: String?, + asScalingList: Boolean, testTag: String?, contentPaddingValues: PaddingValues, modifier: Modifier = Modifier, ) = text?.let { item(contentType = "header") { + val style = + if (asScalingList) { + MaterialTheme.typography.titleMedium + } else { + MaterialTheme.typography.titleLarge + } ListHeader( modifier = modifier.requiredHeightIn(1.dp), // We do not want default min height contentPadding = contentPaddingValues, @@ -376,7 +377,7 @@ private fun ListScopeWrapper.titleItem( text = it, textAlign = TextAlign.Center, modifier = Modifier.optionalTestTag(testTag), - style = MaterialTheme.typography.titleLarge.copy(hyphens = Hyphens.Auto), + style = style.copy(hyphens = Hyphens.Auto), ) } } diff --git a/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffoldPaddingDefaults.kt b/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffoldPaddingDefaults.kt index 595fb50a3..01b8fc4be 100644 --- a/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffoldPaddingDefaults.kt +++ b/PermissionController/wear-permission-components/src/wear.permission.components/material3/WearPermissionScaffoldPaddingDefaults.kt @@ -25,15 +25,15 @@ data class WearPermissionScaffoldPaddingDefaults( private val screenHeight: Int, ) { private val scrollContentHorizontalPadding = (screenWidth * 0.052).dp - private val titleHorizontalPadding = (screenWidth * 0.0520).dp - private val subtitleHorizontalPadding = (screenWidth * 0.0624).dp + private val titleHorizontalPadding = (screenWidth * 0.1200).dp + private val subtitleHorizontalPadding = (screenWidth * 0.0416).dp private val scrollContentTopPadding = (screenHeight * 0.1664).dp private val dialogScrollContentLargeTopPadding = (screenHeight * 0.10).dp private val dialogScrollContentTopPadding = (screenHeight * 0.012).dp private val scrollContentBottomPadding = (screenHeight * 0.3646).dp private val noPadding = 0.dp private val defaultItemPadding = 4.dp - private val largeItemPadding = 8.dp + private val largeItemPadding = 12.dp private val extraLargePadding = 12.dp fun titlePaddingValues(needsLargePadding: Boolean): PaddingValues = @@ -48,7 +48,7 @@ data class WearPermissionScaffoldPaddingDefaults( PaddingValues( start = subtitleHorizontalPadding, top = if (needsLargePadding) extraLargePadding else noPadding, - bottom = largeItemPadding, + bottom = 8.dp, end = subtitleHorizontalPadding, ) diff --git a/service/java/com/android/ecm/EnhancedConfirmationService.java b/service/java/com/android/ecm/EnhancedConfirmationService.java index 1a6b80a3c..1cc3a743e 100644 --- a/service/java/com/android/ecm/EnhancedConfirmationService.java +++ b/service/java/com/android/ecm/EnhancedConfirmationService.java @@ -685,7 +685,7 @@ public class EnhancedConfirmationService extends SystemService { if (number != null && mTelephonyManager.isEmergencyNumber(number)) { return CALL_TYPE_EMERGENCY; } - } catch (IllegalStateException | UnsupportedOperationException e) { + } catch (RuntimeException e) { // If either of these are thrown, the telephony service is not available on the // current device, either because the device lacks telephony calling, or the // telephony service is unavailable. diff --git a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt index e1068e19a..0f6504502 100644 --- a/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt +++ b/tests/cts/permissionmultidevice/src/android/permissionmultidevice/cts/DeviceAwarePermissionGrantTest.kt @@ -62,13 +62,8 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) -@SdkSuppress(minSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM, codeName = "VanillaIceCream") +@SdkSuppress(minSdkVersion = Build.VERSION_CODES.BAKLAVA, codeName = "Baklava") @AppModeFull(reason = "VirtualDeviceManager cannot be accessed by instant apps") -@RequiresFlagsEnabled( - Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED, - Flags.FLAG_DEVICE_AWARE_PERMISSIONS_ENABLED, - Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES, -) open class DeviceAwarePermissionGrantTest { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() private val defaultDeviceContext = instrumentation.targetContext @@ -273,7 +268,7 @@ open class DeviceAwarePermissionGrantTest { ) } - // TODO: Receives PERMISSION_DENIED but it's fine if remote permission is held?? + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun deviceAwarePermission_onRemote_requestHostPermission() { createVirtualDevice(cameraPolicy = DEVICE_POLICY_CUSTOM) @@ -289,6 +284,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun deviceAwarePermission_onRemote_withRemotePermission_requestHostPermission() { createVirtualDevice(cameraPolicy = DEVICE_POLICY_CUSTOM) @@ -334,6 +330,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun deviceAwarePermissionWithoutCapability_onHost_requestRemotePermission() { createVirtualDevice(cameraPolicy = DEVICE_POLICY_DEFAULT) @@ -349,6 +346,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun deviceAwarePermissionWithoutCapability_onRemote_requestRemotePermission() { createVirtualDevice(cameraPolicy = DEVICE_POLICY_DEFAULT) @@ -364,6 +362,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun deviceAwarePermissionWithoutCapability_onRemote_requestPermissionWithoutDeviceId() { createVirtualDevice(cameraPolicy = DEVICE_POLICY_DEFAULT) @@ -379,6 +378,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun deviceAwarePermissionWithoutCapability_onRemote_requestHostPermission() { createVirtualDevice(cameraPolicy = DEVICE_POLICY_DEFAULT) @@ -424,6 +424,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun nonDeviceAwarePermission_onHost_requestRemotePermission() { createVirtualDevice() @@ -439,6 +440,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun nonDeviceAwarePermission_onRemote_requestRemotePermission() { createVirtualDevice() @@ -454,6 +456,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun nonDeviceAwarePermission_onRemote_requestPermissionWithoutDeviceId() { createVirtualDevice() @@ -469,6 +472,7 @@ open class DeviceAwarePermissionGrantTest { ) } + @RequiresFlagsEnabled(Flags.FLAG_ALLOW_HOST_PERMISSION_DIALOGS_ON_VIRTUAL_DEVICES) @Test fun nonDeviceAwarePermission_onRemote_requestHostPermission() { createVirtualDevice() diff --git a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt index 9ec09137e..eda6e92be 100644 --- a/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt +++ b/tests/cts/permissionui/src/android/permissionui/cts/BaseUsePermissionTest.kt @@ -57,6 +57,7 @@ import com.android.compatibility.common.util.UiDumpUtils import com.android.modules.utils.build.SdkLevel import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit +import java.util.concurrent.TimeoutException import java.util.regex.Pattern import org.junit.After import org.junit.Assert @@ -64,7 +65,6 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Before -import java.util.concurrent.TimeoutException abstract class BaseUsePermissionTest : BasePermissionTest() { companion object { @@ -237,7 +237,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_PRIVACY, PICKER_ENABLED_SETTING, - true + true, ) } } @@ -246,7 +246,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { enum class PermissionState { ALLOWED, DENIED, - DENIED_WITH_PREJUDICE + DENIED_WITH_PREJUDICE, } private val windowManagerStateHelper = WindowManagerStateHelper() @@ -311,7 +311,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { android.Manifest.permission.READ_MEDIA_IMAGES to "@android:string/permgrouplab_readMediaVisual", android.Manifest.permission.READ_MEDIA_VIDEO to - "@android:string/permgrouplab_readMediaVisual" + "@android:string/permgrouplab_readMediaVisual", ) @Before @@ -333,7 +333,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { grantRuntimePermissions, expectSuccess, installSource, - false + false, ) } @@ -350,7 +350,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { reinstall, grantRuntimePermissions, expectSuccess, - installSource + installSource, ) val targetSdk = getTargetSdk() @@ -397,9 +397,9 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { uiDevice.wait( Until.hasObject( By.textStartsWith("This app was built for an older version of Android") - .displayId(displayId) + .displayId(displayId) ), - timeoutMillis + timeoutMillis, ) if (targetSdkWarningVisible) { try { @@ -425,8 +425,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (isAutomotive || isWatch) { clickAndWaitForWindowTransition( By.text(getPermissionControllerString("review_button_continue")) - .displayId(displayId), - TIMEOUT_MILLIS * 2 + .displayId(displayId), + TIMEOUT_MILLIS * 2, ) } else { clickAndWaitForWindowTransition( @@ -452,7 +452,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { installPackageViaSession( apkName, AppMetadata.createDefaultAppMetadata(), - PACKAGE_SOURCE_STORE + PACKAGE_SOURCE_STORE, ) } @@ -460,7 +460,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { installPackageViaSession( apkName, AppMetadata.createDefaultAppMetadata(), - PACKAGE_SOURCE_LOCAL_FILE + PACKAGE_SOURCE_LOCAL_FILE, ) } @@ -468,18 +468,18 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { installPackageViaSession( apkName, AppMetadata.createDefaultAppMetadata(), - PACKAGE_SOURCE_DOWNLOADED_FILE + PACKAGE_SOURCE_DOWNLOADED_FILE, ) } protected fun installPackageWithInstallSourceFromDownloadedFileAndAllowHardRestrictedPerms( - apkName: String, + apkName: String ) { installPackageViaSession( apkName, AppMetadata.createDefaultAppMetadata(), PACKAGE_SOURCE_DOWNLOADED_FILE, - allowlistedRestrictedPermissions = SessionParams.RESTRICTED_PERMISSIONS_ALL + allowlistedRestrictedPermissions = SessionParams.RESTRICTED_PERMISSIONS_ALL, ) } @@ -487,7 +487,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { installPackageViaSession( apkName, AppMetadata.createDefaultAppMetadata(), - PACKAGE_SOURCE_OTHER + PACKAGE_SOURCE_OTHER, ) } @@ -516,38 +516,38 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } protected fun installPackageWithInstallSourceAndMetadataWithoutTopLevelVersion( - apkName: String, + apkName: String ) { installPackageViaSession( apkName, - AppMetadata.createInvalidAppMetadataWithoutTopLevelVersion() + AppMetadata.createInvalidAppMetadataWithoutTopLevelVersion(), ) } protected fun installPackageWithInstallSourceAndMetadataWithInvalidTopLevelVersion( - apkName: String, + apkName: String ) { installPackageViaSession( apkName, - AppMetadata.createInvalidAppMetadataWithInvalidTopLevelVersion() + AppMetadata.createInvalidAppMetadataWithInvalidTopLevelVersion(), ) } protected fun installPackageWithInstallSourceAndMetadataWithoutSafetyLabelVersion( - apkName: String, + apkName: String ) { installPackageViaSession( apkName, - AppMetadata.createInvalidAppMetadataWithoutSafetyLabelVersion() + AppMetadata.createInvalidAppMetadataWithoutSafetyLabelVersion(), ) } protected fun installPackageWithInstallSourceAndMetadataWithInvalidSafetyLabelVersion( - apkName: String, + apkName: String ) { installPackageViaSession( apkName, - AppMetadata.createInvalidAppMetadataWithInvalidSafetyLabelVersion() + AppMetadata.createInvalidAppMetadataWithInvalidSafetyLabelVersion(), ) } @@ -557,12 +557,14 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } protected fun assertPermissionRationaleActivityTitleIsVisible(expected: Boolean) { - findView(By.res(PERMISSION_RATIONALE_ACTIVITY_TITLE_VIEW).displayId(displayId), - expected = expected) + findView( + By.res(PERMISSION_RATIONALE_ACTIVITY_TITLE_VIEW).displayId(displayId), + expected = expected, + ) } protected fun assertPermissionRationaleActivityDataSharingSourceSectionVisible( - expected: Boolean, + expected: Boolean ) { findView(By.res(DATA_SHARING_SOURCE_TITLE_ID).displayId(displayId), expected = expected) findView(By.res(DATA_SHARING_SOURCE_MESSAGE_ID).displayId(displayId), expected = expected) @@ -579,8 +581,10 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } protected fun assertPermissionRationaleActivitySettingsSectionVisible(expected: Boolean) { - findView(By.res(PERMISSION_RATIONALE_SETTINGS_SECTION).displayId(displayId), - expected = expected) + findView( + By.res(PERMISSION_RATIONALE_SETTINGS_SECTION).displayId(displayId), + expected = expected, + ) findView(By.res(SETTINGS_TITLE_ID).displayId(displayId), expected = expected) findView(By.res(SETTINGS_MESSAGE_ID).displayId(displayId), expected = expected) } @@ -599,8 +603,10 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } protected fun assertPermissionRationaleContainerOnGrantDialogIsVisible(expected: Boolean) { - findView(By.res(GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW).displayId(displayId), - expected = expected) + findView( + By.res(GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW).displayId(displayId), + expected = expected, + ) } protected fun clickPermissionReviewCancel() { @@ -645,7 +651,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { block() assertEquals( expectedResultCode, - future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).resultCode + future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS).resultCode, ) } @@ -660,7 +666,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { component = ComponentName( APP_PACKAGE_NAME, - "$APP_PACKAGE_NAME.RequestPermissionsActivity" + "$APP_PACKAGE_NAME.RequestPermissionsActivity", ) putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions) addFlags(FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_CLEAR_TASK) @@ -678,20 +684,16 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { crossinline block: () -> Unit, ): Instrumentation.ActivityResult { // Request the permissions - val future = - startActivityForFuture( - Intent().apply { - component = - ComponentName( - APP_PACKAGE_NAME, - "$APP_PACKAGE_NAME.RequestPermissionsActivity" - ) - putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions) - putExtra("$APP_PACKAGE_NAME.ASK_TWICE", askTwice) - } - ) - - waitForPermissionRequestActivity() + lateinit var future: CompletableFuture<Instrumentation.ActivityResult> + // The WindowManagerStateHelper#waitForValidState only supports S+ + if (SdkLevel.isAtLeastS()) { + future = startActivityForFuture(*permissions, askTwice = askTwice) + waitForPermissionRequestActivity() + } else { + doAndWaitForWindowTransition { + startActivityForFuture(*permissions, askTwice = askTwice) + } + } // Notification permission prompt is shown first, so get it out of the way clickNotificationPermissionRequestAllowButtonIfAvailable() @@ -711,9 +713,22 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } } + fun startActivityForFuture( + vararg permissions: String?, + askTwice: Boolean, + ): CompletableFuture<Instrumentation.ActivityResult> = + startActivityForFuture( + Intent().apply { + component = + ComponentName(APP_PACKAGE_NAME, "$APP_PACKAGE_NAME.RequestPermissionsActivity") + putExtra("$APP_PACKAGE_NAME.PERMISSIONS", permissions) + putExtra("$APP_PACKAGE_NAME.ASK_TWICE", askTwice) + } + ) + /** - * This method waits for permission controller activity to be in a valid state, the timeout - * is 5 seconds. + * This method waits for permission controller activity to be in a valid state, the timeout is 5 + * seconds. */ fun waitForPermissionRequestActivity() { val requestPermissionIntent = Intent(PackageManager.ACTION_REQUEST_PERMISSIONS) @@ -757,12 +772,12 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { *permissions, askTwice = askTwice, waitForWindowTransition = shouldWaitForWindowTransition, - block = block + block = block, ) assertEquals( "Permission request result had unexpected resultCode:", Activity.RESULT_OK, - result.resultCode + result.resultCode, ) val responseSize: Int = @@ -770,14 +785,14 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { assertEquals( "Permission request result had unexpected number of grant results:", responseSize, - result.resultData!!.getIntArrayExtra("$APP_PACKAGE_NAME.GRANT_RESULTS")!!.size + result.resultData!!.getIntArrayExtra("$APP_PACKAGE_NAME.GRANT_RESULTS")!!.size, ) // Note that the behavior around requesting `null` permissions changed in the platform // in Android U. Currently, null permissions are ignored and left out of the result set. assertTrue( "Permission request result had fewer permissions than request", - permissions.size >= responseSize + permissions.size >= responseSize, ) assertEquals( "Permission request result had unexpected grant results:", @@ -789,7 +804,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { result.resultData!!.getIntArrayExtra("$APP_PACKAGE_NAME.GRANT_RESULTS")!!.map { it == PackageManager.PERMISSION_GRANTED } - ) + ), ) permissionAndExpectedGrantResults.forEach { @@ -808,7 +823,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { permissionAndExpectedGrantResults, askTwice, waitForWindowTransition, - block + block, ) } @@ -819,7 +834,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { !uiDevice.performActionAndWait( { block() }, Until.newWindow(), - NEW_WINDOW_TIMEOUT_MILLIS + NEW_WINDOW_TIMEOUT_MILLIS, ) if (timeoutOccurred) { @@ -832,8 +847,10 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { protected fun findPermissionRequestAllowButton(timeoutMillis: Long = 20000) { if (isAutomotive || isWatch) { - waitFindObject(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)) - .displayId(displayId), timeoutMillis) + waitFindObject( + By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)).displayId(displayId), + timeoutMillis, + ) } else { waitFindObject(By.res(ALLOW_BUTTON).displayId(displayId), timeoutMillis) } @@ -844,8 +861,10 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { isHealthPermission: Boolean = false, ) { if (isAutomotive || isWatch || isHealthPermission) { - click(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)).displayId(displayId), - timeoutMillis) + click( + By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)).displayId(displayId), + timeoutMillis, + ) } else { click(By.res(ALLOW_BUTTON).displayId(displayId), timeoutMillis) } @@ -864,14 +883,16 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { uiDevice.wait( Until.hasObject( By.text(getPermissionControllerString(NOTIF_TEXT, APP_PACKAGE_NAME)) - .displayId(displayId) + .displayId(displayId) ), - 1000 + 1000, ) if (notificationPermissionRequestVisible) { if (isAutomotive) { - click(By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)) - .displayId(displayId)) + click( + By.text(getPermissionControllerString(ALLOW_BUTTON_TEXT)) + .displayId(displayId) + ) } else { click(By.res(ALLOW_BUTTON).displayId(displayId)) } @@ -887,11 +908,15 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { protected fun clickAllowAlwaysInSettings() { if (isAutomotive || isTv || isWatch) { - click(By.text(getPermissionControllerString("app_permission_button_allow_always")) - .displayId(displayId)) + click( + By.text(getPermissionControllerString("app_permission_button_allow_always")) + .displayId(displayId) + ) } else { - click(By.res("com.android.permissioncontroller:id/allow_always_radio_button") - .displayId(displayId)) + click( + By.res("com.android.permissioncontroller:id/allow_always_radio_button") + .displayId(displayId) + ) } } @@ -901,11 +926,14 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { protected fun clicksDenyInSettings() { if (isAutomotive || isWatch) { - click(By.text(getPermissionControllerString("app_permission_button_deny")) - .displayId(displayId)) + click( + By.text(getPermissionControllerString("app_permission_button_deny")) + .displayId(displayId) + ) } else { - click(By.res("com.android.permissioncontroller:id/deny_radio_button") - .displayId(displayId)) + click( + By.res("com.android.permissioncontroller:id/deny_radio_button").displayId(displayId) + ) } } @@ -913,8 +941,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (isAutomotive || isWatch) { waitFindObject( By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)) - .displayId(displayId), - timeoutMillis + .displayId(displayId), + timeoutMillis, ) } else { waitFindObject(By.res(ALLOW_FOREGROUND_BUTTON).displayId(displayId), timeoutMillis) @@ -925,8 +953,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (isAutomotive || isWatch) { click( By.text(getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT)) - .displayId(displayId), - timeoutMillis + .displayId(displayId), + timeoutMillis, ) } else { click(By.res(ALLOW_FOREGROUND_BUTTON).displayId(displayId), timeoutMillis) @@ -1004,7 +1032,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { val nextScreenNode: AccessibilityNodeInfo? = findAccessibilityNodeInfosByTextForSurfaceView( uiAutomation.rootInActiveWindow, - "All the time" + "All the time", ) if (nextScreenNode != null) { clickedOnLink = true @@ -1022,9 +1050,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (!isWatch) { // Check "Allow all" button is visible. val allowAllNode = - uiAutomation.rootInActiveWindow.findAccessibilityNodeInfosByText( - HEALTH_PERMISSION_ALLOW_ALL_PLAIN_TEXT - )[0] + uiAutomation.rootInActiveWindow + .findAccessibilityNodeInfosByText(HEALTH_PERMISSION_ALLOW_ALL_PLAIN_TEXT)[0] assertTrue(allowAllNode.isVisibleToUser) // Select "Heart rate" toggle and click "Allow" button. @@ -1050,7 +1077,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { scrollToBottom() clickAndWaitForWindowTransition( By.text(getPermissionControllerString(DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT)) - .displayId(displayId) + .displayId(displayId) ) } else if (isWatch) { click(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)).displayId(displayId)) @@ -1066,15 +1093,17 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } else { click( By.res("com.android.permissioncontroller:id/permission_deny_dont_ask_again_button") - .displayId(displayId) + .displayId(displayId) ) } } protected fun clickPermissionRequestNoUpgradeAndDontAskAgainButton() { if (isAutomotive || isWatch) { - click(By.text(getPermissionControllerString(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT)) - .displayId(displayId)) + click( + By.text(getPermissionControllerString(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON_TEXT)) + .displayId(displayId) + ) } else { click(By.res(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON).displayId(displayId)) } @@ -1083,13 +1112,14 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { protected fun clickPermissionRationaleContentInAppPermission() { clickAndWaitForWindowTransition( By.text(getPermissionControllerString(APP_PERMISSION_RATIONALE_SUBTITLE_TEXT)) - .displayId(displayId) + .displayId(displayId) ) } protected fun clickPermissionRationaleViewInGrantDialog() { clickAndWaitForWindowTransition( - By.res(GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW).displayId(displayId)) + By.res(GRANT_DIALOG_PERMISSION_RATIONALE_CONTAINER_VIEW).displayId(displayId) + ) } protected fun grantAppPermissionsByUi(vararg permissions: String) { @@ -1109,7 +1139,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { setAppPermissionState( *permissions, state = PermissionState.DENIED, - isLegacyApp = isLegacyApp + isLegacyApp = isLegacyApp, ) } @@ -1151,7 +1181,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { throw e } }, - TIMEOUT_MILLIS + TIMEOUT_MILLIS, ) } @@ -1164,6 +1194,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } } } + protected fun navigateToIndividualPermissionSetting( permission: String, manuallyNavigate: Boolean = false, @@ -1181,8 +1212,10 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { navigateToAppPermissionSettings() val permissionLabel = getPermissionLabel(permission) if (isWatch) { - clickAndWaitForWindowTransition(By.text(permissionLabel).displayId(displayId), - 40_000) + clickAndWaitForWindowTransition( + By.text(permissionLabel).displayId(displayId), + 40_000, + ) } else { clickPermissionControllerUi(By.text(permissionLabel).displayId(displayId)) } @@ -1280,24 +1313,28 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { // won't show an "Ask every time" message !waitFindObject( By.text(getPermissionControllerString("app_permission_button_deny")) - .displayId(displayId) + .displayId(displayId) ) .isChecked } else if (isTv || isWatch) { - !(waitFindObject(By.text(getPermissionControllerString(DENY_BUTTON_TEXT)) - .displayId(displayId)) + !(waitFindObject( + By.text(getPermissionControllerString(DENY_BUTTON_TEXT)) + .displayId(displayId) + ) .isChecked || (!isLegacyApp && hasAskButton(permission) && - waitFindObject(By.text(getPermissionControllerString(ASK_BUTTON_TEXT)) - .displayId(displayId)) + waitFindObject( + By.text(getPermissionControllerString(ASK_BUTTON_TEXT)) + .displayId(displayId) + ) .isChecked)) } else { !(waitFindObject(By.res(DENY_RADIO_BUTTON).displayId(displayId)).isChecked || (!isLegacyApp && hasAskButton(permission) && waitFindObject(By.res(ASK_RADIO_BUTTON).displayId(displayId)) - .isChecked)) + .isChecked)) } var alreadyChecked = false val button = @@ -1309,50 +1346,55 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { PermissionState.ALLOWED -> if (showsForegroundOnlyButton(permission)) { By.text( - getPermissionControllerString( - "app_permission_button_allow_foreground" + getPermissionControllerString( + "app_permission_button_allow_foreground" + ) ) - ).displayId(displayId) + .displayId(displayId) } else { By.text( - getPermissionControllerString("app_permission_button_allow") - ).displayId(displayId) + getPermissionControllerString( + "app_permission_button_allow" + ) + ) + .displayId(displayId) } PermissionState.DENIED -> By.text(getPermissionControllerString("app_permission_button_deny")) - .displayId(displayId) + .displayId(displayId) PermissionState.DENIED_WITH_PREJUDICE -> By.text(getPermissionControllerString("app_permission_button_deny")) - .displayId(displayId) + .displayId(displayId) } } else if (isTv || isWatch) { when (state) { PermissionState.ALLOWED -> if (showsForegroundOnlyButton(permission)) { By.text( - getPermissionControllerString( - ALLOW_FOREGROUND_PREFERENCE_TEXT + getPermissionControllerString( + ALLOW_FOREGROUND_PREFERENCE_TEXT + ) ) - ).displayId(displayId) + .displayId(displayId) } else { byAnyText( getPermissionControllerResString(ALLOW_BUTTON_TEXT), getPermissionControllerResString( ALLOW_ALL_FILES_BUTTON_TEXT - ) + ), ) } PermissionState.DENIED -> if (!isLegacyApp && hasAskButton(permission)) { By.text(getPermissionControllerString(ASK_BUTTON_TEXT)) - .displayId(displayId) + .displayId(displayId) } else { By.text(getPermissionControllerString(DENY_BUTTON_TEXT)) - .displayId(displayId) + .displayId(displayId) } PermissionState.DENIED_WITH_PREJUDICE -> By.text(getPermissionControllerString(DENY_BUTTON_TEXT)) - .displayId(displayId) + .displayId(displayId) } } else { when (state) { @@ -1370,8 +1412,8 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } else { By.res(DENY_RADIO_BUTTON).displayId(displayId) } - PermissionState.DENIED_WITH_PREJUDICE -> By.res(DENY_RADIO_BUTTON) - .displayId(displayId) + PermissionState.DENIED_WITH_PREJUDICE -> + By.res(DENY_RADIO_BUTTON).displayId(displayId) } } ) @@ -1388,8 +1430,11 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (isWatch) { click( By.desc( - getPermissionControllerString("media_confirm_dialog_positive_button") - ).displayId(displayId) + getPermissionControllerString( + "media_confirm_dialog_positive_button" + ) + ) + .displayId(displayId) ) } else { click(By.res(ALERT_DIALOG_OK_BUTTON).displayId(displayId)) @@ -1400,7 +1445,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (isWatch) { waitFindObject( By.text(getPermissionControllerString("old_sdk_deny_warning")) - .displayId(displayId) + .displayId(displayId) ) } else { waitFindObject(By.res(ALERT_DIALOG_MESSAGE).displayId(displayId)) @@ -1421,7 +1466,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { resources.getIdentifier( "com.android.permissioncontroller:string/grant_dialog_button_deny_anyway", null, - null + null, ) val confirmText = resources.getString(confirmTextRes) @@ -1447,8 +1492,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION, - android.Manifest.permission.ACCESS_BACKGROUND_LOCATION, - -> true + android.Manifest.permission.ACCESS_BACKGROUND_LOCATION -> true else -> false } @@ -1459,8 +1503,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { return when (permission) { Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES, - Manifest.permission.READ_MEDIA_VIDEO, - -> true + Manifest.permission.READ_MEDIA_VIDEO -> true else -> false } } @@ -1468,8 +1511,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { private fun showsForegroundOnlyButton(permission: String): Boolean = when (permission) { android.Manifest.permission.CAMERA, - android.Manifest.permission.RECORD_AUDIO, - -> true + android.Manifest.permission.RECORD_AUDIO -> true else -> false } @@ -1515,7 +1557,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { } private fun byTextRes(textRes: Int): BySelector = - By.text(context.getString(textRes)).displayId(displayId) + By.text(context.getString(textRes)).displayId(displayId) private fun byTextStartsWithCaseInsensitive(prefix: String): BySelector = By.text(Pattern.compile("(?i)^${Pattern.quote(prefix)}.*$")).displayId(displayId) @@ -1525,7 +1567,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { assertTrue( "Invalid permission check result: $checkPermissionResult", checkPermissionResult == PackageManager.PERMISSION_GRANTED || - checkPermissionResult == PackageManager.PERMISSION_DENIED + checkPermissionResult == PackageManager.PERMISSION_DENIED, ) if (!expectPermission && checkPermissionResult == PackageManager.PERMISSION_GRANTED) { Assert.fail( @@ -1548,7 +1590,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { component = ComponentName( APP_PACKAGE_NAME, - "$APP_PACKAGE_NAME.CheckCalendarAccessActivity" + "$APP_PACKAGE_NAME.CheckCalendarAccessActivity", ) } ) @@ -1558,7 +1600,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { assertTrue(result.resultData!!.hasExtra("$APP_PACKAGE_NAME.HAS_ACCESS")) assertEquals( expectAccess, - result.resultData!!.getBooleanExtra("$APP_PACKAGE_NAME.HAS_ACCESS", false) + result.resultData!!.getBooleanExtra("$APP_PACKAGE_NAME.HAS_ACCESS", false), ) } |