From 09f2defc1756b05b4094284018e824903c614b7b Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Wed, 19 Oct 2022 12:47:47 +0000 Subject: [DO NOT MERGE] Migrate clock and lock icon to flows Use transition repository to begin replacing doze amount listeners. Since a lot of this code will be in Java, provide an adapter to collect this flows. For testing with Java code, create two test files. The Java one that tests with the flag turned off, and a new Kotlin one with the flag turned on, in order to test with coroutines. Create a base class in order for both to work. Use flags to test in teamfood first. Test: atest LockIconViewControllerTest ClockEventControllerTest LockIconViewControllerWithCoroutinesTest Bug: 195430376 Change-Id: Ia5d440e1a6d7c4f526aa508c89e04b4a01abe35e --- .../com/android/keyguard/ClockEventController.kt | 15 +- .../android/keyguard/LockIconViewController.java | 74 +++- .../src/com/android/systemui/flags/Flags.kt | 6 + .../interactor/KeyguardTransitionInteractor.kt | 15 + .../android/systemui/util/kotlin/JavaAdapter.kt | 40 ++ .../keyguard/LockIconViewControllerBaseTest.java | 225 ++++++++++ .../keyguard/LockIconViewControllerTest.java | 267 ++++++++++++ .../LockIconViewControllerWithCoroutinesTest.kt | 123 ++++++ .../keyguard/LockIconViewControllerTest.java | 476 --------------------- 9 files changed, 744 insertions(+), 497 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt create mode 100644 packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java create mode 100644 packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java create mode 100644 packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt delete mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt index 910955a45f7b..d93363d2c165 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 @@ -217,8 +218,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) + } } } } @@ -261,10 +265,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 c41b7522ce93..fe7c70ae4c7e 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -23,6 +23,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; @@ -44,6 +46,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; @@ -53,6 +56,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; @@ -65,6 +72,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; @@ -101,6 +109,9 @@ public class LockIconViewController extends ViewController 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; @@ -137,6 +148,20 @@ public class LockIconViewController extends ViewController impleme private boolean mDownDetected; private final Rect mSensorTouchLocation = new Rect(); + @VisibleForTesting + final Consumer mDozeTransitionCallback = (TransitionStep step) -> { + mInterpolatedDarkAmount = step.getValue(); + mView.setDozeAmount(step.getValue()); + updateBurnInOffsets(); + }; + + @VisibleForTesting + final Consumer mIsDozingCallback = (Boolean isDozing) -> { + mIsDozing = isDozing; + updateBurnInOffsets(); + updateVisibility(); + }; + @Inject public LockIconViewController( @Nullable LockIconView view, @@ -152,7 +177,10 @@ public class LockIconViewController extends ViewController 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; @@ -166,6 +194,9 @@ public class LockIconViewController extends ViewController 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); @@ -182,6 +213,12 @@ public class LockIconViewController extends ViewController 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 @@ -377,14 +414,17 @@ public class LockIconViewController extends ViewController 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); @@ -425,16 +465,20 @@ public class LockIconViewController extends ViewController 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 4fea51c739ab..ae4c214d7c77 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -120,6 +120,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 = repository.transition(AOD, LOCKSCREEN) + + /** LOCKSCREEN->AOD transition information. */ + val lockscreenToAodTransition: Flow = repository.transition(LOCKSCREEN, AOD) + + /** + * AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <-> + * Lockscreen (0f). + */ + val dozeAmountTransition: Flow = + 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 collectFlow( + view: View, + flow: Flow, + consumer: Consumer, + 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 mAttachCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + + @Captor protected ArgumentCaptor mKeyguardStateCaptor = + ArgumentCaptor.forClass(KeyguardStateController.Callback.class); + protected KeyguardStateController.Callback mKeyguardStateCallback; + + @Captor protected ArgumentCaptor mStatusBarStateCaptor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + protected StatusBarStateController.StateListener mStatusBarStateListener; + + @Captor protected ArgumentCaptor mAuthControllerCallbackCaptor; + protected AuthController.Callback mAuthControllerCallback; + + @Captor protected ArgumentCaptor + mKeyguardUpdateMonitorCallbackCaptor = + ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); + protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; + + @Captor protected ArgumentCaptor 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 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..da40595a4f12 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java @@ -0,0 +1,267 @@ +/* + * 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 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 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 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 udfps = setupUdfps(); + + // WHEN all authenticators are registered + mAuthControllerCallback.onAllAuthenticatorsRegistered(); + 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 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 cefd68dde0a0..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/LockIconViewControllerTest.java +++ /dev/null @@ -1,476 +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 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 mAttachCaptor = - ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - private View.OnAttachStateChangeListener mAttachListener; - - @Captor private ArgumentCaptor mKeyguardStateCaptor = - ArgumentCaptor.forClass(KeyguardStateController.Callback.class); - private KeyguardStateController.Callback mKeyguardStateCallback; - - @Captor private ArgumentCaptor mStatusBarStateCaptor = - ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); - private StatusBarStateController.StateListener mStatusBarStateListener; - - @Captor private ArgumentCaptor mAuthControllerCallbackCaptor; - private AuthController.Callback mAuthControllerCallback; - - @Captor private ArgumentCaptor - mKeyguardUpdateMonitorCallbackCaptor = - ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); - private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; - - @Captor private ArgumentCaptor 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 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 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 udfps = setupUdfps(); - - // WHEN all authenticators are registered - mAuthControllerCallback.onAllAuthenticatorsRegistered(); - 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 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 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(); - } -} -- cgit v1.2.3-59-g8ed1b