Merge "[DO NOT MERGE] Migrate clock and lock icon to flows" into tm-qpr-dev

This commit is contained in:
Matt Pietal
2022-10-25 11:40:19 +00:00
committed by Android (Google) Code Review
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
@@ -221,8 +222,11 @@ open class ClockEventController @Inject constructor(
disposableHandle = parent.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
listenForDozing(this)
listenForDozeAmount(this)
listenForDozeAmountTransition(this)
if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
listenForDozeAmountTransition(this)
} else {
listenForDozeAmount(this)
}
}
}
}
@@ -265,10 +269,9 @@ open class ClockEventController @Inject constructor(
@VisibleForTesting
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
return scope.launch {
keyguardTransitionInteractor.aodToLockscreenTransition.collect {
// Would eventually run this:
// dozeAmount = it.value
// clock?.animations?.doze(dozeAmount)
keyguardTransitionInteractor.dozeAmountTransition.collect {
dozeAmount = it.value
clock?.animations?.doze(dozeAmount)
}
}
}

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

@@ -118,6 +118,12 @@ object Flags {
*/
@JvmField val STEP_CLOCK_ANIMATION = UnreleasedFlag(212)
/**
* Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
* will occur in stages. This is one stage of many to come.
*/
@JvmField val DOZING_MIGRATION_1 = UnreleasedFlag(213, teamfood = true)
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = ReleasedFlag(300)

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