diff options
9 files changed, 746 insertions, 499 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 386c09575a1a..40a96b060bc0 100644 --- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt +++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt @@ -30,6 +30,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags.DOZING_MIGRATION_1 import com.android.systemui.flags.Flags.REGION_SAMPLING import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor @@ -221,8 +222,11 @@ open class ClockEventController @Inject constructor( disposableHandle = parent.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) - listenForDozeAmount(this) - listenForDozeAmountTransition(this) + if (featureFlags.isEnabled(DOZING_MIGRATION_1)) { + listenForDozeAmountTransition(this) + } else { + listenForDozeAmount(this) + } } } } @@ -265,10 +269,9 @@ open class ClockEventController @Inject constructor( @VisibleForTesting internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job { return scope.launch { - keyguardTransitionInteractor.aodToLockscreenTransition.collect { - // Would eventually run this: - // dozeAmount = it.value - // clock?.animations?.doze(dozeAmount) + keyguardTransitionInteractor.dozeAmountTransition.collect { + dozeAmount = it.value + clock?.animations?.doze(dozeAmount) } } } diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 70758dfec932..8fbbd3840964 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -24,6 +24,8 @@ import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; import static com.android.systemui.classifier.Classifier.LOCK_ICON; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; +import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,6 +48,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import com.android.systemui.Dumpable; @@ -55,6 +58,10 @@ import com.android.systemui.biometrics.AuthRippleController; import com.android.systemui.biometrics.UdfpsController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; @@ -67,6 +74,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.PrintWriter; import java.util.Objects; +import java.util.function.Consumer; import javax.inject.Inject; @@ -103,6 +111,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull private CharSequence mLockedLabel; @NonNull private final VibratorHelper mVibrator; @Nullable private final AuthRippleController mAuthRippleController; + @NonNull private final FeatureFlags mFeatureFlags; + @NonNull private final KeyguardTransitionInteractor mTransitionInteractor; + @NonNull private final KeyguardInteractor mKeyguardInteractor; // Tracks the velocity of a touch to help filter out the touches that move too fast. private VelocityTracker mVelocityTracker; @@ -139,6 +150,20 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private boolean mDownDetected; private final Rect mSensorTouchLocation = new Rect(); + @VisibleForTesting + final Consumer<TransitionStep> mDozeTransitionCallback = (TransitionStep step) -> { + mInterpolatedDarkAmount = step.getValue(); + mView.setDozeAmount(step.getValue()); + updateBurnInOffsets(); + }; + + @VisibleForTesting + final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> { + mIsDozing = isDozing; + updateBurnInOffsets(); + updateVisibility(); + }; + @Inject public LockIconViewController( @Nullable LockIconView view, @@ -154,7 +179,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @NonNull @Main DelayableExecutor executor, @NonNull VibratorHelper vibrator, @Nullable AuthRippleController authRippleController, - @NonNull @Main Resources resources + @NonNull @Main Resources resources, + @NonNull KeyguardTransitionInteractor transitionInteractor, + @NonNull KeyguardInteractor keyguardInteractor, + @NonNull FeatureFlags featureFlags ) { super(view); mStatusBarStateController = statusBarStateController; @@ -168,6 +196,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mExecutor = executor; mVibrator = vibrator; mAuthRippleController = authRippleController; + mTransitionInteractor = transitionInteractor; + mKeyguardInteractor = keyguardInteractor; + mFeatureFlags = featureFlags; mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); @@ -184,6 +215,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme @Override protected void onInit() { mView.setAccessibilityDelegate(mAccessibilityDelegate); + + if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { + collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(), + mDozeTransitionCallback); + collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); + } } @Override @@ -379,14 +416,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme pw.println(" mShowUnlockIcon: " + mShowUnlockIcon); pw.println(" mShowLockIcon: " + mShowLockIcon); pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon); - pw.println(" mIsDozing: " + mIsDozing); - pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); - pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric); - pw.println(" mRunningFPS: " + mRunningFPS); - pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); - pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); - pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); - pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); + pw.println(); + pw.println(" mIsDozing: " + mIsDozing); + pw.println(" isFlagEnabled(DOZING_MIGRATION_1): " + + mFeatureFlags.isEnabled(DOZING_MIGRATION_1)); + pw.println(" mIsBouncerShowing: " + mIsBouncerShowing); + pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric); + pw.println(" mRunningFPS: " + mRunningFPS); + pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen); + pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState)); + pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); + pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); if (mView != null) { mView.dump(pw, args); @@ -427,16 +467,20 @@ public class LockIconViewController extends ViewController<LockIconView> impleme new StatusBarStateController.StateListener() { @Override public void onDozeAmountChanged(float linear, float eased) { - mInterpolatedDarkAmount = eased; - mView.setDozeAmount(eased); - updateBurnInOffsets(); + if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { + mInterpolatedDarkAmount = eased; + mView.setDozeAmount(eased); + updateBurnInOffsets(); + } } @Override public void onDozingChanged(boolean isDozing) { - mIsDozing = isDozing; - updateBurnInOffsets(); - updateVisibility(); + if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) { + mIsDozing = isDozing; + updateBurnInOffsets(); + updateVisibility(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index 3620ba77d434..723b49e477cd 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -118,6 +118,12 @@ object Flags { */ @JvmField val STEP_CLOCK_ANIMATION = UnreleasedFlag(212) + /** + * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository + * will occur in stages. This is one stage of many to come. + */ + @JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213, teamfood = true) + // 300 - power menu // TODO(b/254512600): Tracking Bug @JvmField val POWER_MENU_LITE = ReleasedFlag(300) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index 59bb22786917..7409aec57b4c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -24,6 +24,8 @@ import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionStep import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge /** Encapsulates business-logic related to the keyguard transitions. */ @SysUISingleton @@ -34,4 +36,17 @@ constructor( ) { /** AOD->LOCKSCREEN transition information. */ val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN) + + /** LOCKSCREEN->AOD transition information. */ + val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD) + + /** + * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> + * Lockscreen (0f). + */ + val dozeAmountTransition: Flow<TransitionStep> = + merge( + aodToLockscreenTransition.map { step -> step.copy(value = 1f - step.value) }, + lockscreenToAodTransition, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt new file mode 100644 index 000000000000..9653985cb6e6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.android.systemui.util.kotlin + +import android.view.View +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.lifecycle.repeatWhenAttached +import java.util.function.Consumer +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.collect + +/** + * Collect information for the given [flow], calling [consumer] for each emitted event. Defaults to + * [LifeCycle.State.CREATED] to better align with legacy ViewController usage of attaching listeners + * during onViewAttached() and removing during onViewRemoved() + */ +@JvmOverloads +fun <T> collectFlow( + view: View, + flow: Flow<T>, + consumer: Consumer<T>, + state: Lifecycle.State = Lifecycle.State.CREATED, +) { + view.repeatWhenAttached { repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } } } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java new file mode 100644 index 000000000000..ae8f419d4e64 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.drawable.AnimatedStateListDrawable; +import android.util.Pair; +import android.view.View; +import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; + +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.doze.util.BurnInHelperKt; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.After; +import org.junit.Before; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.MockitoSession; +import org.mockito.quality.Strictness; + +public class LockIconViewControllerBaseTest extends SysuiTestCase { + protected static final String UNLOCKED_LABEL = "unlocked"; + protected static final int PADDING = 10; + + protected MockitoSession mStaticMockSession; + + protected @Mock LockIconView mLockIconView; + protected @Mock AnimatedStateListDrawable mIconDrawable; + protected @Mock Context mContext; + protected @Mock Resources mResources; + protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager; + protected @Mock StatusBarStateController mStatusBarStateController; + protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; + protected @Mock KeyguardViewController mKeyguardViewController; + protected @Mock KeyguardStateController mKeyguardStateController; + protected @Mock FalsingManager mFalsingManager; + protected @Mock AuthController mAuthController; + protected @Mock DumpManager mDumpManager; + protected @Mock AccessibilityManager mAccessibilityManager; + protected @Mock ConfigurationController mConfigurationController; + protected @Mock VibratorHelper mVibrator; + protected @Mock AuthRippleController mAuthRippleController; + protected @Mock FeatureFlags mFeatureFlags; + protected @Mock KeyguardTransitionRepository mTransitionRepository; + protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); + + protected LockIconViewController mUnderTest; + + // Capture listeners so that they can be used to send events + @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + + @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor = + ArgumentCaptor.forClass(KeyguardStateController.Callback.class); + protected KeyguardStateController.Callback mKeyguardStateCallback; + + @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + protected StatusBarStateController.StateListener mStatusBarStateListener; + + @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; + protected AuthController.Callback mAuthControllerCallback; + + @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback> + mKeyguardUpdateMonitorCallbackCaptor = + ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); + protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; + + @Captor protected ArgumentCaptor<Point> mPointCaptor; + + @Before + public void setUp() throws Exception { + mStaticMockSession = mockitoSession() + .mockStatic(BurnInHelperKt.class) + .strictness(Strictness.LENIENT) + .startMocking(); + MockitoAnnotations.initMocks(this); + + setupLockIconViewMocks(); + when(mContext.getResources()).thenReturn(mResources); + when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager); + Rect windowBounds = new Rect(0, 0, 800, 1200); + when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds); + when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL); + when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable); + when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING); + when(mAuthController.getScaleFactor()).thenReturn(1f); + + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + + mUnderTest = new LockIconViewController( + mLockIconView, + mStatusBarStateController, + mKeyguardUpdateMonitor, + mKeyguardViewController, + mKeyguardStateController, + mFalsingManager, + mAuthController, + mDumpManager, + mAccessibilityManager, + mConfigurationController, + mDelayableExecutor, + mVibrator, + mAuthRippleController, + mResources, + new KeyguardTransitionInteractor(mTransitionRepository), + new KeyguardInteractor(new FakeKeyguardRepository()), + mFeatureFlags + ); + } + + @After + public void tearDown() { + mStaticMockSession.finishMocking(); + } + + protected Pair<Float, Point> setupUdfps() { + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); + final Point udfpsLocation = new Point(50, 75); + final float radius = 33f; + when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation); + when(mAuthController.getUdfpsRadius()).thenReturn(radius); + + return new Pair(radius, udfpsLocation); + } + + protected void setupShowLockIcon() { + when(mKeyguardStateController.isShowing()).thenReturn(true); + when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); + when(mStatusBarStateController.isDozing()).thenReturn(false); + when(mStatusBarStateController.getDozeAmount()).thenReturn(0f); + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); + when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false); + } + + protected void captureAuthControllerCallback() { + verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); + mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); + } + + protected void captureKeyguardStateCallback() { + verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture()); + mKeyguardStateCallback = mKeyguardStateCaptor.getValue(); + } + + protected void captureStatusBarStateListener() { + verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture()); + mStatusBarStateListener = mStatusBarStateCaptor.getValue(); + } + + protected void captureKeyguardUpdateMonitorCallback() { + verify(mKeyguardUpdateMonitor).registerCallback( + mKeyguardUpdateMonitorCallbackCaptor.capture()); + mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); + } + + protected void setupLockIconViewMocks() { + when(mLockIconView.getResources()).thenReturn(mResources); + when(mLockIconView.getContext()).thenReturn(mContext); + } + + protected void resetLockIconView() { + reset(mLockIconView); + setupLockIconViewMocks(); + } + + protected void init(boolean useMigrationFlag) { + when(mFeatureFlags.isEnabled(DOZING_MIGRATION_1)).thenReturn(useMigrationFlag); + mUnderTest.init(); + + verify(mLockIconView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture()); + mAttachCaptor.getValue().onViewAttachedToWindow(mLockIconView); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java new file mode 100644 index 000000000000..f4c2284de2d3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + +import static com.android.keyguard.LockIconView.ICON_LOCK; +import static com.android.keyguard.LockIconView.ICON_UNLOCK; + +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.graphics.Point; +import android.hardware.biometrics.BiometricSourceType; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.Pair; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.doze.util.BurnInHelperKt; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class LockIconViewControllerTest extends LockIconViewControllerBaseTest { + + @Test + public void testUpdateFingerprintLocationOnInit() { + // GIVEN fp sensor location is available pre-attached + Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location + + // WHEN lock icon view controller is initialized and attached + init(/* useMigrationFlag= */false); + + // THEN lock icon view location is updated to the udfps location with UDFPS radius + verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), + eq(PADDING)); + } + + @Test + public void testUpdatePaddingBasedOnResolutionScale() { + // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5 + Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location + when(mAuthController.getScaleFactor()).thenReturn(5f); + + // WHEN lock icon view controller is initialized and attached + init(/* useMigrationFlag= */false); + + // THEN lock icon view location is updated with the scaled radius + verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), + eq(PADDING * 5)); + } + + @Test + public void testUpdateLockIconLocationOnAuthenticatorsRegistered() { + // GIVEN fp sensor location is not available pre-init + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); + when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); + init(/* useMigrationFlag= */false); + resetLockIconView(); // reset any method call counts for when we verify method calls later + + // GIVEN fp sensor location is available post-attached + captureAuthControllerCallback(); + Pair<Float, Point> udfps = setupUdfps(); + + // WHEN all authenticators are registered + mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); + mDelayableExecutor.runAllReady(); + + // THEN lock icon view location is updated with the same coordinates as auth controller vals + verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), + eq(PADDING)); + } + + @Test + public void testUpdateLockIconLocationOnUdfpsLocationChanged() { + // GIVEN fp sensor location is not available pre-init + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); + when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); + init(/* useMigrationFlag= */false); + resetLockIconView(); // reset any method call counts for when we verify method calls later + + // GIVEN fp sensor location is available post-attached + captureAuthControllerCallback(); + Pair<Float, Point> udfps = setupUdfps(); + + // WHEN udfps location changes + mAuthControllerCallback.onUdfpsLocationChanged(); + mDelayableExecutor.runAllReady(); + + // THEN lock icon view location is updated with the same coordinates as auth controller vals + verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), + eq(PADDING)); + } + + @Test + public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() { + // GIVEN Udpfs sensor location is available + setupUdfps(); + + // WHEN the view is attached + init(/* useMigrationFlag= */false); + + // THEN the lock icon view background should be enabled + verify(mLockIconView).setUseBackground(true); + } + + @Test + public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() { + // GIVEN Udfps sensor location is not supported + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); + + // WHEN the view is attached + init(/* useMigrationFlag= */false); + + // THEN the lock icon view background should be disabled + verify(mLockIconView).setUseBackground(false); + } + + @Test + public void testUnlockIconShows_biometricUnlockedTrue() { + // GIVEN UDFPS sensor location is available + setupUdfps(); + + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureKeyguardUpdateMonitorCallback(); + + // GIVEN user has unlocked with a biometric auth (ie: face auth) + when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); + reset(mLockIconView); + + // WHEN face auth's biometric running state changes + mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, + BiometricSourceType.FACE); + + // THEN the unlock icon is shown + verify(mLockIconView).setContentDescription(UNLOCKED_LABEL); + } + + @Test + public void testLockIconStartState() { + // GIVEN lock icon state + setupShowLockIcon(); + + // WHEN lock icon controller is initialized + init(/* useMigrationFlag= */false); + + // THEN the lock icon should show + verify(mLockIconView).updateIcon(ICON_LOCK, false); + } + + @Test + public void testLockIcon_updateToUnlock() { + // GIVEN starting state for the lock icon + setupShowLockIcon(); + + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureKeyguardStateCallback(); + reset(mLockIconView); + + // WHEN the unlocked state changes to canDismissLockScreen=true + when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); + mKeyguardStateCallback.onUnlockedChanged(); + + // THEN the unlock should show + verify(mLockIconView).updateIcon(ICON_UNLOCK, false); + } + + @Test + public void testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() { + // GIVEN udfps not enrolled + setupUdfps(); + when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false); + + // GIVEN starting state for the lock icon + setupShowLockIcon(); + + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureStatusBarStateListener(); + reset(mLockIconView); + + // WHEN the dozing state changes + mStatusBarStateListener.onDozingChanged(true /* isDozing */); + + // THEN the icon is cleared + verify(mLockIconView).clearIcon(); + } + + @Test + public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() { + // GIVEN udfps enrolled + setupUdfps(); + when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); + + // GIVEN starting state for the lock icon + setupShowLockIcon(); + + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */false); + captureStatusBarStateListener(); + reset(mLockIconView); + + // WHEN the dozing state changes + mStatusBarStateListener.onDozingChanged(true /* isDozing */); + + // THEN the AOD lock icon should show + verify(mLockIconView).updateIcon(ICON_LOCK, true); + } + + @Test + public void testBurnInOffsetsUpdated_onDozeAmountChanged() { + // GIVEN udfps enrolled + setupUdfps(); + when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); + + // GIVEN burn-in offset = 5 + int burnInOffset = 5; + when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset); + + // GIVEN starting state for the lock icon (keyguard) + setupShowLockIcon(); + init(/* useMigrationFlag= */false); + captureStatusBarStateListener(); + reset(mLockIconView); + + // WHEN dozing updates + mStatusBarStateListener.onDozingChanged(true /* isDozing */); + mStatusBarStateListener.onDozeAmountChanged(1f, 1f); + + // THEN the view's translation is updated to use the AoD burn-in offsets + verify(mLockIconView).setTranslationY(burnInOffset); + verify(mLockIconView).setTranslationX(burnInOffset); + reset(mLockIconView); + + // WHEN the device is no longer dozing + mStatusBarStateListener.onDozingChanged(false /* isDozing */); + mStatusBarStateListener.onDozeAmountChanged(0f, 0f); + + // THEN the view is updated to NO translation (no burn-in offsets anymore) + verify(mLockIconView).setTranslationY(0); + verify(mLockIconView).setTranslationX(0); + + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt new file mode 100644 index 000000000000..d2c54b4cc0e7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.keyguard.LockIconView.ICON_LOCK +import com.android.systemui.doze.util.getBurnInOffset +import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN +import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED +import com.android.systemui.keyguard.shared.model.TransitionStep +import com.android.systemui.util.mockito.whenever +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyBoolean +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.reset +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class LockIconViewControllerWithCoroutinesTest : LockIconViewControllerBaseTest() { + + /** After migration, replaces LockIconViewControllerTest version */ + @Test + fun testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() = + runBlocking(IMMEDIATE) { + // GIVEN udfps not enrolled + setupUdfps() + whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false) + + // GIVEN starting state for the lock icon + setupShowLockIcon() + + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */ true) + reset(mLockIconView) + + // WHEN the dozing state changes + mUnderTest.mIsDozingCallback.accept(true) + + // THEN the icon is cleared + verify(mLockIconView).clearIcon() + } + + /** After migration, replaces LockIconViewControllerTest version */ + @Test + fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() = + runBlocking(IMMEDIATE) { + // GIVEN udfps enrolled + setupUdfps() + whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true) + + // GIVEN starting state for the lock icon + setupShowLockIcon() + + // GIVEN lock icon controller is initialized and view is attached + init(/* useMigrationFlag= */ true) + reset(mLockIconView) + + // WHEN the dozing state changes + mUnderTest.mIsDozingCallback.accept(true) + + // THEN the AOD lock icon should show + verify(mLockIconView).updateIcon(ICON_LOCK, true) + } + + /** After migration, replaces LockIconViewControllerTest version */ + @Test + fun testBurnInOffsetsUpdated_onDozeAmountChanged() = + runBlocking(IMMEDIATE) { + // GIVEN udfps enrolled + setupUdfps() + whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true) + + // GIVEN burn-in offset = 5 + val burnInOffset = 5 + whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset) + + // GIVEN starting state for the lock icon (keyguard) + setupShowLockIcon() + init(/* useMigrationFlag= */ true) + reset(mLockIconView) + + // WHEN dozing updates + mUnderTest.mIsDozingCallback.accept(true) + mUnderTest.mDozeTransitionCallback.accept(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED)) + + // THEN the view's translation is updated to use the AoD burn-in offsets + verify(mLockIconView).setTranslationY(burnInOffset.toFloat()) + verify(mLockIconView).setTranslationX(burnInOffset.toFloat()) + reset(mLockIconView) + + // WHEN the device is no longer dozing + mUnderTest.mIsDozingCallback.accept(false) + mUnderTest.mDozeTransitionCallback.accept(TransitionStep(AOD, LOCKSCREEN, 0f, FINISHED)) + + // THEN the view is updated to NO translation (no burn-in offsets anymore) + verify(mLockIconView).setTranslationY(0f) + verify(mLockIconView).setTranslationX(0f) + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java deleted file mode 100644 index 27a5190367a8..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.keyguard; - -import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; -import static com.android.keyguard.LockIconView.ICON_LOCK; -import static com.android.keyguard.LockIconView.ICON_UNLOCK; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.anyBoolean; -import static org.mockito.Mockito.anyInt; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.drawable.AnimatedStateListDrawable; -import android.hardware.biometrics.BiometricSourceType; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.util.Pair; -import android.view.View; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityManager; - -import androidx.test.filters.SmallTest; - -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.keyguard.KeyguardViewController; -import com.android.keyguard.LockIconView; -import com.android.keyguard.LockIconViewController; -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.doze.util.BurnInHelperKt; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Answers; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.MockitoSession; -import org.mockito.quality.Strictness; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class LockIconViewControllerTest extends SysuiTestCase { - private static final String UNLOCKED_LABEL = "unlocked"; - private static final int PADDING = 10; - - private MockitoSession mStaticMockSession; - - private @Mock LockIconView mLockIconView; - private @Mock AnimatedStateListDrawable mIconDrawable; - private @Mock Context mContext; - private @Mock Resources mResources; - private @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager; - private @Mock StatusBarStateController mStatusBarStateController; - private @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private @Mock KeyguardViewController mKeyguardViewController; - private @Mock KeyguardStateController mKeyguardStateController; - private @Mock FalsingManager mFalsingManager; - private @Mock AuthController mAuthController; - private @Mock DumpManager mDumpManager; - private @Mock AccessibilityManager mAccessibilityManager; - private @Mock ConfigurationController mConfigurationController; - private @Mock VibratorHelper mVibrator; - private @Mock AuthRippleController mAuthRippleController; - private FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock()); - - private LockIconViewController mLockIconViewController; - - // Capture listeners so that they can be used to send events - @Captor private ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = - ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - private View.OnAttachStateChangeListener mAttachListener; - - @Captor private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor = - ArgumentCaptor.forClass(KeyguardStateController.Callback.class); - private KeyguardStateController.Callback mKeyguardStateCallback; - - @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor = - ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); - private StatusBarStateController.StateListener mStatusBarStateListener; - - @Captor private ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor; - private AuthController.Callback mAuthControllerCallback; - - @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> - mKeyguardUpdateMonitorCallbackCaptor = - ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); - private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; - - @Captor private ArgumentCaptor<Point> mPointCaptor; - - @Before - public void setUp() throws Exception { - mStaticMockSession = mockitoSession() - .mockStatic(BurnInHelperKt.class) - .strictness(Strictness.LENIENT) - .startMocking(); - MockitoAnnotations.initMocks(this); - - setupLockIconViewMocks(); - when(mContext.getResources()).thenReturn(mResources); - when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager); - Rect windowBounds = new Rect(0, 0, 800, 1200); - when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds); - when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL); - when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable); - when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING); - when(mAuthController.getScaleFactor()).thenReturn(1f); - - when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - - mLockIconViewController = new LockIconViewController( - mLockIconView, - mStatusBarStateController, - mKeyguardUpdateMonitor, - mKeyguardViewController, - mKeyguardStateController, - mFalsingManager, - mAuthController, - mDumpManager, - mAccessibilityManager, - mConfigurationController, - mDelayableExecutor, - mVibrator, - mAuthRippleController, - mResources - ); - } - - @After - public void tearDown() { - mStaticMockSession.finishMocking(); - } - - @Test - public void testUpdateFingerprintLocationOnInit() { - // GIVEN fp sensor location is available pre-attached - Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location - - // WHEN lock icon view controller is initialized and attached - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN lock icon view location is updated to the udfps location with UDFPS radius - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testUpdatePaddingBasedOnResolutionScale() { - // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5 - Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location - when(mAuthController.getScaleFactor()).thenReturn(5f); - - // WHEN lock icon view controller is initialized and attached - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN lock icon view location is updated with the scaled radius - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING * 5)); - } - - @Test - public void testUpdateLockIconLocationOnAuthenticatorsRegistered() { - // GIVEN fp sensor location is not available pre-init - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - resetLockIconView(); // reset any method call counts for when we verify method calls later - - // GIVEN fp sensor location is available post-attached - captureAuthControllerCallback(); - Pair<Float, Point> udfps = setupUdfps(); - - // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT); - mDelayableExecutor.runAllReady(); - - // THEN lock icon view location is updated with the same coordinates as auth controller vals - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testUpdateLockIconLocationOnUdfpsLocationChanged() { - // GIVEN fp sensor location is not available pre-init - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - when(mAuthController.getFingerprintSensorLocation()).thenReturn(null); - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - resetLockIconView(); // reset any method call counts for when we verify method calls later - - // GIVEN fp sensor location is available post-attached - captureAuthControllerCallback(); - Pair<Float, Point> udfps = setupUdfps(); - - // WHEN udfps location changes - mAuthControllerCallback.onUdfpsLocationChanged(); - mDelayableExecutor.runAllReady(); - - // THEN lock icon view location is updated with the same coordinates as auth controller vals - verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first), - eq(PADDING)); - } - - @Test - public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() { - // GIVEN Udpfs sensor location is available - setupUdfps(); - - mLockIconViewController.init(); - captureAttachListener(); - - // WHEN the view is attached - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN the lock icon view background should be enabled - verify(mLockIconView).setUseBackground(true); - } - - @Test - public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() { - // GIVEN Udfps sensor location is not supported - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); - - mLockIconViewController.init(); - captureAttachListener(); - - // WHEN the view is attached - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN the lock icon view background should be disabled - verify(mLockIconView).setUseBackground(false); - } - - @Test - public void testUnlockIconShows_biometricUnlockedTrue() { - // GIVEN UDFPS sensor location is available - setupUdfps(); - - // GIVEN lock icon controller is initialized and view is attached - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - captureKeyguardUpdateMonitorCallback(); - - // GIVEN user has unlocked with a biometric auth (ie: face auth) - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true); - reset(mLockIconView); - - // WHEN face auth's biometric running state changes - mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false, - BiometricSourceType.FACE); - - // THEN the unlock icon is shown - verify(mLockIconView).setContentDescription(UNLOCKED_LABEL); - } - - @Test - public void testLockIconStartState() { - // GIVEN lock icon state - setupShowLockIcon(); - - // WHEN lock icon controller is initialized - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - - // THEN the lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, false); - } - - @Test - public void testLockIcon_updateToUnlock() { - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - captureKeyguardStateCallback(); - reset(mLockIconView); - - // WHEN the unlocked state changes to canDismissLockScreen=true - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - mKeyguardStateCallback.onUnlockedChanged(); - - // THEN the unlock should show - verify(mLockIconView).updateIcon(ICON_UNLOCK, false); - } - - @Test - public void testLockIcon_clearsIconOnAod_whenUdfpsNotEnrolled() { - // GIVEN udfps not enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false); - - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - - // THEN the icon is cleared - verify(mLockIconView).clearIcon(); - } - - @Test - public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() { - // GIVEN udfps enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - // GIVEN starting state for the lock icon - setupShowLockIcon(); - - // GIVEN lock icon controller is initialized and view is attached - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN the dozing state changes - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - - // THEN the AOD lock icon should show - verify(mLockIconView).updateIcon(ICON_LOCK, true); - } - - @Test - public void testBurnInOffsetsUpdated_onDozeAmountChanged() { - // GIVEN udfps enrolled - setupUdfps(); - when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true); - - // GIVEN burn-in offset = 5 - int burnInOffset = 5; - when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset); - - // GIVEN starting state for the lock icon (keyguard) - setupShowLockIcon(); - mLockIconViewController.init(); - captureAttachListener(); - mAttachListener.onViewAttachedToWindow(mLockIconView); - captureStatusBarStateListener(); - reset(mLockIconView); - - // WHEN dozing updates - mStatusBarStateListener.onDozingChanged(true /* isDozing */); - mStatusBarStateListener.onDozeAmountChanged(1f, 1f); - - // THEN the view's translation is updated to use the AoD burn-in offsets - verify(mLockIconView).setTranslationY(burnInOffset); - verify(mLockIconView).setTranslationX(burnInOffset); - reset(mLockIconView); - - // WHEN the device is no longer dozing - mStatusBarStateListener.onDozingChanged(false /* isDozing */); - mStatusBarStateListener.onDozeAmountChanged(0f, 0f); - - // THEN the view is updated to NO translation (no burn-in offsets anymore) - verify(mLockIconView).setTranslationY(0); - verify(mLockIconView).setTranslationX(0); - - } - private Pair<Float, Point> setupUdfps() { - when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); - final Point udfpsLocation = new Point(50, 75); - final float radius = 33f; - when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation); - when(mAuthController.getUdfpsRadius()).thenReturn(radius); - - return new Pair(radius, udfpsLocation); - } - - private void setupShowLockIcon() { - when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false); - when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarStateController.getDozeAmount()).thenReturn(0f); - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD); - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false); - } - - private void captureAuthControllerCallback() { - verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture()); - mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue(); - } - - private void captureAttachListener() { - verify(mLockIconView).addOnAttachStateChangeListener(mAttachCaptor.capture()); - mAttachListener = mAttachCaptor.getValue(); - } - - private void captureKeyguardStateCallback() { - verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture()); - mKeyguardStateCallback = mKeyguardStateCaptor.getValue(); - } - - private void captureStatusBarStateListener() { - verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture()); - mStatusBarStateListener = mStatusBarStateCaptor.getValue(); - } - - private void captureKeyguardUpdateMonitorCallback() { - verify(mKeyguardUpdateMonitor).registerCallback( - mKeyguardUpdateMonitorCallbackCaptor.capture()); - mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); - } - - private void setupLockIconViewMocks() { - when(mLockIconView.getResources()).thenReturn(mResources); - when(mLockIconView.getContext()).thenReturn(mContext); - } - - private void resetLockIconView() { - reset(mLockIconView); - setupLockIconViewMocks(); - } -} |