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.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; 41 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; 65 66 import java.io.FileDescriptor; 67 import java.io.PrintWriter; 68 import java.text.NumberFormat; 69 import java.util.IllegalFormatConversionException; 70 71 /** 72 * Controls the indications and error messages shown on the Keyguard 73 */ 74 public class KeyguardIndicationController implements StateListener, 75 UnlockMethodCache.OnUnlockMethodChangedListener { 76 77 private static final String TAG = "KeyguardIndication"; 78 private static final boolean DEBUG_CHARGING_SPEED = false; 79 80 private static final int MSG_HIDE_TRANSIENT = 1; 81 private static final int MSG_CLEAR_BIOMETRIC_MSG = 2; 82 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300; 83 84 private final Context mContext; 85 private final ShadeController mShadeController; 86 private final AccessibilityController mAccessibilityController; 87 private final UnlockMethodCache mUnlockMethodCache; 88 private final StatusBarStateController mStatusBarStateController; 89 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 90 private ViewGroup mIndicationArea; 91 private KeyguardIndicationTextView mTextView; 92 private KeyguardIndicationTextView mDisclosure; 93 private final UserManager mUserManager; 94 private final IBatteryStats mBatteryInfo; 95 private final SettableWakeLock mWakeLock; 96 private final LockPatternUtils mLockPatternUtils; 97 98 private final int mSlowThreshold; 99 private final int mFastThreshold; 100 private final LockIcon mLockIcon; 101 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 102 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger(); 103 104 private String mRestingIndication; 105 private CharSequence mTransientIndication; 106 private ColorStateList mTransientTextColorState; 107 private ColorStateList mInitialTextColorState; 108 private boolean mVisible; 109 110 private boolean mPowerPluggedIn; 111 private boolean mPowerPluggedInWired; 112 private boolean mPowerCharged; 113 private int mChargingSpeed; 114 private int mChargingWattage; 115 private int mBatteryLevel; 116 private String mMessageToShowOnScreenOn; 117 118 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; 119 120 private final DevicePolicyManager mDevicePolicyManager; 121 private boolean mDozing; 122 private final ViewClippingUtil.ClippingParameters mClippingParams = 123 new ViewClippingUtil.ClippingParameters() { 124 @Override 125 public boolean shouldFinish(View view) { 126 return view == mIndicationArea; 127 } 128 }; 129 130 /** 131 * Creates a new KeyguardIndicationController and registers callbacks. 132 */ KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon)133 public KeyguardIndicationController(Context context, ViewGroup indicationArea, 134 LockIcon lockIcon) { 135 this(context, indicationArea, lockIcon, new LockPatternUtils(context), 136 WakeLock.createPartial(context, "Doze:KeyguardIndication"), 137 Dependency.get(ShadeController.class), 138 Dependency.get(AccessibilityController.class), 139 UnlockMethodCache.getInstance(context), 140 Dependency.get(StatusBarStateController.class), 141 KeyguardUpdateMonitor.getInstance(context)); 142 } 143 144 /** 145 * Creates a new KeyguardIndicationController for testing. 146 */ 147 @VisibleForTesting KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon, LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController, AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache, StatusBarStateController statusBarStateController, KeyguardUpdateMonitor keyguardUpdateMonitor)148 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon, 149 LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController, 150 AccessibilityController accessibilityController, UnlockMethodCache unlockMethodCache, 151 StatusBarStateController statusBarStateController, 152 KeyguardUpdateMonitor keyguardUpdateMonitor) { 153 mContext = context; 154 mLockIcon = lockIcon; 155 mShadeController = shadeController; 156 mAccessibilityController = accessibilityController; 157 mUnlockMethodCache = unlockMethodCache; 158 mStatusBarStateController = statusBarStateController; 159 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 160 // lock icon is not used on all form factors. 161 if (mLockIcon != null) { 162 mLockIcon.setOnLongClickListener(this::handleLockLongClick); 163 mLockIcon.setOnClickListener(this::handleLockClick); 164 } 165 mWakeLock = new SettableWakeLock(wakeLock, TAG); 166 mLockPatternUtils = lockPatternUtils; 167 168 Resources res = context.getResources(); 169 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold); 170 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold); 171 172 mUserManager = context.getSystemService(UserManager.class); 173 mBatteryInfo = IBatteryStats.Stub.asInterface( 174 ServiceManager.getService(BatteryStats.SERVICE_NAME)); 175 176 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService( 177 Context.DEVICE_POLICY_SERVICE); 178 setIndicationArea(indicationArea); 179 updateDisclosure(); 180 181 mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback()); 182 mKeyguardUpdateMonitor.registerCallback(mTickReceiver); 183 mStatusBarStateController.addCallback(this); 184 mUnlockMethodCache.addListener(this); 185 } 186 setIndicationArea(ViewGroup indicationArea)187 public void setIndicationArea(ViewGroup indicationArea) { 188 mIndicationArea = indicationArea; 189 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text); 190 mInitialTextColorState = mTextView != null ? 191 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE); 192 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure); 193 updateIndication(false /* animate */); 194 } 195 handleLockLongClick(View view)196 private boolean handleLockLongClick(View view) { 197 mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK, 198 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); 199 showTransientIndication(R.string.keyguard_indication_trust_disabled); 200 mKeyguardUpdateMonitor.onLockIconPressed(); 201 mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser()); 202 203 return true; 204 } 205 handleLockClick(View view)206 private void handleLockClick(View view) { 207 if (!mAccessibilityController.isAccessibilityEnabled()) { 208 return; 209 } 210 mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */); 211 } 212 213 /** 214 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this 215 * {@link KeyguardIndicationController}. 216 * 217 * <p>Subclasses may override this method to extend or change the callback behavior by extending 218 * the {@link BaseKeyguardCallback}. 219 * 220 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the 221 * same instance. 222 */ getKeyguardCallback()223 protected KeyguardUpdateMonitorCallback getKeyguardCallback() { 224 if (mUpdateMonitorCallback == null) { 225 mUpdateMonitorCallback = new BaseKeyguardCallback(); 226 } 227 return mUpdateMonitorCallback; 228 } 229 updateDisclosure()230 private void updateDisclosure() { 231 if (mDevicePolicyManager == null) { 232 return; 233 } 234 235 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) { 236 final CharSequence organizationName = 237 mDevicePolicyManager.getDeviceOwnerOrganizationName(); 238 if (organizationName != null) { 239 mDisclosure.switchIndication(mContext.getResources().getString( 240 R.string.do_disclosure_with_name, organizationName)); 241 } else { 242 mDisclosure.switchIndication(R.string.do_disclosure_generic); 243 } 244 mDisclosure.setVisibility(View.VISIBLE); 245 } else { 246 mDisclosure.setVisibility(View.GONE); 247 } 248 } 249 setVisible(boolean visible)250 public void setVisible(boolean visible) { 251 mVisible = visible; 252 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE); 253 if (visible) { 254 // If this is called after an error message was already shown, we should not clear it. 255 // Otherwise the error message won't be shown 256 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) { 257 hideTransientIndication(); 258 } 259 updateIndication(false); 260 } else if (!visible) { 261 // If we unlock and return to keyguard quickly, previous error should not be shown 262 hideTransientIndication(); 263 } 264 } 265 266 /** 267 * Sets the indication that is shown if nothing else is showing. 268 */ setRestingIndication(String restingIndication)269 public void setRestingIndication(String restingIndication) { 270 mRestingIndication = restingIndication; 271 updateIndication(false); 272 } 273 274 /** 275 * Sets the active controller managing changes and callbacks to user information. 276 */ setUserInfoController(UserInfoController userInfoController)277 public void setUserInfoController(UserInfoController userInfoController) { 278 } 279 280 /** 281 * Returns the indication text indicating that trust has been granted. 282 * 283 * @return {@code null} or an empty string if a trust indication text should not be shown. 284 */ 285 @VisibleForTesting getTrustGrantedIndication()286 String getTrustGrantedIndication() { 287 return mContext.getString(R.string.keyguard_indication_trust_unlocked); 288 } 289 290 /** 291 * Returns the indication text indicating that trust is currently being managed. 292 * 293 * @return {@code null} or an empty string if a trust managed text should not be shown. 294 */ getTrustManagedIndication()295 private String getTrustManagedIndication() { 296 return null; 297 } 298 299 /** 300 * Hides transient indication in {@param delayMs}. 301 */ hideTransientIndicationDelayed(long delayMs)302 public void hideTransientIndicationDelayed(long delayMs) { 303 mHandler.sendMessageDelayed( 304 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs); 305 } 306 307 /** 308 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 309 */ showTransientIndication(int transientIndication)310 public void showTransientIndication(int transientIndication) { 311 showTransientIndication(mContext.getResources().getString(transientIndication)); 312 } 313 314 /** 315 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 316 */ showTransientIndication(CharSequence transientIndication)317 public void showTransientIndication(CharSequence transientIndication) { 318 showTransientIndication(transientIndication, mInitialTextColorState); 319 } 320 321 /** 322 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}. 323 */ showTransientIndication(CharSequence transientIndication, ColorStateList textColorState)324 public void showTransientIndication(CharSequence transientIndication, 325 ColorStateList textColorState) { 326 mTransientIndication = transientIndication; 327 mTransientTextColorState = textColorState; 328 mHandler.removeMessages(MSG_HIDE_TRANSIENT); 329 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) { 330 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared. 331 mWakeLock.setAcquired(true); 332 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS); 333 } 334 335 updateIndication(false); 336 } 337 338 /** 339 * Hides transient indication. 340 */ hideTransientIndication()341 public void hideTransientIndication() { 342 if (mTransientIndication != null) { 343 mTransientIndication = null; 344 mHandler.removeMessages(MSG_HIDE_TRANSIENT); 345 updateIndication(false); 346 } 347 } 348 updateIndication(boolean animate)349 protected final void updateIndication(boolean animate) { 350 if (TextUtils.isEmpty(mTransientIndication)) { 351 mWakeLock.setAcquired(false); 352 } 353 354 if (mVisible) { 355 // Walk down a precedence-ordered list of what indication 356 // should be shown based on user or device state 357 if (mDozing) { 358 // When dozing we ignore any text color and use white instead, because 359 // colors can be hard to read in low brightness. 360 mTextView.setTextColor(Color.WHITE); 361 if (!TextUtils.isEmpty(mTransientIndication)) { 362 mTextView.switchIndication(mTransientIndication); 363 } else if (mPowerPluggedIn) { 364 String indication = computePowerIndication(); 365 if (animate) { 366 animateText(mTextView, indication); 367 } else { 368 mTextView.switchIndication(indication); 369 } 370 } else { 371 String percentage = NumberFormat.getPercentInstance() 372 .format(mBatteryLevel / 100f); 373 mTextView.switchIndication(percentage); 374 } 375 return; 376 } 377 378 int userId = KeyguardUpdateMonitor.getCurrentUser(); 379 String trustGrantedIndication = getTrustGrantedIndication(); 380 String trustManagedIndication = getTrustManagedIndication(); 381 if (!mUserManager.isUserUnlocked(userId)) { 382 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked); 383 mTextView.setTextColor(mInitialTextColorState); 384 } else if (!TextUtils.isEmpty(mTransientIndication)) { 385 mTextView.switchIndication(mTransientIndication); 386 mTextView.setTextColor(mTransientTextColorState); 387 } else if (!TextUtils.isEmpty(trustGrantedIndication) 388 && mKeyguardUpdateMonitor.getUserHasTrust(userId)) { 389 mTextView.switchIndication(trustGrantedIndication); 390 mTextView.setTextColor(mInitialTextColorState); 391 } else if (mPowerPluggedIn) { 392 String indication = computePowerIndication(); 393 if (DEBUG_CHARGING_SPEED) { 394 indication += ", " + (mChargingWattage / 1000) + " mW"; 395 } 396 mTextView.setTextColor(mInitialTextColorState); 397 if (animate) { 398 animateText(mTextView, indication); 399 } else { 400 mTextView.switchIndication(indication); 401 } 402 } else if (!TextUtils.isEmpty(trustManagedIndication) 403 && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId) 404 && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) { 405 mTextView.switchIndication(trustManagedIndication); 406 mTextView.setTextColor(mInitialTextColorState); 407 } else { 408 mTextView.switchIndication(mRestingIndication); 409 mTextView.setTextColor(mInitialTextColorState); 410 } 411 } 412 } 413 414 // animates textView - textView moves up and bounces down animateText(KeyguardIndicationTextView textView, String indication)415 private void animateText(KeyguardIndicationTextView textView, String indication) { 416 int yTranslation = mContext.getResources().getInteger( 417 R.integer.wired_charging_keyguard_text_animation_distance); 418 int animateUpDuration = mContext.getResources().getInteger( 419 R.integer.wired_charging_keyguard_text_animation_duration_up); 420 int animateDownDuration = mContext.getResources().getInteger( 421 R.integer.wired_charging_keyguard_text_animation_duration_down); 422 textView.animate().cancel(); 423 float translation = textView.getTranslationY(); 424 ViewClippingUtil.setClippingDeactivated(textView, true, mClippingParams); 425 textView.animate() 426 .translationYBy(yTranslation) 427 .setInterpolator(Interpolators.LINEAR) 428 .setDuration(animateUpDuration) 429 .setListener(new AnimatorListenerAdapter() { 430 private boolean mCancelled; 431 432 @Override 433 public void onAnimationStart(Animator animation) { 434 textView.switchIndication(indication); 435 } 436 437 @Override 438 public void onAnimationCancel(Animator animation) { 439 textView.setTranslationY(translation); 440 mCancelled = true; 441 } 442 443 @Override 444 public void onAnimationEnd(Animator animation) { 445 if (mCancelled) { 446 ViewClippingUtil.setClippingDeactivated(textView, false, 447 mClippingParams); 448 return; 449 } 450 textView.animate() 451 .setDuration(animateDownDuration) 452 .setInterpolator(Interpolators.BOUNCE) 453 .translationY(translation) 454 .setListener(new AnimatorListenerAdapter() { 455 @Override 456 public void onAnimationCancel(Animator animation) { 457 textView.setTranslationY(translation); 458 } 459 460 @Override 461 public void onAnimationEnd(Animator animation) { 462 ViewClippingUtil.setClippingDeactivated(textView, false, 463 mClippingParams); 464 } 465 }); 466 } 467 }); 468 } 469 computePowerIndication()470 private String computePowerIndication() { 471 if (mPowerCharged) { 472 return mContext.getResources().getString(R.string.keyguard_charged); 473 } 474 475 // Try fetching charging time from battery stats. 476 long chargingTimeRemaining = 0; 477 try { 478 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining(); 479 480 } catch (RemoteException e) { 481 Log.e(TAG, "Error calling IBatteryStats: ", e); 482 } 483 final boolean hasChargingTime = chargingTimeRemaining > 0; 484 485 int chargingId; 486 if (mPowerPluggedInWired) { 487 switch (mChargingSpeed) { 488 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: 489 chargingId = hasChargingTime 490 ? R.string.keyguard_indication_charging_time_fast 491 : R.string.keyguard_plugged_in_charging_fast; 492 break; 493 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: 494 chargingId = hasChargingTime 495 ? R.string.keyguard_indication_charging_time_slowly 496 : R.string.keyguard_plugged_in_charging_slowly; 497 break; 498 default: 499 chargingId = hasChargingTime 500 ? R.string.keyguard_indication_charging_time 501 : R.string.keyguard_plugged_in; 502 break; 503 } 504 } else { 505 chargingId = hasChargingTime 506 ? R.string.keyguard_indication_charging_time_wireless 507 : R.string.keyguard_plugged_in_wireless; 508 } 509 510 String percentage = NumberFormat.getPercentInstance() 511 .format(mBatteryLevel / 100f); 512 if (hasChargingTime) { 513 // We now have battery percentage in these strings and it's expected that all 514 // locales will also have it in the future. For now, we still have to support the old 515 // format until all languages get the new translations. 516 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes( 517 mContext, chargingTimeRemaining); 518 try { 519 return mContext.getResources().getString(chargingId, chargingTimeFormatted, 520 percentage); 521 } catch (IllegalFormatConversionException e) { 522 return mContext.getResources().getString(chargingId, chargingTimeFormatted); 523 } 524 } else { 525 // Same as above 526 try { 527 return mContext.getResources().getString(chargingId, percentage); 528 } catch (IllegalFormatConversionException e) { 529 return mContext.getResources().getString(chargingId); 530 } 531 } 532 } 533 setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)534 public void setStatusBarKeyguardViewManager( 535 StatusBarKeyguardViewManager statusBarKeyguardViewManager) { 536 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 537 } 538 539 private final KeyguardUpdateMonitorCallback mTickReceiver = 540 new KeyguardUpdateMonitorCallback() { 541 @Override 542 public void onTimeChanged() { 543 if (mVisible) { 544 updateIndication(false /* animate */); 545 } 546 } 547 }; 548 549 private final Handler mHandler = new Handler() { 550 @Override 551 public void handleMessage(Message msg) { 552 if (msg.what == MSG_HIDE_TRANSIENT) { 553 hideTransientIndication(); 554 } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) { 555 mLockIcon.setTransientBiometricsError(false); 556 } 557 } 558 }; 559 setDozing(boolean dozing)560 public void setDozing(boolean dozing) { 561 if (mDozing == dozing) { 562 return; 563 } 564 mDozing = dozing; 565 updateIndication(false); 566 updateDisclosure(); 567 } 568 dump(FileDescriptor fd, PrintWriter pw, String[] args)569 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 570 pw.println("KeyguardIndicationController:"); 571 pw.println(" mTransientTextColorState: " + mTransientTextColorState); 572 pw.println(" mInitialTextColorState: " + mInitialTextColorState); 573 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired); 574 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn); 575 pw.println(" mPowerCharged: " + mPowerCharged); 576 pw.println(" mChargingSpeed: " + mChargingSpeed); 577 pw.println(" mChargingWattage: " + mChargingWattage); 578 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn); 579 pw.println(" mDozing: " + mDozing); 580 pw.println(" mBatteryLevel: " + mBatteryLevel); 581 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText())); 582 pw.println(" computePowerIndication(): " + computePowerIndication()); 583 } 584 585 @Override onStateChanged(int newState)586 public void onStateChanged(int newState) { 587 // don't care 588 } 589 590 @Override onDozingChanged(boolean isDozing)591 public void onDozingChanged(boolean isDozing) { 592 setDozing(isDozing); 593 } 594 595 @Override onUnlockMethodStateChanged()596 public void onUnlockMethodStateChanged() { 597 updateIndication(!mDozing); 598 } 599 600 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback { 601 public static final int HIDE_DELAY_MS = 5000; 602 603 @Override onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status)604 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) { 605 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING 606 || status.status == BatteryManager.BATTERY_STATUS_FULL; 607 boolean wasPluggedIn = mPowerPluggedIn; 608 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull; 609 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull; 610 mPowerCharged = status.isCharged(); 611 mChargingWattage = status.maxChargingWattage; 612 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold); 613 mBatteryLevel = status.level; 614 updateIndication(!wasPluggedIn && mPowerPluggedInWired); 615 if (mDozing) { 616 if (!wasPluggedIn && mPowerPluggedIn) { 617 showTransientIndication(computePowerIndication()); 618 hideTransientIndicationDelayed(HIDE_DELAY_MS); 619 } else if (wasPluggedIn && !mPowerPluggedIn) { 620 hideTransientIndication(); 621 } 622 } 623 } 624 625 @Override onKeyguardVisibilityChanged(boolean showing)626 public void onKeyguardVisibilityChanged(boolean showing) { 627 if (showing) { 628 updateDisclosure(); 629 } 630 } 631 632 @Override onBiometricHelp(int msgId, String helpString, BiometricSourceType biometricSourceType)633 public void onBiometricHelp(int msgId, String helpString, 634 BiometricSourceType biometricSourceType) { 635 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 636 if (!updateMonitor.isUnlockingWithBiometricAllowed()) { 637 return; 638 } 639 animatePadlockError(); 640 if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 641 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, 642 mInitialTextColorState); 643 } else if (updateMonitor.isScreenOn()) { 644 showTransientIndication(helpString); 645 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); 646 } 647 } 648 649 @Override onBiometricError(int msgId, String errString, BiometricSourceType biometricSourceType)650 public void onBiometricError(int msgId, String errString, 651 BiometricSourceType biometricSourceType) { 652 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 653 if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) { 654 return; 655 } 656 animatePadlockError(); 657 if (mStatusBarKeyguardViewManager.isBouncerShowing()) { 658 mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState); 659 } else if (updateMonitor.isScreenOn()) { 660 showTransientIndication(errString); 661 // We want to keep this message around in case the screen was off 662 hideTransientIndicationDelayed(HIDE_DELAY_MS); 663 } else { 664 mMessageToShowOnScreenOn = errString; 665 } 666 } 667 animatePadlockError()668 private void animatePadlockError() { 669 mLockIcon.setTransientBiometricsError(true); 670 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG); 671 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG), 672 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT); 673 } 674 shouldSuppressBiometricError(int msgId, BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor)675 private boolean shouldSuppressBiometricError(int msgId, 676 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) { 677 if (biometricSourceType == BiometricSourceType.FINGERPRINT) 678 return shouldSuppressFingerprintError(msgId, updateMonitor); 679 if (biometricSourceType == BiometricSourceType.FACE) 680 return shouldSuppressFaceError(msgId, updateMonitor); 681 return false; 682 } 683 shouldSuppressFingerprintError(int msgId, KeyguardUpdateMonitor updateMonitor)684 private boolean shouldSuppressFingerprintError(int msgId, 685 KeyguardUpdateMonitor updateMonitor) { 686 return ((!updateMonitor.isUnlockingWithBiometricAllowed() 687 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT) 688 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED); 689 } 690 shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor)691 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) { 692 return ((!updateMonitor.isUnlockingWithBiometricAllowed() 693 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) 694 || msgId == FaceManager.FACE_ERROR_CANCELED); 695 } 696 697 @Override onTrustAgentErrorMessage(CharSequence message)698 public void onTrustAgentErrorMessage(CharSequence message) { 699 showTransientIndication(message, Utils.getColorError(mContext)); 700 } 701 702 @Override onScreenTurnedOn()703 public void onScreenTurnedOn() { 704 if (mMessageToShowOnScreenOn != null) { 705 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext)); 706 // We want to keep this message around in case the screen was off 707 hideTransientIndicationDelayed(HIDE_DELAY_MS); 708 mMessageToShowOnScreenOn = null; 709 } 710 } 711 712 @Override onBiometricRunningStateChanged(boolean running, BiometricSourceType biometricSourceType)713 public void onBiometricRunningStateChanged(boolean running, 714 BiometricSourceType biometricSourceType) { 715 if (running) { 716 // Let's hide any previous messages when authentication starts, otherwise 717 // multiple auth attempts would overlap. 718 hideTransientIndication(); 719 mMessageToShowOnScreenOn = null; 720 } 721 } 722 723 @Override onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType)724 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { 725 super.onBiometricAuthenticated(userId, biometricSourceType); 726 mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT); 727 } 728 729 @Override onUserUnlocked()730 public void onUserUnlocked() { 731 if (mVisible) { 732 updateIndication(false); 733 } 734 } 735 736 @Override onKeyguardBouncerChanged(boolean bouncer)737 public void onKeyguardBouncerChanged(boolean bouncer) { 738 if (mLockIcon == null) { 739 return; 740 } 741 mLockIcon.setBouncerVisible(bouncer); 742 } 743 }; 744 } 745