Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7f947fbf authored by Beverly Tai's avatar Beverly Tai Committed by Android (Google) Code Review
Browse files

Merge "KeyguardRotateController handles minimum show time" into sc-v2-dev

parents 4c79fd90 69af3501
Loading
Loading
Loading
Loading
+96 −22
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.keyguard;
import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.SystemClock;
import android.text.TextUtils;

import androidx.annotation.IntDef;
@@ -40,13 +41,24 @@ import java.util.List;
import java.util.Map;

/**
 * Rotates through messages to show on the keyguard bottom area on the lock screen
 * NOTE: This controller should not be used on AoD to avoid waking up the AP too often.
 * Animates through messages to show on the keyguard bottom area on the lock screen.
 * Utilizes a {@link KeyguardIndicationTextView} for animations. This class handles the rotating
 * nature of the messages including:
 *  - ensuring a message is shown for its minimum amount of time. Minimum time is determined by
 *  {@link KeyguardIndication#getMinVisibilityMillis()}
 *  - showing the next message after a default of 3.5 seconds before animating to the next
 *  - statically showing a single message if there is only one message to show
 *  - showing certain messages immediately, assuming te current message has been shown for
 *  at least {@link KeyguardIndication#getMinVisibilityMillis()}. For example, transient and
 *  biometric messages are meant to be shown immediately.
 *  - ending animations when dozing begins, and resuming when dozing ends. Rotating messages on
 *  AoD is undesirable since it wakes up the AP too often.
 */
