[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
This commit is contained in:
Matt Pietal
2022-10-19 12:47:47 +00:00
parent a52c75387d
commit 09f2defc17
9 changed files with 744 additions and 497 deletions

View File

@@ -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)
}
}
}

View File

@@ -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<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;
@@ -137,6 +148,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,
@@ -152,7 +177,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;
@@ -166,6 +194,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);
@@ -182,6 +213,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
@@ -377,14 +414,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);
@@ -425,16 +465,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

View File

@@ -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)

View File

@@ -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,
)
}

View File

@@ -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) } } }
}

View File

@@ -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);
}
}

View File

@@ -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<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();
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);
}
}

View File

@@ -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
}
}

View File

@@ -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<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();
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();
}
}