2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.systemui.statusbar;
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.Context;
23 import android.content.res.ColorStateList;
24 import android.content.res.Resources;
25 import android.graphics.Color;
26 import android.hardware.biometrics.BiometricSourceType;
27 import android.hardware.face.FaceManager;
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.UserManager;
36 import android.text.TextUtils;
37 import android.text.format.Formatter;
38 import android.util.Log;
39 import android.view.View;
40 import android.view.ViewGroup;
42 import com.android.internal.annotations.VisibleForTesting;
43 import com.android.internal.app.IBatteryStats;
44 import com.android.internal.logging.nano.MetricsProto;
45 import com.android.internal.widget.LockPatternUtils;
46 import com.android.internal.widget.ViewClippingUtil;
47 import com.android.keyguard.KeyguardUpdateMonitor;
48 import com.android.keyguard.KeyguardUpdateMonitorCallback;
49 import com.android.settingslib.Utils;
50 import com.android.systemui.Dependency;
51 import com.android.systemui.Interpolators;
52 import com.android.systemui.R;
53 import com.android.systemui.plugins.statusbar.StatusBarStateController;
54 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
55 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
56 import com.android.systemui.statusbar.phone.LockIcon;
57 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
58 import com.android.systemui.statusbar.phone.ShadeController;
59 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
60 import com.android.systemui.statusbar.phone.UnlockMethodCache;
61 import com.android.systemui.statusbar.policy.AccessibilityController;
62 import com.android.systemui.statusbar.policy.UserInfoController;
63 import com.android.systemui.util.wakelock.SettableWakeLock;
64 import com.android.systemui.util.wakelock.WakeLock;
66 import java.io.FileDescriptor;
67 import java.io.PrintWriter;
68 import java.text.NumberFormat;
69 import java.util.IllegalFormatConversionException;
72 * Controls the indications and error messages shown on the Keyguard
74 public class KeyguardIndicationController implements StateListener,
75 UnlockMethodCache.OnUnlockMethodChangedListener {
77 private static final String TAG = "KeyguardIndication";
78 private static final boolean DEBUG_CHARGING_SPEED = false;
80 private static final int MSG_HIDE_TRANSIENT = 1;
81 private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
82 private static final int MSG_SWIPE_UP_TO_UNLOCK = 3;
83 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
84 private static final float BOUNCE_ANIMATION_FINAL_Y = 0f;
86 private final Context mContext;
87 private final ShadeController mShadeController;
88 private final AccessibilityController mAccessibilityController;
89 private final UnlockMethodCache mUnlockMethodCache;
90 private final StatusBarStateController mStatusBarStateController;
91 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
92 private ViewGroup mIndicationArea;
93 private KeyguardIndicationTextView mTextView;
94 private KeyguardIndicationTextView mDisclosure;
95 private final UserManager mUserManager;
96 private final IBatteryStats mBatteryInfo;
97 private final SettableWakeLock mWakeLock;
98 private final LockPatternUtils mLockPatternUtils;
100 private final int mSlowThreshold;
101 private final int mFastThreshold;
102 private final LockIcon mLockIcon;
103 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
104 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
106 private String mRestingIndication;
107 private CharSequence mTransientIndication;
108 private ColorStateList mTransientTextColorState;
109 private ColorStateList mInitialTextColorState;
110 private boolean mVisible;
112 private boolean mPowerPluggedIn;
113 private boolean mPowerPluggedInWired;
114 private boolean mPowerCharged;
115 private int mChargingSpeed;
116 private int mChargingWattage;
117 private int mBatteryLevel;
118 private String mMessageToShowOnScreenOn;
120 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
122 private final DevicePolicyManager mDevicePolicyManager;
123 private boolean mDozing;
124 private final ViewClippingUtil.ClippingParameters mClippingParams =
125 new ViewClippingUtil.ClippingParameters() {
127 public boolean shouldFinish(View view) {
128 return view == mIndicationArea;
133 * Creates a new KeyguardIndicationController and registers callbacks.
135 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
137 this(context, indicationArea, lockIcon, new LockPatternUtils(context),
138 WakeLock.createPartial(context, "Doze:KeyguardIndication"),
139 Dependency.get(ShadeController.class),
140 Dependency.get(AccessibilityController.class),
141 UnlockMethodCache.getInstance(context),
142 Dependency.get(StatusBarStateController.class),
143 KeyguardUpdateMonitor.getInstance(context));
147 * Creates a new KeyguardIndicationController for testing.
150 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
151 LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController,
152 AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache,
153 StatusBarStateController statusBarStateController,
154 KeyguardUpdateMonitor keyguardUpdateMonitor) {
156 mLockIcon = lockIcon;
157 mShadeController = shadeController;
158 mAccessibilityController = accessibilityController;
159 mUnlockMethodCache = unlockMethodCache;
160 mStatusBarStateController = statusBarStateController;
161 mKeyguardUpdateMonitor = keyguardUpdateMonitor;
162 // lock icon is not used on all form factors.
163 if (mLockIcon != null) {
164 mLockIcon.setOnLongClickListener(this::handleLockLongClick);
165 mLockIcon.setOnClickListener(this::handleLockClick);
167 mWakeLock = new SettableWakeLock(wakeLock, TAG);
168 mLockPatternUtils = lockPatternUtils;
170 Resources res = context.getResources();
171 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
172 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
174 mUserManager = context.getSystemService(UserManager.class);
175 mBatteryInfo = IBatteryStats.Stub.asInterface(
176 ServiceManager.getService(BatteryStats.SERVICE_NAME));
178 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
179 Context.DEVICE_POLICY_SERVICE);
180 setIndicationArea(indicationArea);
183 mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
184 mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
185 mStatusBarStateController.addCallback(this);
186 mUnlockMethodCache.addListener(this);
189 public void setIndicationArea(ViewGroup indicationArea) {
190 mIndicationArea = indicationArea;
191 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
192 mInitialTextColorState = mTextView != null ?
193 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
194 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
195 updateIndication(false /* animate */);
198 private boolean handleLockLongClick(View view) {
199 mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
200 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
201 showTransientIndication(R.string.keyguard_indication_trust_disabled);
202 mKeyguardUpdateMonitor.onLockIconPressed();
203 mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
208 private void handleLockClick(View view) {
209 if (!mAccessibilityController.isAccessibilityEnabled()) {
212 mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
216 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
217 * {@link KeyguardIndicationController}.
219 * <p>Subclasses may override this method to extend or change the callback behavior by extending
220 * the {@link BaseKeyguardCallback}.
222 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
225 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
226 if (mUpdateMonitorCallback == null) {
227 mUpdateMonitorCallback = new BaseKeyguardCallback();
229 return mUpdateMonitorCallback;
232 private void updateDisclosure() {
233 if (mDevicePolicyManager == null) {
237 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
238 final CharSequence organizationName =
239 mDevicePolicyManager.getDeviceOwnerOrganizationName();
240 if (organizationName != null) {
241 mDisclosure.switchIndication(mContext.getResources().getString(
242 R.string.do_disclosure_with_name, organizationName));
244 mDisclosure.switchIndication(R.string.do_disclosure_generic);
246 mDisclosure.setVisibility(View.VISIBLE);
248 mDisclosure.setVisibility(View.GONE);
252 public void setVisible(boolean visible) {
254 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
256 // If this is called after an error message was already shown, we should not clear it.
257 // Otherwise the error message won't be shown
258 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
259 hideTransientIndication();
261 updateIndication(false);
262 } else if (!visible) {
263 // If we unlock and return to keyguard quickly, previous error should not be shown
264 hideTransientIndication();
269 * Sets the indication that is shown if nothing else is showing.
271 public void setRestingIndication(String restingIndication) {
272 mRestingIndication = restingIndication;
273 updateIndication(false);
277 * Sets the active controller managing changes and callbacks to user information.
279 public void setUserInfoController(UserInfoController userInfoController) {
283 * Returns the indication text indicating that trust has been granted.
285 * @return {@code null} or an empty string if a trust indication text should not be shown.
288 String getTrustGrantedIndication() {
289 return mContext.getString(R.string.keyguard_indication_trust_unlocked);
293 * Returns the indication text indicating that trust is currently being managed.
295 * @return {@code null} or an empty string if a trust managed text should not be shown.
297 private String getTrustManagedIndication() {
302 * Hides transient indication in {@param delayMs}.
304 public void hideTransientIndicationDelayed(long delayMs) {
305 mHandler.sendMessageDelayed(
306 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
310 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
312 public void showTransientIndication(int transientIndication) {
313 showTransientIndication(mContext.getResources().getString(transientIndication));
317 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
319 public void showTransientIndication(CharSequence transientIndication) {
320 showTransientIndication(transientIndication, mInitialTextColorState);
324 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
326 public void showTransientIndication(CharSequence transientIndication,
327 ColorStateList textColorState) {
328 mTransientIndication = transientIndication;
329 mTransientTextColorState = textColorState;
330 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
331 mHandler.removeMessages(MSG_SWIPE_UP_TO_UNLOCK);
332 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
333 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
334 mWakeLock.setAcquired(true);
335 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
338 updateIndication(false);
342 * Hides transient indication.
344 public void hideTransientIndication() {
345 if (mTransientIndication != null) {
346 mTransientIndication = null;
347 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
348 updateIndication(false);
352 protected final void updateIndication(boolean animate) {
353 if (TextUtils.isEmpty(mTransientIndication)) {
354 mWakeLock.setAcquired(false);
358 // Walk down a precedence-ordered list of what indication
359 // should be shown based on user or device state
361 // When dozing we ignore any text color and use white instead, because
362 // colors can be hard to read in low brightness.
363 mTextView.setTextColor(Color.WHITE);
364 if (!TextUtils.isEmpty(mTransientIndication)) {
365 mTextView.switchIndication(mTransientIndication);
366 } else if (mPowerPluggedIn) {
367 String indication = computePowerIndication();
369 animateText(mTextView, indication);
371 mTextView.switchIndication(indication);
374 String percentage = NumberFormat.getPercentInstance()
375 .format(mBatteryLevel / 100f);
376 mTextView.switchIndication(percentage);
381 int userId = KeyguardUpdateMonitor.getCurrentUser();
382 String trustGrantedIndication = getTrustGrantedIndication();
383 String trustManagedIndication = getTrustManagedIndication();
384 if (!mUserManager.isUserUnlocked(userId)) {
385 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
386 mTextView.setTextColor(mInitialTextColorState);
387 } else if (!TextUtils.isEmpty(mTransientIndication)) {
388 mTextView.switchIndication(mTransientIndication);
389 mTextView.setTextColor(mTransientTextColorState);
390 } else if (!TextUtils.isEmpty(trustGrantedIndication)
391 && mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
392 mTextView.switchIndication(trustGrantedIndication);
393 mTextView.setTextColor(mInitialTextColorState);
394 } else if (mPowerPluggedIn) {
395 String indication = computePowerIndication();
396 if (DEBUG_CHARGING_SPEED) {
397 indication += ", " + (mChargingWattage / 1000) + " mW";
399 mTextView.setTextColor(mInitialTextColorState);
401 animateText(mTextView, indication);
403 mTextView.switchIndication(indication);
405 } else if (!TextUtils.isEmpty(trustManagedIndication)
406 && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
407 && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
408 mTextView.switchIndication(trustManagedIndication);
409 mTextView.setTextColor(mInitialTextColorState);
411 mTextView.switchIndication(mRestingIndication);
412 mTextView.setTextColor(mInitialTextColorState);
417 // animates textView - textView moves up and bounces down
418 private void animateText(KeyguardIndicationTextView textView, String indication) {
419 int yTranslation = mContext.getResources().getInteger(
420 R.integer.wired_charging_keyguard_text_animation_distance);
421 int animateUpDuration = mContext.getResources().getInteger(
422 R.integer.wired_charging_keyguard_text_animation_duration_up);
423 int animateDownDuration = mContext.getResources().getInteger(
424 R.integer.wired_charging_keyguard_text_animation_duration_down);
425 textView.animate().cancel();
426 ViewClippingUtil.setClippingDeactivated(textView, true, mClippingParams);
428 .translationYBy(yTranslation)
429 .setInterpolator(Interpolators.LINEAR)
430 .setDuration(animateUpDuration)
431 .setListener(new AnimatorListenerAdapter() {
432 private boolean mCancelled;
435 public void onAnimationStart(Animator animation) {
436 textView.switchIndication(indication);
440 public void onAnimationCancel(Animator animation) {
441 textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
446 public void onAnimationEnd(Animator animation) {
448 ViewClippingUtil.setClippingDeactivated(textView, false,
453 .setDuration(animateDownDuration)
454 .setInterpolator(Interpolators.BOUNCE)
455 .translationY(BOUNCE_ANIMATION_FINAL_Y)
456 .setListener(new AnimatorListenerAdapter() {
458 public void onAnimationEnd(Animator animation) {
459 textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
460 ViewClippingUtil.setClippingDeactivated(textView, false,
468 private String computePowerIndication() {
470 return mContext.getResources().getString(R.string.keyguard_charged);
473 // Try fetching charging time from battery stats.
474 long chargingTimeRemaining = 0;
476 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
478 } catch (RemoteException e) {
479 Log.e(TAG, "Error calling IBatteryStats: ", e);
481 final boolean hasChargingTime = chargingTimeRemaining > 0;
484 if (mPowerPluggedInWired) {
485 switch (mChargingSpeed) {
486 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
487 chargingId = hasChargingTime
488 ? R.string.keyguard_indication_charging_time_fast
489 : R.string.keyguard_plugged_in_charging_fast;
491 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
492 chargingId = hasChargingTime
493 ? R.string.keyguard_indication_charging_time_slowly
494 : R.string.keyguard_plugged_in_charging_slowly;
497 chargingId = hasChargingTime
498 ? R.string.keyguard_indication_charging_time
499 : R.string.keyguard_plugged_in;
503 chargingId = hasChargingTime
504 ? R.string.keyguard_indication_charging_time_wireless
505 : R.string.keyguard_plugged_in_wireless;
508 String percentage = NumberFormat.getPercentInstance()
509 .format(mBatteryLevel / 100f);
510 if (hasChargingTime) {
511 // We now have battery percentage in these strings and it's expected that all
512 // locales will also have it in the future. For now, we still have to support the old
513 // format until all languages get the new translations.
514 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
515 mContext, chargingTimeRemaining);
517 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
519 } catch (IllegalFormatConversionException e) {
520 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
525 return mContext.getResources().getString(chargingId, percentage);
526 } catch (IllegalFormatConversionException e) {
527 return mContext.getResources().getString(chargingId);
532 public void setStatusBarKeyguardViewManager(
533 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
534 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
537 private final KeyguardUpdateMonitorCallback mTickReceiver =
538 new KeyguardUpdateMonitorCallback() {
540 public void onTimeChanged() {
542 updateIndication(false /* animate */);
547 private final Handler mHandler = new Handler() {
549 public void handleMessage(Message msg) {
550 if (msg.what == MSG_HIDE_TRANSIENT) {
551 hideTransientIndication();
552 } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
553 mLockIcon.setTransientBiometricsError(false);
554 } else if (msg.what == MSG_SWIPE_UP_TO_UNLOCK) {
555 showSwipeUpToUnlock();
560 private void showSwipeUpToUnlock() {
565 String message = mContext.getString(R.string.keyguard_unlock);
566 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
567 mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState);
568 } else if (mKeyguardUpdateMonitor.isScreenOn()) {
569 showTransientIndication(message);
570 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
574 public void setDozing(boolean dozing) {
575 if (mDozing == dozing) {
579 updateIndication(false);
583 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
584 pw.println("KeyguardIndicationController:");
585 pw.println(" mTransientTextColorState: " + mTransientTextColorState);
586 pw.println(" mInitialTextColorState: " + mInitialTextColorState);
587 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
588 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
589 pw.println(" mPowerCharged: " + mPowerCharged);
590 pw.println(" mChargingSpeed: " + mChargingSpeed);
591 pw.println(" mChargingWattage: " + mChargingWattage);
592 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
593 pw.println(" mDozing: " + mDozing);
594 pw.println(" mBatteryLevel: " + mBatteryLevel);
595 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
596 pw.println(" computePowerIndication(): " + computePowerIndication());
600 public void onStateChanged(int newState) {
605 public void onDozingChanged(boolean isDozing) {
610 public void onUnlockMethodStateChanged() {
611 updateIndication(!mDozing);
614 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
615 public static final int HIDE_DELAY_MS = 5000;
618 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
619 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
620 || status.status == BatteryManager.BATTERY_STATUS_FULL;
621 boolean wasPluggedIn = mPowerPluggedIn;
622 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
623 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
624 mPowerCharged = status.isCharged();
625 mChargingWattage = status.maxChargingWattage;
626 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
627 mBatteryLevel = status.level;
628 updateIndication(!wasPluggedIn && mPowerPluggedInWired);
630 if (!wasPluggedIn && mPowerPluggedIn) {
631 showTransientIndication(computePowerIndication());
632 hideTransientIndicationDelayed(HIDE_DELAY_MS);
633 } else if (wasPluggedIn && !mPowerPluggedIn) {
634 hideTransientIndication();
640 public void onKeyguardVisibilityChanged(boolean showing) {
647 public void onBiometricHelp(int msgId, String helpString,
648 BiometricSourceType biometricSourceType) {
649 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
650 if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
653 animatePadlockError();
654 boolean showSwipeToUnlock =
655 msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
656 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
657 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
658 mInitialTextColorState);
659 } else if (updateMonitor.isScreenOn()) {
660 showTransientIndication(helpString);
661 if (!showSwipeToUnlock) {
662 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
665 if (showSwipeToUnlock) {
666 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWIPE_UP_TO_UNLOCK),
667 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
672 public void onBiometricError(int msgId, String errString,
673 BiometricSourceType biometricSourceType) {
674 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
675 if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
678 animatePadlockError();
679 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
680 mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
681 } else if (updateMonitor.isScreenOn()) {
682 showTransientIndication(errString);
683 // We want to keep this message around in case the screen was off
684 hideTransientIndicationDelayed(HIDE_DELAY_MS);
686 mMessageToShowOnScreenOn = errString;
690 private void animatePadlockError() {
691 mLockIcon.setTransientBiometricsError(true);
692 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
693 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
694 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
697 private boolean shouldSuppressBiometricError(int msgId,
698 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
699 if (biometricSourceType == BiometricSourceType.FINGERPRINT)
700 return shouldSuppressFingerprintError(msgId, updateMonitor);
701 if (biometricSourceType == BiometricSourceType.FACE)
702 return shouldSuppressFaceError(msgId, updateMonitor);
706 private boolean shouldSuppressFingerprintError(int msgId,
707 KeyguardUpdateMonitor updateMonitor) {
708 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
709 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
710 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
713 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
714 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
715 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
716 || msgId == FaceManager.FACE_ERROR_CANCELED);
720 public void onTrustAgentErrorMessage(CharSequence message) {
721 showTransientIndication(message, Utils.getColorError(mContext));
725 public void onScreenTurnedOn() {
726 if (mMessageToShowOnScreenOn != null) {
727 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
728 // We want to keep this message around in case the screen was off
729 hideTransientIndicationDelayed(HIDE_DELAY_MS);
730 mMessageToShowOnScreenOn = null;
735 public void onBiometricRunningStateChanged(boolean running,
736 BiometricSourceType biometricSourceType) {
738 // Let's hide any previous messages when authentication starts, otherwise
739 // multiple auth attempts would overlap.
740 hideTransientIndication();
741 mMessageToShowOnScreenOn = null;
746 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
747 super.onBiometricAuthenticated(userId, biometricSourceType);
748 mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
752 public void onUserUnlocked() {
754 updateIndication(false);