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

Commit 69af3501 authored by Beverly's avatar Beverly
Browse files

KeyguardRotateController handles minimum show time

Instead of KeyguardIndicationTextView ensuring that
keyguard messages are shown for a minimum amount of time,
move logic into KeygaurdIndicationRotateTextViewController,
since the rotating text view controller already handles
the rotating text durations its easier to coordinate
the timing in one class rather than across two.

Update the charging info if the device is at full charged and
is plugged in. Sometimes the battery status is not updated
so we need  to check the actual %.

Test: manual, atest KeyguardIndicationControllerTest
KeyguardIndicationRotateTextViewControllerTest
Fixes: 188134239
Fixes: 206342960
Fixes: 195347563
Change-Id: Id0cf9bb6c5ac95f83dbb2dcf32f41fa719c0853e

Change-Id: If67b1b1cef2fd6db554df105005a185b1ad0f7d2
parent f5fe79de
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());
        }
    }
}