Home | History | Annotate | Download | only in statusbar
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.systemui.statusbar;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.app.admin.DevicePolicyManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.res.Resources;
     27 import android.graphics.Color;
     28 import android.hardware.fingerprint.FingerprintManager;
     29 import android.os.BatteryManager;
     30 import android.os.BatteryStats;
     31 import android.os.Handler;
     32 import android.os.Message;
     33 import android.os.RemoteException;
     34 import android.os.ServiceManager;
     35 import android.os.UserHandle;
     36 import android.os.UserManager;
     37 import android.text.TextUtils;
     38 import android.text.format.Formatter;
     39 import android.util.Log;
     40 import android.view.View;
     41 import android.view.ViewGroup;
     42 
     43 import com.android.internal.annotations.VisibleForTesting;
     44 import com.android.internal.app.IBatteryStats;
     45 import com.android.keyguard.KeyguardUpdateMonitor;
     46 import com.android.keyguard.KeyguardUpdateMonitorCallback;
     47 import com.android.settingslib.Utils;
     48 import com.android.systemui.Dependency;
     49 import com.android.systemui.Interpolators;
     50 import com.android.systemui.R;
     51 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
     52 import com.android.systemui.statusbar.phone.LockIcon;
     53 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
     54 import com.android.systemui.statusbar.policy.UserInfoController;
     55 import com.android.systemui.util.wakelock.SettableWakeLock;
     56 import com.android.systemui.util.wakelock.WakeLock;
     57 
     58 import java.io.FileDescriptor;
     59 import java.io.PrintWriter;
     60 import java.text.NumberFormat;
     61 import java.util.IllegalFormatConversionException;
     62 
     63 /**
     64  * Controls the indications and error messages shown on the Keyguard
     65  */
     66 public class KeyguardIndicationController {
     67 
     68     private static final String TAG = "KeyguardIndication";
     69     private static final boolean DEBUG_CHARGING_SPEED = false;
     70 
     71     private static final int MSG_HIDE_TRANSIENT = 1;
     72     private static final int MSG_CLEAR_FP_MSG = 2;
     73     private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
     74 
     75     private final Context mContext;
     76     private ViewGroup mIndicationArea;
     77     private KeyguardIndicationTextView mTextView;
     78     private KeyguardIndicationTextView mDisclosure;
     79     private final UserManager mUserManager;
     80     private final IBatteryStats mBatteryInfo;
     81     private final SettableWakeLock mWakeLock;
     82 
     83     private final int mSlowThreshold;
     84     private final int mFastThreshold;
     85     private LockIcon mLockIcon;
     86     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     87 
     88     private String mRestingIndication;
     89     private CharSequence mTransientIndication;
     90     private int mTransientTextColor;
     91     private int mInitialTextColor;
     92     private boolean mVisible;
     93 
     94     private boolean mPowerPluggedIn;
     95     private boolean mPowerPluggedInWired;
     96     private boolean mPowerCharged;
     97     private int mChargingSpeed;
     98     private int mChargingWattage;
     99     private int mBatteryLevel;
    100     private String mMessageToShowOnScreenOn;
    101 
    102     private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
    103 
    104     private final DevicePolicyManager mDevicePolicyManager;
    105     private boolean mDozing;
    106 
    107     /**
    108      * Creates a new KeyguardIndicationController and registers callbacks.
    109      */
    110     public KeyguardIndicationController(Context context, ViewGroup indicationArea,
    111             LockIcon lockIcon) {
    112         this(context, indicationArea, lockIcon,
    113                 WakeLock.createPartial(context, "Doze:KeyguardIndication"));
    114 
    115         registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
    116     }
    117 
    118     /**
    119      * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
    120      */
    121     @VisibleForTesting
    122     KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
    123                 WakeLock wakeLock) {
    124         mContext = context;
    125         mIndicationArea = indicationArea;
    126         mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
    127         mInitialTextColor = mTextView != null ? mTextView.getCurrentTextColor() : Color.WHITE;
    128         mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
    129         mLockIcon = lockIcon;
    130         mWakeLock = new SettableWakeLock(wakeLock);
    131 
    132         Resources res = context.getResources();
    133         mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
    134         mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
    135 
    136         mUserManager = context.getSystemService(UserManager.class);
    137         mBatteryInfo = IBatteryStats.Stub.asInterface(
    138                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
    139 
    140         mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
    141                 Context.DEVICE_POLICY_SERVICE);
    142 
    143         updateDisclosure();
    144     }
    145 
    146     private void registerCallbacks(KeyguardUpdateMonitor monitor) {
    147         monitor.registerCallback(getKeyguardCallback());
    148 
    149         mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
    150                 new IntentFilter(Intent.ACTION_TIME_TICK), null,
    151                 Dependency.get(Dependency.TIME_TICK_HANDLER));
    152     }
    153 
    154     /**
    155      * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
    156      * {@link KeyguardIndicationController}.
    157      *
    158      * <p>Subclasses may override this method to extend or change the callback behavior by extending
    159      * the {@link BaseKeyguardCallback}.
    160      *
    161      * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
    162      * same instance.
    163      */
    164     protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
    165         if (mUpdateMonitorCallback == null) {
    166             mUpdateMonitorCallback = new BaseKeyguardCallback();
    167         }
    168         return mUpdateMonitorCallback;
    169     }
    170 
    171     private void updateDisclosure() {
    172         if (mDevicePolicyManager == null) {
    173             return;
    174         }
    175 
    176         if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
    177             final CharSequence organizationName =
    178                     mDevicePolicyManager.getDeviceOwnerOrganizationName();
    179             if (organizationName != null) {
    180                 mDisclosure.switchIndication(mContext.getResources().getString(
    181                         R.string.do_disclosure_with_name, organizationName));
    182             } else {
    183                 mDisclosure.switchIndication(R.string.do_disclosure_generic);
    184             }
    185             mDisclosure.setVisibility(View.VISIBLE);
    186         } else {
    187             mDisclosure.setVisibility(View.GONE);
    188         }
    189     }
    190 
    191     public void setVisible(boolean visible) {
    192         mVisible = visible;
    193         mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
    194         if (visible) {
    195             // If this is called after an error message was already shown, we should not clear it.
    196             // Otherwise the error message won't be shown
    197             if  (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
    198                 hideTransientIndication();
    199             }
    200             updateIndication(false);
    201         } else if (!visible) {
    202             // If we unlock and return to keyguard quickly, previous error should not be shown
    203             hideTransientIndication();
    204         }
    205     }
    206 
    207     /**
    208      * Sets the indication that is shown if nothing else is showing.
    209      */
    210     public void setRestingIndication(String restingIndication) {
    211         mRestingIndication = restingIndication;
    212         updateIndication(false);
    213     }
    214 
    215     /**
    216      * Sets the active controller managing changes and callbacks to user information.
    217      */
    218     public void setUserInfoController(UserInfoController userInfoController) {
    219     }
    220 
    221     /**
    222      * Returns the indication text indicating that trust has been granted.
    223      *
    224      * @return {@code null} or an empty string if a trust indication text should not be shown.
    225      */
    226     protected String getTrustGrantedIndication() {
    227         return null;
    228     }
    229 
    230     /**
    231      * Returns the indication text indicating that trust is currently being managed.
    232      *
    233      * @return {@code null} or an empty string if a trust managed text should not be shown.
    234      */
    235     protected String getTrustManagedIndication() {
    236         return null;
    237     }
    238 
    239     /**
    240      * Hides transient indication in {@param delayMs}.
    241      */
    242     public void hideTransientIndicationDelayed(long delayMs) {
    243         mHandler.sendMessageDelayed(
    244                 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
    245     }
    246 
    247     /**
    248      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
    249      */
    250     public void showTransientIndication(int transientIndication) {
    251         showTransientIndication(mContext.getResources().getString(transientIndication));
    252     }
    253 
    254     /**
    255      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
    256      */
    257     public void showTransientIndication(CharSequence transientIndication) {
    258         showTransientIndication(transientIndication, mInitialTextColor);
    259     }
    260 
    261     /**
    262      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
    263      */
    264     public void showTransientIndication(CharSequence transientIndication, int textColor) {
    265         mTransientIndication = transientIndication;
    266         mTransientTextColor = textColor;
    267         mHandler.removeMessages(MSG_HIDE_TRANSIENT);
    268         if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
    269             // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
    270             mWakeLock.setAcquired(true);
    271             hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
    272         }
    273 
    274         updateIndication(false);
    275     }
    276 
    277     /**
    278      * Hides transient indication.
    279      */
    280     public void hideTransientIndication() {
    281         if (mTransientIndication != null) {
    282             mTransientIndication = null;
    283             mHandler.removeMessages(MSG_HIDE_TRANSIENT);
    284             updateIndication(false);
    285         }
    286     }
    287 
    288     protected final void updateIndication(boolean animate) {
    289         if (TextUtils.isEmpty(mTransientIndication)) {
    290             mWakeLock.setAcquired(false);
    291         }
    292 
    293         if (mVisible) {
    294             // Walk down a precedence-ordered list of what indication
    295             // should be shown based on user or device state
    296             if (mDozing) {
    297                 mTextView.setTextColor(Color.WHITE);
    298                 if (!TextUtils.isEmpty(mTransientIndication)) {
    299                     // When dozing we ignore any text color and use white instead, because
    300                     // colors can be hard to read in low brightness.
    301                     mTextView.switchIndication(mTransientIndication);
    302                 } else if (mPowerPluggedIn) {
    303                     String indication = computePowerIndication();
    304                     if (animate) {
    305                         animateText(mTextView, indication);
    306                     } else {
    307                         mTextView.switchIndication(indication);
    308                     }
    309                 } else {
    310                     String percentage = NumberFormat.getPercentInstance()
    311                             .format(mBatteryLevel / 100f);
    312                     mTextView.switchIndication(percentage);
    313                 }
    314                 return;
    315             }
    316 
    317             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
    318             int userId = KeyguardUpdateMonitor.getCurrentUser();
    319             String trustGrantedIndication = getTrustGrantedIndication();
    320             String trustManagedIndication = getTrustManagedIndication();
    321             if (!mUserManager.isUserUnlocked(userId)) {
    322                 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
    323                 mTextView.setTextColor(mInitialTextColor);
    324             } else if (!TextUtils.isEmpty(mTransientIndication)) {
    325                 mTextView.switchIndication(mTransientIndication);
    326                 mTextView.setTextColor(mTransientTextColor);
    327             } else if (!TextUtils.isEmpty(trustGrantedIndication)
    328                     && updateMonitor.getUserHasTrust(userId)) {
    329                 mTextView.switchIndication(trustGrantedIndication);
    330                 mTextView.setTextColor(mInitialTextColor);
    331             } else if (mPowerPluggedIn) {
    332                 String indication = computePowerIndication();
    333                 if (DEBUG_CHARGING_SPEED) {
    334                     indication += ",  " + (mChargingWattage / 1000) + " mW";
    335                 }
    336                 mTextView.setTextColor(mInitialTextColor);
    337                 if (animate) {
    338                     animateText(mTextView, indication);
    339                 } else {
    340                     mTextView.switchIndication(indication);
    341                 }
    342             } else if (!TextUtils.isEmpty(trustManagedIndication)
    343                     && updateMonitor.getUserTrustIsManaged(userId)
    344                     && !updateMonitor.getUserHasTrust(userId)) {
    345                 mTextView.switchIndication(trustManagedIndication);
    346                 mTextView.setTextColor(mInitialTextColor);
    347             } else {
    348                 mTextView.switchIndication(mRestingIndication);
    349                 mTextView.setTextColor(mInitialTextColor);
    350             }
    351         }
    352     }
    353 
    354     // animates textView - textView moves up and bounces down
    355     private void animateText(KeyguardIndicationTextView textView, String indication) {
    356         int yTranslation = mContext.getResources().getInteger(
    357                 R.integer.wired_charging_keyguard_text_animation_distance);
    358         int animateUpDuration = mContext.getResources().getInteger(
    359                 R.integer.wired_charging_keyguard_text_animation_duration_up);
    360         int animateDownDuration = mContext.getResources().getInteger(
    361                 R.integer.wired_charging_keyguard_text_animation_duration_down);
    362         textView.animate()
    363                 .translationYBy(yTranslation)
    364                 .setInterpolator(Interpolators.LINEAR)
    365                 .setDuration(animateUpDuration)
    366                 .setListener(new AnimatorListenerAdapter() {
    367                     @Override
    368                     public void onAnimationStart(Animator animation) {
    369                         textView.switchIndication(indication);
    370                     }
    371                     @Override
    372                     public void onAnimationEnd(Animator animation) {
    373                         textView.animate()
    374                                 .setDuration(animateDownDuration)
    375                                 .setInterpolator(Interpolators.BOUNCE)
    376                                 .translationYBy(-1 * yTranslation)
    377                                 .setListener(null);
    378                     }
    379                 });
    380     }
    381 
    382     private String computePowerIndication() {
    383         if (mPowerCharged) {
    384             return mContext.getResources().getString(R.string.keyguard_charged);
    385         }
    386 
    387         // Try fetching charging time from battery stats.
    388         long chargingTimeRemaining = 0;
    389         try {
    390             chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
    391 
    392         } catch (RemoteException e) {
    393             Log.e(TAG, "Error calling IBatteryStats: ", e);
    394         }
    395         final boolean hasChargingTime = chargingTimeRemaining > 0;
    396 
    397         int chargingId;
    398         switch (mChargingSpeed) {
    399             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
    400                 chargingId = hasChargingTime
    401                         ? R.string.keyguard_indication_charging_time_fast
    402                         : R.string.keyguard_plugged_in_charging_fast;
    403                 break;
    404             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
    405                 chargingId = hasChargingTime
    406                         ? R.string.keyguard_indication_charging_time_slowly
    407                         : R.string.keyguard_plugged_in_charging_slowly;
    408                 break;
    409             default:
    410                 chargingId = hasChargingTime
    411                         ? R.string.keyguard_indication_charging_time
    412                         : R.string.keyguard_plugged_in;
    413                 break;
    414         }
    415 
    416         String percentage = NumberFormat.getPercentInstance()
    417                 .format(mBatteryLevel / 100f);
    418         if (hasChargingTime) {
    419             // We now have battery percentage in these strings and it's expected that all
    420             // locales will also have it in the future. For now, we still have to support the old
    421             // format until all languages get the new translations.
    422             String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
    423                     mContext, chargingTimeRemaining);
    424             try {
    425                 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
    426                         percentage);
    427             } catch (IllegalFormatConversionException e) {
    428                 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
    429             }
    430         } else {
    431             // Same as above
    432             try {
    433                 return mContext.getResources().getString(chargingId, percentage);
    434             } catch (IllegalFormatConversionException e) {
    435                 return mContext.getResources().getString(chargingId);
    436             }
    437         }
    438     }
    439 
    440     public void setStatusBarKeyguardViewManager(
    441             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
    442         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
    443     }
    444 
    445     private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
    446         @Override
    447         public void onReceive(Context context, Intent intent) {
    448             mHandler.post(() -> {
    449                 if (mVisible) {
    450                     updateIndication(false);
    451                 }
    452             });
    453         }
    454     };
    455 
    456     private final Handler mHandler = new Handler() {
    457         @Override
    458         public void handleMessage(Message msg) {
    459             if (msg.what == MSG_HIDE_TRANSIENT) {
    460                 hideTransientIndication();
    461             } else if (msg.what == MSG_CLEAR_FP_MSG) {
    462                 mLockIcon.setTransientFpError(false);
    463             }
    464         }
    465     };
    466 
    467     public void setDozing(boolean dozing) {
    468         if (mDozing == dozing) {
    469             return;
    470         }
    471         mDozing = dozing;
    472         updateIndication(false);
    473         updateDisclosure();
    474     }
    475 
    476     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    477         pw.println("KeyguardIndicationController:");
    478         pw.println("  mTransientTextColor: " + Integer.toHexString(mTransientTextColor));
    479         pw.println("  mInitialTextColor: " + Integer.toHexString(mInitialTextColor));
    480         pw.println("  mPowerPluggedInWired: " + mPowerPluggedInWired);
    481         pw.println("  mPowerPluggedIn: " + mPowerPluggedIn);
    482         pw.println("  mPowerCharged: " + mPowerCharged);
    483         pw.println("  mChargingSpeed: " + mChargingSpeed);
    484         pw.println("  mChargingWattage: " + mChargingWattage);
    485         pw.println("  mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
    486         pw.println("  mDozing: " + mDozing);
    487         pw.println("  mBatteryLevel: " + mBatteryLevel);
    488         pw.println("  mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
    489         pw.println("  computePowerIndication(): " + computePowerIndication());
    490     }
    491 
    492     protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
    493         public static final int HIDE_DELAY_MS = 5000;
    494         private int mLastSuccessiveErrorMessage = -1;
    495 
    496         @Override
    497         public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
    498             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
    499                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
    500             boolean wasPluggedIn = mPowerPluggedIn;
    501             mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
    502             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
    503             mPowerCharged = status.isCharged();
    504             mChargingWattage = status.maxChargingWattage;
    505             mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
    506             mBatteryLevel = status.level;
    507             updateIndication(!wasPluggedIn && mPowerPluggedInWired);
    508             if (mDozing) {
    509                 if (!wasPluggedIn && mPowerPluggedIn) {
    510                     showTransientIndication(computePowerIndication());
    511                     hideTransientIndicationDelayed(HIDE_DELAY_MS);
    512                 } else if (wasPluggedIn && !mPowerPluggedIn) {
    513                     hideTransientIndication();
    514                 }
    515             }
    516         }
    517 
    518         @Override
    519         public void onKeyguardVisibilityChanged(boolean showing) {
    520             if (showing) {
    521                 updateDisclosure();
    522             }
    523         }
    524 
    525         @Override
    526         public void onFingerprintHelp(int msgId, String helpString) {
    527             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
    528             if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
    529                 return;
    530             }
    531             int errorColor = Utils.getColorError(mContext);
    532             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
    533                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
    534             } else if (updateMonitor.isScreenOn()) {
    535                 mLockIcon.setTransientFpError(true);
    536                 showTransientIndication(helpString, errorColor);
    537                 hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
    538                 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
    539                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
    540                         TRANSIENT_FP_ERROR_TIMEOUT);
    541             }
    542             // Help messages indicate that there was actually a try since the last error, so those
    543             // are not two successive error messages anymore.
    544             mLastSuccessiveErrorMessage = -1;
    545         }
    546 
    547         @Override
    548         public void onFingerprintError(int msgId, String errString) {
    549             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
    550             if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
    551                     && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
    552                     || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
    553                 return;
    554             }
    555             int errorColor = Utils.getColorError(mContext);
    556             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
    557                 // When swiping up right after receiving a fingerprint error, the bouncer calls
    558                 // authenticate leading to the same message being shown again on the bouncer.
    559                 // We want to avoid this, as it may confuse the user when the message is too
    560                 // generic.
    561                 if (mLastSuccessiveErrorMessage != msgId) {
    562                     mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
    563                 }
    564             } else if (updateMonitor.isScreenOn()) {
    565                 showTransientIndication(errString, errorColor);
    566                 // We want to keep this message around in case the screen was off
    567                 hideTransientIndicationDelayed(HIDE_DELAY_MS);
    568             } else {
    569                 mMessageToShowOnScreenOn = errString;
    570             }
    571             mLastSuccessiveErrorMessage = msgId;
    572         }
    573 
    574         @Override
    575         public void onTrustAgentErrorMessage(CharSequence message) {
    576             int errorColor = Utils.getColorError(mContext);
    577             showTransientIndication(message, errorColor);
    578         }
    579 
    580         @Override
    581         public void onScreenTurnedOn() {
    582             if (mMessageToShowOnScreenOn != null) {
    583                 int errorColor = Utils.getColorError(mContext);
    584                 showTransientIndication(mMessageToShowOnScreenOn, errorColor);
    585                 // We want to keep this message around in case the screen was off
    586                 hideTransientIndicationDelayed(HIDE_DELAY_MS);
    587                 mMessageToShowOnScreenOn = null;
    588             }
    589         }
    590 
    591         @Override
    592         public void onFingerprintRunningStateChanged(boolean running) {
    593             if (running) {
    594                 mMessageToShowOnScreenOn = null;
    595             }
    596         }
    597 
    598         @Override
    599         public void onFingerprintAuthenticated(int userId) {
    600             super.onFingerprintAuthenticated(userId);
    601             mLastSuccessiveErrorMessage = -1;
    602         }
    603 
    604         @Override
    605         public void onFingerprintAuthFailed() {
    606             super.onFingerprintAuthFailed();
    607             mLastSuccessiveErrorMessage = -1;
    608         }
    609 
    610         @Override
    611         public void onUserUnlocked() {
    612             if (mVisible) {
    613                 updateIndication(false);
    614             }
    615         }
    616     };
    617 }
    618