OSDN Git Service

Merge "docs: Add documentation for equals() method" into qt-dev am: 732a127636
[android-x86/frameworks-base.git] / packages / SystemUI / src / com / android / systemui / statusbar / KeyguardIndicationController.java
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 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;
85
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;
99
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();
105
106     private String mRestingIndication;
107     private CharSequence mTransientIndication;
108     private ColorStateList mTransientTextColorState;
109     private ColorStateList mInitialTextColorState;
110     private boolean mVisible;
111
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;
119
120     private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
121
122     private final DevicePolicyManager mDevicePolicyManager;
123     private boolean mDozing;
124     private final ViewClippingUtil.ClippingParameters mClippingParams =
125             new ViewClippingUtil.ClippingParameters() {
126                 @Override
127                 public boolean shouldFinish(View view) {
128                     return view == mIndicationArea;
129                 }
130             };
131
132     /**
133      * Creates a new KeyguardIndicationController and registers callbacks.
134      */
135     public KeyguardIndicationController(Context context, ViewGroup indicationArea,
136             LockIcon lockIcon) {
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));
144     }
145
146     /**
147      * Creates a new KeyguardIndicationController for testing.
148      */
149     @VisibleForTesting
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) {
155         mContext = context;
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);
166         }
167         mWakeLock = new SettableWakeLock(wakeLock, TAG);
168         mLockPatternUtils = lockPatternUtils;
169
170         Resources res = context.getResources();
171         mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
172         mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
173
174         mUserManager = context.getSystemService(UserManager.class);
175         mBatteryInfo = IBatteryStats.Stub.asInterface(
176                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
177
178         mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
179                 Context.DEVICE_POLICY_SERVICE);
180         setIndicationArea(indicationArea);
181         updateDisclosure();
182
183         mKeyguardUpdateMonitor.registerCallback(getKeyguardCallback());
184         mKeyguardUpdateMonitor.registerCallback(mTickReceiver);
185         mStatusBarStateController.addCallback(this);
186         mUnlockMethodCache.addListener(this);
187     }
188
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 */);
196     }
197
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());
204
205         return true;
206     }
207
208     private void handleLockClick(View view) {
209         if (!mAccessibilityController.isAccessibilityEnabled()) {
210             return;
211         }
212         mShadeController.animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_NONE, true /* force */);
213     }
214
215     /**
216      * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
217      * {@link KeyguardIndicationController}.
218      *
219      * <p>Subclasses may override this method to extend or change the callback behavior by extending
220      * the {@link BaseKeyguardCallback}.
221      *
222      * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
223      * same instance.
224      */
225     protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
226         if (mUpdateMonitorCallback == null) {
227             mUpdateMonitorCallback = new BaseKeyguardCallback();
228         }
229         return mUpdateMonitorCallback;
230     }
231
232     private void updateDisclosure() {
233         if (mDevicePolicyManager == null) {
234             return;
235         }
236
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));
243             } else {
244                 mDisclosure.switchIndication(R.string.do_disclosure_generic);
245             }
246             mDisclosure.setVisibility(View.VISIBLE);
247         } else {
248             mDisclosure.setVisibility(View.GONE);
249         }
250     }
251
252     public void setVisible(boolean visible) {
253         mVisible = visible;
254         mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
255         if (visible) {
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();
260             }
261             updateIndication(false);
262         } else if (!visible) {
263             // If we unlock and return to keyguard quickly, previous error should not be shown
264             hideTransientIndication();
265         }
266     }
267
268     /**
269      * Sets the indication that is shown if nothing else is showing.
270      */
271     public void setRestingIndication(String restingIndication) {
272         mRestingIndication = restingIndication;
273         updateIndication(false);
274     }
275
276     /**
277      * Sets the active controller managing changes and callbacks to user information.
278      */
279     public void setUserInfoController(UserInfoController userInfoController) {
280     }
281
282     /**
283      * Returns the indication text indicating that trust has been granted.
284      *
285      * @return {@code null} or an empty string if a trust indication text should not be shown.
286      */
287     @VisibleForTesting
288     String getTrustGrantedIndication() {
289         return mContext.getString(R.string.keyguard_indication_trust_unlocked);
290     }
291
292     /**
293      * Returns the indication text indicating that trust is currently being managed.
294      *
295      * @return {@code null} or an empty string if a trust managed text should not be shown.
296      */
297     private String getTrustManagedIndication() {
298         return null;
299     }
300
301     /**
302      * Hides transient indication in {@param delayMs}.
303      */
304     public void hideTransientIndicationDelayed(long delayMs) {
305         mHandler.sendMessageDelayed(
306                 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
307     }
308
309     /**
310      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
311      */
312     public void showTransientIndication(int transientIndication) {
313         showTransientIndication(mContext.getResources().getString(transientIndication));
314     }
315
316     /**
317      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
318      */
319     public void showTransientIndication(CharSequence transientIndication) {
320         showTransientIndication(transientIndication, mInitialTextColorState);
321     }
322
323     /**
324      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
325      */
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);
336         }
337
338         updateIndication(false);
339     }
340
341     /**
342      * Hides transient indication.
343      */
344     public void hideTransientIndication() {
345         if (mTransientIndication != null) {
346             mTransientIndication = null;
347             mHandler.removeMessages(MSG_HIDE_TRANSIENT);
348             updateIndication(false);
349         }
350     }
351
352     protected final void updateIndication(boolean animate) {
353         if (TextUtils.isEmpty(mTransientIndication)) {
354             mWakeLock.setAcquired(false);
355         }
356
357         if (mVisible) {
358             // Walk down a precedence-ordered list of what indication
359             // should be shown based on user or device state
360             if (mDozing) {
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();
368                     if (animate) {
369                         animateText(mTextView, indication);
370                     } else {
371                         mTextView.switchIndication(indication);
372                     }
373                 } else {
374                     String percentage = NumberFormat.getPercentInstance()
375                             .format(mBatteryLevel / 100f);
376                     mTextView.switchIndication(percentage);
377                 }
378                 return;
379             }
380
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";
398                 }
399                 mTextView.setTextColor(mInitialTextColorState);
400                 if (animate) {
401                     animateText(mTextView, indication);
402                 } else {
403                     mTextView.switchIndication(indication);
404                 }
405             } else if (!TextUtils.isEmpty(trustManagedIndication)
406                     && mKeyguardUpdateMonitor.getUserTrustIsManaged(userId)
407                     && !mKeyguardUpdateMonitor.getUserHasTrust(userId)) {
408                 mTextView.switchIndication(trustManagedIndication);
409                 mTextView.setTextColor(mInitialTextColorState);
410             } else {
411                 mTextView.switchIndication(mRestingIndication);
412                 mTextView.setTextColor(mInitialTextColorState);
413             }
414         }
415     }
416
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);
427         textView.animate()
428                 .translationYBy(yTranslation)
429                 .setInterpolator(Interpolators.LINEAR)
430                 .setDuration(animateUpDuration)
431                 .setListener(new AnimatorListenerAdapter() {
432                     private boolean mCancelled;
433
434                     @Override
435                     public void onAnimationStart(Animator animation) {
436                         textView.switchIndication(indication);
437                     }
438
439                     @Override
440                     public void onAnimationCancel(Animator animation) {
441                         textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
442                         mCancelled = true;
443                     }
444
445                     @Override
446                     public void onAnimationEnd(Animator animation) {
447                         if (mCancelled) {
448                             ViewClippingUtil.setClippingDeactivated(textView, false,
449                                     mClippingParams);
450                             return;
451                         }
452                         textView.animate()
453                                 .setDuration(animateDownDuration)
454                                 .setInterpolator(Interpolators.BOUNCE)
455                                 .translationY(BOUNCE_ANIMATION_FINAL_Y)
456                                 .setListener(new AnimatorListenerAdapter() {
457                                     @Override
458                                     public void onAnimationEnd(Animator animation) {
459                                         textView.setTranslationY(BOUNCE_ANIMATION_FINAL_Y);
460                                         ViewClippingUtil.setClippingDeactivated(textView, false,
461                                                 mClippingParams);
462                                     }
463                                 });
464                     }
465                 });
466     }
467
468     private String computePowerIndication() {
469         if (mPowerCharged) {
470             return mContext.getResources().getString(R.string.keyguard_charged);
471         }
472
473         // Try fetching charging time from battery stats.
474         long chargingTimeRemaining = 0;
475         try {
476             chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
477
478         } catch (RemoteException e) {
479             Log.e(TAG, "Error calling IBatteryStats: ", e);
480         }
481         final boolean hasChargingTime = chargingTimeRemaining > 0;
482
483         int chargingId;
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;
490                     break;
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;
495                     break;
496                 default:
497                     chargingId = hasChargingTime
498                             ? R.string.keyguard_indication_charging_time
499                             : R.string.keyguard_plugged_in;
500                     break;
501             }
502         } else {
503             chargingId = hasChargingTime
504                     ? R.string.keyguard_indication_charging_time_wireless
505                     : R.string.keyguard_plugged_in_wireless;
506         }
507
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);
516             try {
517                 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
518                         percentage);
519             } catch (IllegalFormatConversionException e) {
520                 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
521             }
522         } else {
523             // Same as above
524             try {
525                 return mContext.getResources().getString(chargingId, percentage);
526             } catch (IllegalFormatConversionException e) {
527                 return mContext.getResources().getString(chargingId);
528             }
529         }
530     }
531
532     public void setStatusBarKeyguardViewManager(
533             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
534         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
535     }
536
537     private final KeyguardUpdateMonitorCallback mTickReceiver =
538             new KeyguardUpdateMonitorCallback() {
539                 @Override
540                 public void onTimeChanged() {
541                     if (mVisible) {
542                         updateIndication(false /* animate */);
543                     }
544                 }
545             };
546
547     private final Handler mHandler = new Handler() {
548         @Override
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();
556             }
557         }
558     };
559
560     private void showSwipeUpToUnlock() {
561         if (mDozing) {
562             return;
563         }
564
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);
571         }
572     }
573
574     public void setDozing(boolean dozing) {
575         if (mDozing == dozing) {
576             return;
577         }
578         mDozing = dozing;
579         updateIndication(false);
580         updateDisclosure();
581     }
582
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());
597     }
598
599     @Override
600     public void onStateChanged(int newState) {
601         // don't care
602     }
603
604     @Override
605     public void onDozingChanged(boolean isDozing) {
606         setDozing(isDozing);
607     }
608
609     @Override
610     public void onUnlockMethodStateChanged() {
611         updateIndication(!mDozing);
612     }
613
614     protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
615         public static final int HIDE_DELAY_MS = 5000;
616
617         @Override
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);
629             if (mDozing) {
630                 if (!wasPluggedIn && mPowerPluggedIn) {
631                     showTransientIndication(computePowerIndication());
632                     hideTransientIndicationDelayed(HIDE_DELAY_MS);
633                 } else if (wasPluggedIn && !mPowerPluggedIn) {
634                     hideTransientIndication();
635                 }
636             }
637         }
638
639         @Override
640         public void onKeyguardVisibilityChanged(boolean showing) {
641             if (showing) {
642                 updateDisclosure();
643             }
644         }
645
646         @Override
647         public void onBiometricHelp(int msgId, String helpString,
648                 BiometricSourceType biometricSourceType) {
649             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
650             if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
651                 return;
652             }
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);
663                 }
664             }
665             if (showSwipeToUnlock) {
666                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SWIPE_UP_TO_UNLOCK),
667                         TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
668             }
669         }
670
671         @Override
672         public void onBiometricError(int msgId, String errString,
673                 BiometricSourceType biometricSourceType) {
674             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
675             if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
676                 return;
677             }
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);
685             } else {
686                 mMessageToShowOnScreenOn = errString;
687             }
688         }
689
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);
695         }
696
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);
703             return false;
704         }
705
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);
711         }
712
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);
717         }
718
719         @Override
720         public void onTrustAgentErrorMessage(CharSequence message) {
721             showTransientIndication(message, Utils.getColorError(mContext));
722         }
723
724         @Override
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;
731             }
732         }
733
734         @Override
735         public void onBiometricRunningStateChanged(boolean running,
736                 BiometricSourceType biometricSourceType) {
737             if (running) {
738                 // Let's hide any previous messages when authentication starts, otherwise
739                 // multiple auth attempts would overlap.
740                 hideTransientIndication();
741                 mMessageToShowOnScreenOn = null;
742             }
743         }
744
745         @Override
746         public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
747             super.onBiometricAuthenticated(userId, biometricSourceType);
748             mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
749         }
750
751         @Override
752         public void onUserUnlocked() {
753             if (mVisible) {
754                 updateIndication(false);
755             }
756         }
757     }
758 }