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.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
|
||||||
@@ -221,8 +222,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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -265,10 +269,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
|
||||||
|
|||||||
@@ -118,6 +118,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