[DO NOT MERGE] Migrate clock and lock icon to flows
Use transition repository to begin replacing doze amount listeners. Since a lot of this code will be in Java, provide an adapter to collect this flows. For testing with Java code, create two test files. The Java one that tests with the flag turned off, and a new Kotlin one with the flag turned on, in order to test with coroutines. Create a base class in order for both to work. Use flags to test in teamfood first. Test: atest LockIconViewControllerTest ClockEventControllerTest LockIconViewControllerWithCoroutinesTest Bug: 195430376 Change-Id: Ia5d440e1a6d7c4f526aa508c89e04b4a01abe35e
This commit is contained in:
@@ -30,6 +30,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher
|
|||||||
import com.android.systemui.dagger.qualifiers.Background
|
import com.android.systemui.dagger.qualifiers.Background
|
||||||
import com.android.systemui.dagger.qualifiers.Main
|
import com.android.systemui.dagger.qualifiers.Main
|
||||||
import com.android.systemui.flags.FeatureFlags
|
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.flags.Flags.REGION_SAMPLING
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
|
||||||
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
|
||||||
@@ -217,8 +218,11 @@ open class ClockEventController @Inject constructor(
|
|||||||
disposableHandle = parent.repeatWhenAttached {
|
disposableHandle = parent.repeatWhenAttached {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
listenForDozing(this)
|
listenForDozing(this)
|
||||||
listenForDozeAmount(this)
|
if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
|
||||||
listenForDozeAmountTransition(this)
|
listenForDozeAmountTransition(this)
|
||||||
|
} else {
|
||||||
|
listenForDozeAmount(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,10 +265,9 @@ open class ClockEventController @Inject constructor(
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
|
internal fun listenForDozeAmountTransition(scope: CoroutineScope): Job {
|
||||||
return scope.launch {
|
return scope.launch {
|
||||||
keyguardTransitionInteractor.aodToLockscreenTransition.collect {
|
keyguardTransitionInteractor.dozeAmountTransition.collect {
|
||||||
// Would eventually run this:
|
dozeAmount = it.value
|
||||||
// dozeAmount = it.value
|
clock?.animations?.doze(dozeAmount)
|
||||||
// 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.keyguard.LockIconView.ICON_UNLOCK;
|
||||||
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
|
import static com.android.systemui.classifier.Classifier.LOCK_ICON;
|
||||||
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
|
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.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
@@ -44,6 +46,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
|||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||||
|
|
||||||
import com.android.systemui.Dumpable;
|
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.biometrics.UdfpsController;
|
||||||
import com.android.systemui.dagger.qualifiers.Main;
|
import com.android.systemui.dagger.qualifiers.Main;
|
||||||
import com.android.systemui.dump.DumpManager;
|
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.FalsingManager;
|
||||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||||
import com.android.systemui.statusbar.StatusBarState;
|
import com.android.systemui.statusbar.StatusBarState;
|
||||||
@@ -65,6 +72,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor;
|
|||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@@ -101,6 +109,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
@NonNull private CharSequence mLockedLabel;
|
@NonNull private CharSequence mLockedLabel;
|
||||||
@NonNull private final VibratorHelper mVibrator;
|
@NonNull private final VibratorHelper mVibrator;
|
||||||
@Nullable private final AuthRippleController mAuthRippleController;
|
@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.
|
// Tracks the velocity of a touch to help filter out the touches that move too fast.
|
||||||
private VelocityTracker mVelocityTracker;
|
private VelocityTracker mVelocityTracker;
|
||||||
@@ -137,6 +148,20 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
private boolean mDownDetected;
|
private boolean mDownDetected;
|
||||||
private final Rect mSensorTouchLocation = new Rect();
|
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
|
@Inject
|
||||||
public LockIconViewController(
|
public LockIconViewController(
|
||||||
@Nullable LockIconView view,
|
@Nullable LockIconView view,
|
||||||
@@ -152,7 +177,10 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
@NonNull @Main DelayableExecutor executor,
|
@NonNull @Main DelayableExecutor executor,
|
||||||
@NonNull VibratorHelper vibrator,
|
@NonNull VibratorHelper vibrator,
|
||||||
@Nullable AuthRippleController authRippleController,
|
@Nullable AuthRippleController authRippleController,
|
||||||
@NonNull @Main Resources resources
|
@NonNull @Main Resources resources,
|
||||||
|
@NonNull KeyguardTransitionInteractor transitionInteractor,
|
||||||
|
@NonNull KeyguardInteractor keyguardInteractor,
|
||||||
|
@NonNull FeatureFlags featureFlags
|
||||||
) {
|
) {
|
||||||
super(view);
|
super(view);
|
||||||
mStatusBarStateController = statusBarStateController;
|
mStatusBarStateController = statusBarStateController;
|
||||||
@@ -166,6 +194,9 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
mExecutor = executor;
|
mExecutor = executor;
|
||||||
mVibrator = vibrator;
|
mVibrator = vibrator;
|
||||||
mAuthRippleController = authRippleController;
|
mAuthRippleController = authRippleController;
|
||||||
|
mTransitionInteractor = transitionInteractor;
|
||||||
|
mKeyguardInteractor = keyguardInteractor;
|
||||||
|
mFeatureFlags = featureFlags;
|
||||||
|
|
||||||
mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
|
mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
|
||||||
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
|
mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
|
||||||
@@ -182,6 +213,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
@Override
|
@Override
|
||||||
protected void onInit() {
|
protected void onInit() {
|
||||||
mView.setAccessibilityDelegate(mAccessibilityDelegate);
|
mView.setAccessibilityDelegate(mAccessibilityDelegate);
|
||||||
|
|
||||||
|
if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
|
||||||
|
collectFlow(mView, mTransitionInteractor.getDozeAmountTransition(),
|
||||||
|
mDozeTransitionCallback);
|
||||||
|
collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -377,14 +414,17 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
|
pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
|
||||||
pw.println(" mShowLockIcon: " + mShowLockIcon);
|
pw.println(" mShowLockIcon: " + mShowLockIcon);
|
||||||
pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
|
pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
|
||||||
pw.println(" mIsDozing: " + mIsDozing);
|
pw.println();
|
||||||
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
|
pw.println(" mIsDozing: " + mIsDozing);
|
||||||
pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
|
pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
|
||||||
pw.println(" mRunningFPS: " + mRunningFPS);
|
+ mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
|
||||||
pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
|
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
|
||||||
pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
|
pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
|
||||||
pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
|
pw.println(" mRunningFPS: " + mRunningFPS);
|
||||||
pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
|
pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
|
||||||
|
pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
|
||||||
|
pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
|
||||||
|
pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
|
||||||
|
|
||||||
if (mView != null) {
|
if (mView != null) {
|
||||||
mView.dump(pw, args);
|
mView.dump(pw, args);
|
||||||
@@ -425,16 +465,20 @@ public class LockIconViewController extends ViewController<LockIconView> impleme
|
|||||||
new StatusBarStateController.StateListener() {
|
new StatusBarStateController.StateListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onDozeAmountChanged(float linear, float eased) {
|
public void onDozeAmountChanged(float linear, float eased) {
|
||||||
mInterpolatedDarkAmount = eased;
|
if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
|
||||||
mView.setDozeAmount(eased);
|
mInterpolatedDarkAmount = eased;
|
||||||
updateBurnInOffsets();
|
mView.setDozeAmount(eased);
|
||||||
|
updateBurnInOffsets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDozingChanged(boolean isDozing) {
|
public void onDozingChanged(boolean isDozing) {
|
||||||
mIsDozing = isDozing;
|
if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
|
||||||
updateBurnInOffsets();
|
mIsDozing = isDozing;
|
||||||
updateVisibility();
|
updateBurnInOffsets();
|
||||||
|
updateVisibility();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -120,6 +120,12 @@ object Flags {
|
|||||||
*/
|
*/
|
||||||
@JvmField val STEP_CLOCK_ANIMATION = UnreleasedFlag(212)
|
@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
|
// 300 - power menu
|
||||||
// TODO(b/254512600): Tracking Bug
|
// TODO(b/254512600): Tracking Bug
|
||||||
@JvmField val POWER_MENU_LITE = ReleasedFlag(300)
|
@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 com.android.systemui.keyguard.shared.model.TransitionStep
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.merge
|
||||||
|
|
||||||
/** Encapsulates business-logic related to the keyguard transitions. */
|
/** Encapsulates business-logic related to the keyguard transitions. */
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@@ -34,4 +36,17 @@ constructor(
|
|||||||
) {
|
) {
|
||||||
/** AOD->LOCKSCREEN transition information. */
|
/** AOD->LOCKSCREEN transition information. */
|
||||||
val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
|
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