public class KeyguardIndicationRotateTextViewController extends
        ViewController<KeyguardIndicationTextView> implements Dumpable {
    public static String TAG = "KgIndicationRotatingCtrl";
    private static final long DEFAULT_INDICATION_SHOW_LENGTH = 3500; // milliseconds
    public static final long IMPORTANT_MSG_MIN_DURATION = 2000L + 600L; // 2000ms + [Y in duration]

    private final StatusBarStateController mStatusBarStateController;
    private final float mMaxAlpha;
@@ -62,6 +74,8 @@ public class KeyguardIndicationRotateTextViewController extends
    // List of indication types to show. The next indication to show is always at index 0
    private final List<Integer> mIndicationQueue = new LinkedList<>();
    private @IndicationType int mCurrIndicationType = INDICATION_TYPE_NONE;
    private CharSequence mCurrMessage;
    private long mLastIndicationSwitch;

    private boolean mIsDozing;

@@ -94,17 +108,19 @@ public class KeyguardIndicationRotateTextViewController extends
     * Update the indication type with the given String.
     * @param type of indication
     * @param newIndication message to associate with this indication type
     * @param showImmediately if true: shows this indication message immediately. Else, the text
     *                        associated with this type is updated and will show when its turn in
     *                        the IndicationQueue comes around.
     * @param showAsap if true: shows this indication message as soon as possible. If false,
     *                   the text associated with this type is updated and will show when its turn
     *                   in the IndicationQueue comes around.
     */
    public void updateIndication(@IndicationType int type, KeyguardIndication newIndication,
            boolean updateImmediately) {
            boolean showAsap) {
        if (type == INDICATION_TYPE_REVERSE_CHARGING) {
            // temporarily don't show here, instead use AmbientContainer b/181049781
            return;
        }
        final boolean hasPreviousIndication = mIndicationMessages.get(type) != null;
        long minShowDuration = getMinVisibilityMillis(mIndicationMessages.get(mCurrIndicationType));
        final boolean hasPreviousIndication = mIndicationMessages.get(type) != null
                && !TextUtils.isEmpty(mIndicationMessages.get(type).getMessage());
        final boolean hasNewIndication = newIndication != null;
        if (!hasNewIndication) {
            mIndicationMessages.remove(type);
@@ -121,26 +137,47 @@ public class KeyguardIndicationRotateTextViewController extends
            return;
        }

        final boolean showNow = updateImmediately
                || mCurrIndicationType == INDICATION_TYPE_NONE
                || mCurrIndicationType == type;
        long currTime = SystemClock.uptimeMillis();
        long timeSinceLastIndicationSwitch = currTime - mLastIndicationSwitch;
        boolean currMsgShownForMinTime = timeSinceLastIndicationSwitch >= minShowDuration;
        if (hasNewIndication) {
            if (showNow) {
            if (mCurrIndicationType == INDICATION_TYPE_NONE || mCurrIndicationType == type) {
                showIndication(type);
            } else if (showAsap) {
                if (currMsgShownForMinTime) {
                    showIndication(type);
                } else {
                    mIndicationQueue.removeIf(x -> x == type);
                    mIndicationQueue.add(0 /* index */, type /* type */);
                    scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch);
                }
            } else if (!isNextIndicationScheduled()) {
                scheduleShowNextIndication();
                long nextShowTime = Math.max(
                        getMinVisibilityMillis(mIndicationMessages.get(type)),
                        DEFAULT_INDICATION_SHOW_LENGTH);
                if (timeSinceLastIndicationSwitch >= nextShowTime) {
                    showIndication(type);
                } else {
                    scheduleShowNextIndication(
                            nextShowTime - timeSinceLastIndicationSwitch);
                }
            }
            return;
        }

        // current indication is updated to empty
        if (mCurrIndicationType == type
                && !hasNewIndication
                && updateImmediately) {
                && showAsap) {
            if (currMsgShownForMinTime) {
                if (mShowNextIndicationRunnable != null) {
                    mShowNextIndicationRunnable.runImmediately();
                } else {
                    showIndication(INDICATION_TYPE_NONE);
                }
            } else {
                scheduleShowNextIndication(minShowDuration - timeSinceLastIndicationSwitch);
            }
        }
    }

@@ -164,11 +201,10 @@ public class KeyguardIndicationRotateTextViewController extends
     * - will continue to be in the rotation of messages shown until hideTransient is called.
     */
    public void showTransient(CharSequence newIndication) {
        final long inAnimationDuration = 600L; // see KeyguardIndicationTextView.getYInDuration
        updateIndication(INDICATION_TYPE_TRANSIENT,
                new KeyguardIndication.Builder()
                        .setMessage(newIndication)
                        .setMinVisibilityMillis(2000L + inAnimationDuration)
                        .setMinVisibilityMillis(IMPORTANT_MSG_MIN_DURATION)
                        .setTextColor(mInitialTextColorState)
                        .build(),
                /* showImmediately */true);
@@ -188,6 +224,15 @@ public class KeyguardIndicationRotateTextViewController extends
        return mIndicationMessages.keySet().size() > 0;
    }

    /**
     * Clears all messages in the queue and sets the current message to an empty string.
     */
    public void clearMessages() {
        mCurrIndicationType = INDICATION_TYPE_NONE;
        mIndicationQueue.clear();
        mView.clearMessages();
    }

    /**
     * Immediately show the passed indication type and schedule the next indication to show.
     * Will re-add this indication to be re-shown after all other indications have been
@@ -196,27 +241,52 @@ public class KeyguardIndicationRotateTextViewController extends
    private void showIndication(@IndicationType int type) {
        cancelScheduledIndication();

        final CharSequence previousMessage = mCurrMessage;
        final @IndicationType int previousIndicationType = mCurrIndicationType;
        mCurrIndicationType = type;
        mCurrMessage = mIndicationMessages.get(type) != null
                ? mIndicationMessages.get(type).getMessage()
                : null;

        mIndicationQueue.removeIf(x -> x == type);
        if (mCurrIndicationType != INDICATION_TYPE_NONE) {
            mIndicationQueue.add(type); // re-add to show later
        }

        mLastIndicationSwitch = SystemClock.uptimeMillis();
        if (!TextUtils.equals(previousMessage, mCurrMessage)
                || previousIndicationType != mCurrIndicationType) {
            mView.switchIndication(mIndicationMessages.get(type));
        }

        // only schedule next indication if there's more than just this indication in the queue
        if (mCurrIndicationType != INDICATION_TYPE_NONE && mIndicationQueue.size() > 1) {
            scheduleShowNextIndication();
            scheduleShowNextIndication(Math.max(
                    getMinVisibilityMillis(mIndicationMessages.get(type)),
                    DEFAULT_INDICATION_SHOW_LENGTH));
        }
    }

    private long getMinVisibilityMillis(KeyguardIndication indication) {
        if (indication == null) {
            return 0;
        }

        if (indication.getMinVisibilityMillis() == null) {
            return 0;
        }

        return indication.getMinVisibilityMillis();
    }

    protected boolean isNextIndicationScheduled() {
        return mShowNextIndicationRunnable != null;
    }

    private void scheduleShowNextIndication() {

    private void scheduleShowNextIndication(long msUntilShowNextMsg) {
        cancelScheduledIndication();
        mShowNextIndicationRunnable = new ShowNextIndication(DEFAULT_INDICATION_SHOW_LENGTH);
        mShowNextIndicationRunnable = new ShowNextIndication(msUntilShowNextMsg);
    }

    private void cancelScheduledIndication() {
@@ -292,7 +362,9 @@ public class KeyguardIndicationRotateTextViewController extends
        }
    }

    // only used locally to stop showing any messages & stop the rotating messages
    static final int INDICATION_TYPE_NONE = -1;

    public static final int INDICATION_TYPE_OWNER_INFO = 0;
    public static final int INDICATION_TYPE_DISCLOSURE = 1;
    public static final int INDICATION_TYPE_LOGOUT = 2;
@@ -303,6 +375,7 @@ public class KeyguardIndicationRotateTextViewController extends
    public static final int INDICATION_TYPE_RESTING = 7;
    public static final int INDICATION_TYPE_USER_LOCKED = 8;
    public static final int INDICATION_TYPE_REVERSE_CHARGING = 10;
    public static final int INDICATION_TYPE_BIOMETRIC_MESSAGE = 11;

    @IntDef({
            INDICATION_TYPE_NONE,
@@ -316,6 +389,7 @@ public class KeyguardIndicationRotateTextViewController extends
            INDICATION_TYPE_RESTING,
            INDICATION_TYPE_USER_LOCKED,
            INDICATION_TYPE_REVERSE_CHARGING,
            INDICATION_TYPE_BIOMETRIC_MESSAGE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface IndicationType{}
+108 −48

File changed.

Preview size limit exceeded, changes collapsed.

+45 −56
Original line number Diff line number Diff line
@@ -35,23 +35,20 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.keyguard.KeyguardIndication;

import java.util.LinkedList;

/**
 * A view to show hints on Keyguard ("Swipe up to unlock", "Tap again to open").
 */
public class KeyguardIndicationTextView extends TextView {
    private static final long MSG_MIN_DURATION_MILLIS_DEFAULT = 1500;

    @StyleRes
    private static int sStyleId = R.style.TextAppearance_Keyguard_BottomArea;
    @StyleRes
    private static int sButtonStyleId = R.style.TextAppearance_Keyguard_BottomArea_Button;

    private long mNextAnimationTime = 0;
    private boolean mAnimationsEnabled = true;
    private LinkedList<CharSequence> mMessages = new LinkedList<>();
    private LinkedList<KeyguardIndication> mKeyguardIndicationInfo = new LinkedList<>();
    private CharSequence mMessage;
    private KeyguardIndication mKeyguardIndicationInfo;

    private Animator mLastAnimator;

    public KeyguardIndicationTextView(Context context) {
        super(context);
@@ -71,22 +68,24 @@ public class KeyguardIndicationTextView extends TextView {
    }

    /**
     * Clears message queue.
     * Clears message queue and currently shown message.
     */
    public void clearMessages() {
        mMessages.clear();
        mKeyguardIndicationInfo.clear();
        if (mLastAnimator != null) {
            mLastAnimator.cancel();
        }
        setText("");
    }

    /**
     * Changes the text with an animation and makes sure a single indication is shown long enough.
     * Changes the text with an animation.
     */
    public void switchIndication(int textResId) {
        switchIndication(getResources().getText(textResId), null);
    }

    /**
     * Changes the text with an animation and makes sure a single indication is shown long enough.
     * Changes the text with an animation.
     *
     * @param indication The text to show.
     */
@@ -95,15 +94,14 @@ public class KeyguardIndicationTextView extends TextView {
    }

    /**
     * Changes the text with an animation. Makes sure a single indication is shown long enough.
     * Changes the text with an animation.
     */
    public void switchIndication(CharSequence text, KeyguardIndication indication) {
        switchIndication(text, indication, true, null);
    }

    /**
     * Changes the text with an optional animation. For animating text, makes sure a single
     * indication is shown long enough.
     * Updates the text with an optional animation.
     *
     * @param text The text to show.
     * @param indication optional display information for the text
@@ -112,33 +110,15 @@ public class KeyguardIndicationTextView extends TextView {
     */
    public void switchIndication(CharSequence text, KeyguardIndication indication,
            boolean animate, Runnable onAnimationEndCallback) {
        if (text == null) text = "";

        CharSequence lastPendingMessage = mMessages.peekLast();
        if (TextUtils.equals(lastPendingMessage, text)
                || (lastPendingMessage == null && TextUtils.equals(text, getText()))) {
            if (onAnimationEndCallback != null) {
                onAnimationEndCallback.run();
            }
            return;
        }
        mMessages.add(text);
        mKeyguardIndicationInfo.add(indication);
        mMessage = text;
        mKeyguardIndicationInfo = indication;

        if (animate) {
            final boolean hasIcon = indication != null && indication.getIcon() != null;
            final AnimatorSet animator = new AnimatorSet();
            AnimatorSet animator = new AnimatorSet();
            // Make sure each animation is visible for a minimum amount of time, while not worrying
            // about fading in blank text
            long timeInMillis = System.currentTimeMillis();
            long delay = Math.max(0, mNextAnimationTime - timeInMillis);
            setNextAnimationTime(timeInMillis + delay + getFadeOutDuration());
            final long minDurationMillis =
                    (indication != null && indication.getMinVisibilityMillis() != null)
                            ? indication.getMinVisibilityMillis()
                            : MSG_MIN_DURATION_MILLIS_DEFAULT;
            if (!text.equals("") || hasIcon) {
                setNextAnimationTime(mNextAnimationTime + minDurationMillis);
            if (!TextUtils.isEmpty(mMessage) || hasIcon) {
                Animator inAnimator = getInAnimator();
                inAnimator.addListener(new AnimatorListenerAdapter() {
                    @Override
@@ -164,7 +144,10 @@ public class KeyguardIndicationTextView extends TextView {
                animator.play(outAnimator);
            }

            animator.setStartDelay(delay);
            if (mLastAnimator != null) {
                mLastAnimator.cancel();
            }
            mLastAnimator = animator;
            animator.start();
        } else {
            setAlpha(1f);
@@ -173,6 +156,10 @@ public class KeyguardIndicationTextView extends TextView {
            if (onAnimationEndCallback != null) {
                onAnimationEndCallback.run();
            }
            if (mLastAnimator != null) {
                mLastAnimator.cancel();
                mLastAnimator = null;
            }
        }
    }

@@ -182,11 +169,21 @@ public class KeyguardIndicationTextView extends TextView {
        fadeOut.setDuration(getFadeOutDuration());
        fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN);
        fadeOut.addListener(new AnimatorListenerAdapter() {
            private boolean mCancelled = false;
            @Override
            public void onAnimationEnd(Animator animator) {
                super.onAnimationEnd(animator);
                if (!mCancelled) {
                    setNextIndication();
                }
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                super.onAnimationCancel(animator);
                mCancelled = true;
                setAlpha(0);
            }
        });

        Animator yTranslate =
@@ -198,20 +195,19 @@ public class KeyguardIndicationTextView extends TextView {
    }

    private void setNextIndication() {
        KeyguardIndication info = mKeyguardIndicationInfo.poll();
        if (info != null) {
        if (mKeyguardIndicationInfo != null) {
            // First, update the style.
            // If a background is set on the text, we don't want shadow on the text
            if (info.getBackground() != null) {
            if (mKeyguardIndicationInfo.getBackground() != null) {
                setTextAppearance(sButtonStyleId);
            } else {
                setTextAppearance(sStyleId);
            }
            setBackground(info.getBackground());
            setTextColor(info.getTextColor());
            setOnClickListener(info.getClickListener());
            setClickable(info.getClickListener() != null);
            final Drawable icon = info.getIcon();
            setBackground(mKeyguardIndicationInfo.getBackground());
            setTextColor(mKeyguardIndicationInfo.getTextColor());
            setOnClickListener(mKeyguardIndicationInfo.getClickListener());
            setClickable(mKeyguardIndicationInfo.getClickListener() != null);
            final Drawable icon = mKeyguardIndicationInfo.getIcon();
            if (icon != null) {
                icon.setTint(getCurrentTextColor());
                if (icon instanceof AnimatedVectorDrawable) {
@@ -220,7 +216,7 @@ public class KeyguardIndicationTextView extends TextView {
            }
            setCompoundDrawablesRelativeWithIntrinsicBounds(icon, null, null, null);
        }
        setText(mMessages.poll());
        setText(mMessage);
    }

    private AnimatorSet getInAnimator() {
@@ -238,6 +234,7 @@ public class KeyguardIndicationTextView extends TextView {
            public void onAnimationCancel(Animator animation) {
                super.onAnimationCancel(animation);
                setTranslationY(0);
                setAlpha(1f);
            }
        });
        animatorSet.playTogether(yTranslate, fadeIn);
@@ -270,14 +267,6 @@ public class KeyguardIndicationTextView extends TextView {
        return 167L;
    }

    private void setNextAnimationTime(long time) {
        if (mAnimationsEnabled) {
            mNextAnimationTime = time;
        } else {
            mNextAnimationTime = 0L;
        }
    }

    private int getYTranslationPixels() {
        return mContext.getResources().getDimensionPixelSize(
                com.android.systemui.R.dimen.keyguard_indication_y_translation);
+40 −3
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard;


import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;

@@ -56,7 +57,8 @@ import org.mockito.MockitoAnnotations;
public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {

    private static final String TEST_MESSAGE = "test message";
    private static final String TEST_MESSAGE_2 = "test message 2";
    private static final String TEST_MESSAGE_2 = "test message two";
    private int mMsgId = 0;

    @Mock
    private DelayableExecutor mExecutor;
@@ -200,6 +202,24 @@ public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCas
        verify(mExecutor).executeDelayed(any(), anyLong());
    }

    @Test
    public void testSameMessage_noIndicationUpdate() {
        // GIVEN we are showing and indication with a test message
        mController.updateIndication(
                INDICATION_TYPE_OWNER_INFO, createIndication(TEST_MESSAGE), true);
        reset(mView);
        reset(mExecutor);

        // WHEN the same type tries to show the same exact message
        final KeyguardIndication sameIndication = createIndication(TEST_MESSAGE);
        mController.updateIndication(
                INDICATION_TYPE_OWNER_INFO, sameIndication, true);

        // THEN
        // - we don't update the indication b/c there's no reason the animate the same text
        verify(mView, never()).switchIndication(sameIndication);
    }

    @Test
    public void testTransientIndication() {
        // GIVEN we already have two indication messages
@@ -223,8 +243,11 @@ public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCas
    @Test
    public void testHideIndicationOneMessage() {
        // GIVEN we have one indication message
        KeyguardIndication indication = createIndication();
        mController.updateIndication(
                INDICATION_TYPE_OWNER_INFO, createIndication(), false);
                INDICATION_TYPE_OWNER_INFO, indication, false);
        verify(mView).switchIndication(indication);
        reset(mView);

        // WHEN we hide the current indication type
        mController.hideIndication(INDICATION_TYPE_OWNER_INFO);
@@ -254,6 +277,10 @@ public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCas

    @Test
    public void testStartDozing() {
        // GIVEN a biometric message is showing
        mController.updateIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE,
                createIndication(), true);

        // WHEN the device is dozing
        mStatusBarStateListener.onDozingChanged(true);

@@ -293,9 +320,19 @@ public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCas
        verify(mView, never()).switchIndication(any());
    }

    /**
     * Create an indication with a unique message.
     */
    private KeyguardIndication createIndication() {
        return createIndication(TEST_MESSAGE + " " + mMsgId++);
    }

    /**
     * Create an indication with the given message.
     */
    private KeyguardIndication createIndication(String msg) {
        return new KeyguardIndication.Builder()
                .setMessage(TEST_MESSAGE)
                .setMessage(msg)
                .setTextColor(ColorStateList.valueOf(Color.WHITE))
                .build();
    }
+20 −10
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;

import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_DISCLOSURE;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_OWNER_INFO;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_RESTING;
@@ -112,6 +113,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
    private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo",
            "bar");

    private static final int TEST_STRING_RES = R.string.keyguard_indication_trust_unlocked;

    private String mKeyguardTryFingerprintMsg;
    private String mDisclosureWithOrganization;
    private String mDisclosureGeneric;
@@ -419,7 +422,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {

        // WHEN transient text is shown
        mStatusBarStateListener.onDozingChanged(true);
        mController.showTransientIndication("Test");
        mController.showTransientIndication(TEST_STRING_RES);

        // THEN wake lock is held while the animation is running
        assertTrue("WakeLock expected: HELD, was: RELEASED", mWakeLock.isHeld());
@@ -434,7 +437,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {

        // WHEN we show the transient indication
        mStatusBarStateListener.onDozingChanged(true);
        mController.showTransientIndication("Test");
        mController.showTransientIndication(TEST_STRING_RES);

        // THEN wake lock is RELEASED, not held
        assertFalse("WakeLock expected: RELEASED, was: HELD", mWakeLock.isHeld());
@@ -445,10 +448,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
        createController();

        mController.setVisible(true);
        mController.showTransientIndication("Test");
        mController.showTransientIndication(TEST_STRING_RES);
        mStatusBarStateListener.onDozingChanged(true);

        assertThat(mTextView.getText()).isEqualTo("Test");
        assertThat(mTextView.getText()).isEqualTo(
                mContext.getResources().getString(TEST_STRING_RES));
        assertThat(mTextView.getCurrentTextColor()).isEqualTo(Color.WHITE);
        assertThat(mTextView.getAlpha()).isEqualTo(1f);
    }
@@ -462,11 +466,11 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
        mController.getKeyguardCallback().onBiometricHelp(
                KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
                BiometricSourceType.FACE);
        verifyTransientMessage(message);
        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
        reset(mRotateTextViewController);
        mStatusBarStateListener.onDozingChanged(true);

        verifyHideIndication(INDICATION_TYPE_TRANSIENT);
        verifyHideIndication(INDICATION_TYPE_BIOMETRIC_MESSAGE);
    }

    @Test
@@ -478,7 +482,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
        mController.getKeyguardCallback().onBiometricError(FaceManager.FACE_ERROR_TIMEOUT,
                "A message", BiometricSourceType.FACE);

        verifyTransientMessage(message);
        verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
        mStatusBarStateListener.onDozingChanged(true);

        assertThat(mTextView.getText()).isNotEqualTo(message);
@@ -497,7 +501,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
                FingerprintManager.FINGERPRINT_ERROR_CANCELED, "bar",
                BiometricSourceType.FINGERPRINT);

        verifyNoTransientMessage();
        verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
        verifyNoMessage(INDICATION_TYPE_TRANSIENT);
    }

    @Test
@@ -757,7 +762,12 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
        verify(mRotateTextViewController).showTransient(eq(message));
    }

    private void verifyNoTransientMessage() {
        verify(mRotateTextViewController, never()).showTransient(any());
    private void verifyNoMessage(int type) {
        if (type == INDICATION_TYPE_TRANSIENT) {
            verify(mRotateTextViewController, never()).showTransient(anyString());
        } else {
            verify(mRotateTextViewController, never()).updateIndication(eq(type),
                    anyObject(), anyBoolean());
        }
    }
}