From 6a02b08b98c2e549d21f3096825fa7ad127c2383 Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Thu, 29 Jun 2023 13:26:39 -0700 Subject: [PATCH] Prevent lock icon a11y when unlocking. When we are unlocking the lock icon is reading out a content description, indicating that it is locked. In consequence, talkback will often read "device locked. device unlocked" when unlocking the phone when talk back is on. A lot of the keyguard states don't seem viable to use here, but I found that the bouncer indicates that it is animating away here. Fixes: 284902478 Test: with talk back on, open notification in LS and authenticate Test: with talk back on, swipe up bouncer and authenticate Change-Id: I8ae54095c55a5ca0816878f27b368c3a9abd73e3 --- .../keyguard/LockIconViewController.java | 14 +++- .../LockIconViewControllerBaseTest.java | 5 +- .../keyguard/LockIconViewControllerTest.java | 72 +++++++++++++++++++ 3 files changed, 88 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 239a0cc01c457..eeecd66717621 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -56,6 +56,7 @@ import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.biometrics.UdfpsController; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; @@ -112,6 +113,7 @@ public class LockIconViewController extends ViewController impleme @NonNull private final VibratorHelper mVibrator; @Nullable private final AuthRippleController mAuthRippleController; @NonNull private final FeatureFlags mFeatureFlags; + @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor; @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; @NonNull private final KeyguardInteractor mKeyguardInteractor; @@ -180,7 +182,8 @@ public class LockIconViewController extends ViewController impleme @NonNull @Main Resources resources, @NonNull KeyguardTransitionInteractor transitionInteractor, @NonNull KeyguardInteractor keyguardInteractor, - @NonNull FeatureFlags featureFlags + @NonNull FeatureFlags featureFlags, + PrimaryBouncerInteractor primaryBouncerInteractor ) { super(view); mStatusBarStateController = statusBarStateController; @@ -197,6 +200,7 @@ public class LockIconViewController extends ViewController impleme mTransitionInteractor = transitionInteractor; mKeyguardInteractor = keyguardInteractor; mFeatureFlags = featureFlags; + mPrimaryBouncerInteractor = primaryBouncerInteractor; mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); @@ -325,8 +329,14 @@ public class LockIconViewController extends ViewController impleme mView.setContentDescription(null); } + boolean accessibilityEnabled = + !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser(); + mView.setImportantForAccessibility( + accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES + : View.IMPORTANT_FOR_ACCESSIBILITY_NO); + if (!Objects.equals(prevContentDescription, mView.getContentDescription()) - && mView.getContentDescription() != null && mView.isVisibleToUser()) { + && mView.getContentDescription() != null && accessibilityEnabled) { mView.announceForAccessibility(mView.getContentDescription()); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index c88c4d65f4128..cc42a9030770d 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -41,6 +41,7 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.AuthController; import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor; import com.android.systemui.doze.util.BurnInHelperKt; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; @@ -92,6 +93,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { protected @Mock KeyguardTransitionRepository mTransitionRepository; protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); protected FakeFeatureFlags mFeatureFlags; + protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor; protected LockIconViewController mUnderTest; @@ -161,7 +163,8 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { new KeyguardTransitionInteractor(mTransitionRepository, TestScopeProvider.getTestScope().getBackgroundScope()), KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(), - mFeatureFlags + mFeatureFlags, + mPrimaryBouncerInteractor ); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java index b62875988b2e1..ed6a891a6094c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java @@ -33,6 +33,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.Pair; +import android.view.View; import androidx.test.filters.SmallTest; @@ -267,4 +268,75 @@ public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { // THEN the lock icon is shown verify(mLockIconView).setContentDescription(LOCKED_LABEL); } + + @Test + public void lockIconAccessibility_notVisibleToUser() { + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureKeyguardStateCallback(); + captureKeyguardUpdateMonitorCallback(); + + // GIVEN user has unlocked with a biometric auth (ie: face auth) + // and biometric running state changes + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); + mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, + BiometricSourceType.FACE); + reset(mLockIconView); + when(mLockIconView.isVisibleToUser()).thenReturn(false); + + // WHEN the unlocked state changes + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); + mKeyguardStateCallback.onUnlockedChanged(); + + // THEN the lock icon is shown + verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } + + @Test + public void lockIconAccessibility_bouncerAnimatingAway() { + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureKeyguardStateCallback(); + captureKeyguardUpdateMonitorCallback(); + + // GIVEN user has unlocked with a biometric auth (ie: face auth) + // and biometric running state changes + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); + mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, + BiometricSourceType.FACE); + reset(mLockIconView); + when(mLockIconView.isVisibleToUser()).thenReturn(true); + when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true); + + // WHEN the unlocked state changes + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); + mKeyguardStateCallback.onUnlockedChanged(); + + // THEN the lock icon is shown + verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO); + } + + @Test + public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() { + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureKeyguardStateCallback(); + captureKeyguardUpdateMonitorCallback(); + + // GIVEN user has unlocked with a biometric auth (ie: face auth) + // and biometric running state changes + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); + mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, + BiometricSourceType.FACE); + reset(mLockIconView); + when(mLockIconView.isVisibleToUser()).thenReturn(true); + when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false); + + // WHEN the unlocked state changes + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false); + mKeyguardStateCallback.onUnlockedChanged(); + + // THEN the lock icon is shown + verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES); + } }