Merge "[DO NOT MERGE] Migrate clock and lock icon to flows" into tm-qpr-dev
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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) } } }
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user