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      */
KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon)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
KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon, WakeLock wakeLock)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 
registerCallbacks(KeyguardUpdateMonitor monitor)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      */
getKeyguardCallback()164     protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
165         if (mUpdateMonitorCallback == null) {
166             mUpdateMonitorCallback = new BaseKeyguardCallback();
167         }
168         return mUpdateMonitorCallback;
169     }
170 
updateDisclosure()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 
setVisible(boolean visible)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      */
setRestingIndication(String restingIndication)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      */
setUserInfoController(UserInfoController userInfoController)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      */
getTrustGrantedIndication()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      */
getTrustManagedIndication()235     protected String getTrustManagedIndication() {
236         return null;
237     }
238 
239     /**
240      * Hides transient indication in {@param delayMs}.
241      */
hideTransientIndicationDelayed(long delayMs)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      */
showTransientIndication(int transientIndication)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      */
showTransientIndication(CharSequence transientIndication)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      */
showTransientIndication(CharSequence transientIndication, int textColor)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      */
hideTransientIndication()280     public void hideTransientIndication() {
281         if (mTransientIndication != null) {
282             mTransientIndication = null;
283             mHandler.removeMessages(MSG_HIDE_TRANSIENT);
284             updateIndication(false);
285         }
286     }
287 
updateIndication(boolean animate)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
animateText(KeyguardIndicationTextView textView, String indication)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 
computePowerIndication()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 
setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)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 
setDozing(boolean dozing)467     public void setDozing(boolean dozing) {
468         if (mDozing == dozing) {
469             return;
470         }
471         mDozing = dozing;
472         updateIndication(false);
473         updateDisclosure();
474     }
475 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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
onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status)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
onKeyguardVisibilityChanged(boolean showing)519         public void onKeyguardVisibilityChanged(boolean showing) {
520             if (showing) {
521                 updateDisclosure();
522             }
523         }
524 
525         @Override
onFingerprintHelp(int msgId, String helpString)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
onFingerprintError(int msgId, String errString)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
onTrustAgentErrorMessage(CharSequence message)575         public void onTrustAgentErrorMessage(CharSequence message) {
576             int errorColor = Utils.getColorError(mContext);
577             showTransientIndication(message, errorColor);
578         }
579 
580         @Override
onScreenTurnedOn()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
onFingerprintRunningStateChanged(boolean running)592         public void onFingerprintRunningStateChanged(boolean running) {
593             if (running) {
594                 mMessageToShowOnScreenOn = null;
595             }
596         }
597 
598         @Override
onFingerprintAuthenticated(int userId)599         public void onFingerprintAuthenticated(int userId) {
600             super.onFingerprintAuthenticated(userId);
601             mLastSuccessiveErrorMessage = -1;
602         }
603 
604         @Override
onFingerprintAuthFailed()605         public void onFingerprintAuthFailed() {
606             super.onFingerprintAuthFailed();
607             mLastSuccessiveErrorMessage = -1;
608         }
609 
610         @Override
onUserUnlocked()611         public void onUserUnlocked() {
612             if (mVisible) {
613                 updateIndication(false);
614             }
615         }
616     };
617 }
618