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