blob: a00eac4adea0cc163d4cc026064dd10d0a63afcd [file] [log] [blame]
Adrian Roos12c1ef52014-06-04 13:54:08 +02001/*
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
17package com.android.systemui.statusbar;
18
Beverly8c785892018-01-31 17:25:52 -050019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010021import android.app.admin.DevicePolicyManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020022import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
Jason Chang2386a372018-04-24 16:05:30 +080026import android.content.res.ColorStateList;
Lucas Dupinff6628d2018-10-15 10:12:37 -070027import android.content.res.Resources;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070028import android.graphics.Color;
Lucas Dupinff6628d2018-10-15 10:12:37 -070029import android.hardware.biometrics.BiometricSourceType;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020030import android.hardware.face.FaceManager;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070031import android.hardware.fingerprint.FingerprintManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020032import android.os.BatteryManager;
33import android.os.BatteryStats;
34import android.os.Handler;
35import android.os.Message;
36import android.os.RemoteException;
37import android.os.ServiceManager;
38import android.os.UserHandle;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060039import android.os.UserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020040import android.text.TextUtils;
41import android.text.format.Formatter;
42import android.util.Log;
43import android.view.View;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010044import android.view.ViewGroup;
Adrian Roos12c1ef52014-06-04 13:54:08 +020045
Adrian Roosc1b50322017-02-27 21:07:58 +010046import com.android.internal.annotations.VisibleForTesting;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070047import com.android.internal.app.IBatteryStats;
48import com.android.keyguard.KeyguardUpdateMonitor;
49import com.android.keyguard.KeyguardUpdateMonitorCallback;
Jason Monk58be7a62017-02-01 20:17:51 -050050import com.android.settingslib.Utils;
Jason Monk9c7844c2017-01-18 15:21:53 -050051import com.android.systemui.Dependency;
Beverly8c785892018-01-31 17:25:52 -050052import com.android.systemui.Interpolators;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070053import com.android.systemui.R;
54import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
55import com.android.systemui.statusbar.phone.LockIcon;
56import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080057import com.android.systemui.statusbar.policy.UserInfoController;
Adrian Roosc1b50322017-02-27 21:07:58 +010058import com.android.systemui.util.wakelock.SettableWakeLock;
59import com.android.systemui.util.wakelock.WakeLock;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070060
Lucas Dupin3fcdd472018-01-19 19:06:45 -080061import java.io.FileDescriptor;
62import java.io.PrintWriter;
Lucas Dupin4272f442018-01-13 22:00:35 -080063import java.text.NumberFormat;
Lucas Dupin8d595d22018-03-08 10:34:58 -080064import java.util.IllegalFormatConversionException;
Lucas Dupin4272f442018-01-13 22:00:35 -080065
Adrian Roos12c1ef52014-06-04 13:54:08 +020066/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070067 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020068 */
69public class KeyguardIndicationController {
70
Adrian Roos0c859ae2015-11-23 16:47:50 -080071 private static final String TAG = "KeyguardIndication";
72 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020073
74 private static final int MSG_HIDE_TRANSIENT = 1;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020075 private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
76 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020077
78 private final Context mContext;
Lucas Dupin987f1932017-05-13 21:02:52 -070079 private ViewGroup mIndicationArea;
80 private KeyguardIndicationTextView mTextView;
81 private KeyguardIndicationTextView mDisclosure;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060082 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020083 private final IBatteryStats mBatteryInfo;
Adrian Roosc1b50322017-02-27 21:07:58 +010084 private final SettableWakeLock mWakeLock;
Adrian Roos12c1ef52014-06-04 13:54:08 +020085
Adrian Roos7b043112015-07-10 13:00:33 -070086 private final int mSlowThreshold;
87 private final int mFastThreshold;
Lucas Dupin987f1932017-05-13 21:02:52 -070088 private LockIcon mLockIcon;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070089 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Adrian Roos7b043112015-07-10 13:00:33 -070090
Adrian Roos12c1ef52014-06-04 13:54:08 +020091 private String mRestingIndication;
Lucas Dupinef886542018-01-03 16:03:07 -080092 private CharSequence mTransientIndication;
Jason Chang2386a372018-04-24 16:05:30 +080093 private ColorStateList mTransientTextColorState;
94 private ColorStateList mInitialTextColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +020095 private boolean mVisible;
96
97 private boolean mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -040098 private boolean mPowerPluggedInWired;
Adrian Roos12c1ef52014-06-04 13:54:08 +020099 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -0700100 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -0800101 private int mChargingWattage;
Lucas Dupin4272f442018-01-13 22:00:35 -0800102 private int mBatteryLevel;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700103 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200104
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700105 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800106
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100107 private final DevicePolicyManager mDevicePolicyManager;
Adrian Roos91ba3072017-02-14 16:50:46 +0100108 private boolean mDozing;
Lucas Dupinff6628d2018-10-15 10:12:37 -0700109 private float mDarkAmount;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100110
Adrian Roosaf45b602017-03-14 13:10:25 -0700111 /**
112 * Creates a new KeyguardIndicationController and registers callbacks.
113 */
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100114 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
115 LockIcon lockIcon) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100116 this(context, indicationArea, lockIcon,
117 WakeLock.createPartial(context, "Doze:KeyguardIndication"));
Adrian Roosaf45b602017-03-14 13:10:25 -0700118
119 registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
Adrian Roosc1b50322017-02-27 21:07:58 +0100120 }
121
Adrian Roosaf45b602017-03-14 13:10:25 -0700122 /**
123 * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
124 */
Adrian Roosc1b50322017-02-27 21:07:58 +0100125 @VisibleForTesting
126 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
127 WakeLock wakeLock) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200128 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100129 mIndicationArea = indicationArea;
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800130 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
Jason Chang2386a372018-04-24 16:05:30 +0800131 mInitialTextColorState = mTextView != null ?
132 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800133 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700134 mLockIcon = lockIcon;
Adrian Roosc1b50322017-02-27 21:07:58 +0100135 mWakeLock = new SettableWakeLock(wakeLock);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200136
Adrian Roos7b043112015-07-10 13:00:33 -0700137 Resources res = context.getResources();
138 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
139 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
140
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600141 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200142 mBatteryInfo = IBatteryStats.Stub.asInterface(
143 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600144
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100145 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
146 Context.DEVICE_POLICY_SERVICE);
147
Adrian Roosaf45b602017-03-14 13:10:25 -0700148 updateDisclosure();
149 }
150
151 private void registerCallbacks(KeyguardUpdateMonitor monitor) {
152 monitor.registerCallback(getKeyguardCallback());
153
154 mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
Jason Monkcd26af72017-01-11 14:32:58 -0500155 new IntentFilter(Intent.ACTION_TIME_TICK), null,
Jason Monk9c7844c2017-01-18 15:21:53 -0500156 Dependency.get(Dependency.TIME_TICK_HANDLER));
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100157 }
158
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800159 /**
160 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
161 * {@link KeyguardIndicationController}.
162 *
163 * <p>Subclasses may override this method to extend or change the callback behavior by extending
164 * the {@link BaseKeyguardCallback}.
165 *
166 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
167 * same instance.
168 */
169 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700170 if (mUpdateMonitorCallback == null) {
171 mUpdateMonitorCallback = new BaseKeyguardCallback();
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800172 }
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700173 return mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800174 }
175
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100176 private void updateDisclosure() {
177 if (mDevicePolicyManager == null) {
178 return;
179 }
180
Adrian Roos91ba3072017-02-14 16:50:46 +0100181 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100182 final CharSequence organizationName =
183 mDevicePolicyManager.getDeviceOwnerOrganizationName();
184 if (organizationName != null) {
185 mDisclosure.switchIndication(mContext.getResources().getString(
186 R.string.do_disclosure_with_name, organizationName));
187 } else {
188 mDisclosure.switchIndication(R.string.do_disclosure_generic);
189 }
190 mDisclosure.setVisibility(View.VISIBLE);
191 } else {
192 mDisclosure.setVisibility(View.GONE);
193 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200194 }
195
196 public void setVisible(boolean visible) {
197 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100198 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200199 if (visible) {
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700200 // If this is called after an error message was already shown, we should not clear it.
201 // Otherwise the error message won't be shown
202 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
203 hideTransientIndication();
204 }
Beverly8c785892018-01-31 17:25:52 -0500205 updateIndication(false);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700206 } else if (!visible) {
207 // If we unlock and return to keyguard quickly, previous error should not be shown
208 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200209 }
210 }
211
212 /**
213 * Sets the indication that is shown if nothing else is showing.
214 */
215 public void setRestingIndication(String restingIndication) {
216 mRestingIndication = restingIndication;
Beverly8c785892018-01-31 17:25:52 -0500217 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200218 }
219
220 /**
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800221 * Sets the active controller managing changes and callbacks to user information.
222 */
223 public void setUserInfoController(UserInfoController userInfoController) {
224 }
225
226 /**
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700227 * Returns the indication text indicating that trust has been granted.
228 *
229 * @return {@code null} or an empty string if a trust indication text should not be shown.
230 */
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700231 protected String getTrustGrantedIndication() {
232 return null;
233 }
234
235 /**
236 * Returns the indication text indicating that trust is currently being managed.
237 *
238 * @return {@code null} or an empty string if a trust managed text should not be shown.
239 */
240 protected String getTrustManagedIndication() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700241 return null;
242 }
243
244 /**
Adrian Roos12c1ef52014-06-04 13:54:08 +0200245 * Hides transient indication in {@param delayMs}.
246 */
247 public void hideTransientIndicationDelayed(long delayMs) {
248 mHandler.sendMessageDelayed(
249 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
250 }
251
252 /**
253 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
254 */
255 public void showTransientIndication(int transientIndication) {
256 showTransientIndication(mContext.getResources().getString(transientIndication));
257 }
258
259 /**
260 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
261 */
Lucas Dupinef886542018-01-03 16:03:07 -0800262 public void showTransientIndication(CharSequence transientIndication) {
Jason Chang2386a372018-04-24 16:05:30 +0800263 showTransientIndication(transientIndication, mInitialTextColorState);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700264 }
265
266 /**
267 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
268 */
Jason Chang2386a372018-04-24 16:05:30 +0800269 public void showTransientIndication(CharSequence transientIndication,
270 ColorStateList textColorState) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200271 mTransientIndication = transientIndication;
Jason Chang2386a372018-04-24 16:05:30 +0800272 mTransientTextColorState = textColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200273 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Adrian Roosc1b50322017-02-27 21:07:58 +0100274 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
275 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
276 mWakeLock.setAcquired(true);
277 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
278 }
Beverly8c785892018-01-31 17:25:52 -0500279
280 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200281 }
282
283 /**
284 * Hides transient indication.
285 */
286 public void hideTransientIndication() {
287 if (mTransientIndication != null) {
288 mTransientIndication = null;
289 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Beverly8c785892018-01-31 17:25:52 -0500290 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200291 }
292 }
293
Beverly8c785892018-01-31 17:25:52 -0500294 protected final void updateIndication(boolean animate) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100295 if (TextUtils.isEmpty(mTransientIndication)) {
296 mWakeLock.setAcquired(false);
297 }
298
Adrian Roos12c1ef52014-06-04 13:54:08 +0200299 if (mVisible) {
Lucas Dupin53d50622017-05-13 15:54:14 -0700300 // Walk down a precedence-ordered list of what indication
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600301 // should be shown based on user or device state
Lucas Dupinff6628d2018-10-15 10:12:37 -0700302 if (mDozing) {
303 if (!TextUtils.isEmpty(mTransientIndication)) {
304 mTextView.setTextColor(Color.WHITE);
305 mTextView.switchIndication(mTransientIndication);
306 }
307 updateAlphas();
308 return;
309 }
310
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700311 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Jorim Jaggifabc7432017-05-15 02:40:05 +0200312 int userId = KeyguardUpdateMonitor.getCurrentUser();
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700313 String trustGrantedIndication = getTrustGrantedIndication();
314 String trustManagedIndication = getTrustManagedIndication();
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700315 if (!mUserManager.isUserUnlocked(userId)) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600316 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
Jason Chang2386a372018-04-24 16:05:30 +0800317 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600318 } else if (!TextUtils.isEmpty(mTransientIndication)) {
319 mTextView.switchIndication(mTransientIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800320 mTextView.setTextColor(mTransientTextColorState);
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700321 } else if (!TextUtils.isEmpty(trustGrantedIndication)
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700322 && updateMonitor.getUserHasTrust(userId)) {
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700323 mTextView.switchIndication(trustGrantedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800324 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600325 } else if (mPowerPluggedIn) {
326 String indication = computePowerIndication();
327 if (DEBUG_CHARGING_SPEED) {
328 indication += ", " + (mChargingWattage / 1000) + " mW";
329 }
Jason Chang2386a372018-04-24 16:05:30 +0800330 mTextView.setTextColor(mInitialTextColorState);
Beverly85499d92018-02-14 15:55:16 -0500331 if (animate) {
332 animateText(mTextView, indication);
333 } else {
334 mTextView.switchIndication(indication);
335 }
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700336 } else if (!TextUtils.isEmpty(trustManagedIndication)
337 && updateMonitor.getUserTrustIsManaged(userId)
338 && !updateMonitor.getUserHasTrust(userId)) {
339 mTextView.switchIndication(trustManagedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800340 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600341 } else {
342 mTextView.switchIndication(mRestingIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800343 mTextView.setTextColor(mInitialTextColorState);
Adrian Roos7b043112015-07-10 13:00:33 -0700344 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200345 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200346 }
347
Lucas Dupinff6628d2018-10-15 10:12:37 -0700348 private void updateAlphas() {
349 if (!TextUtils.isEmpty(mTransientIndication)) {
350 mTextView.setAlpha(1f);
351 } else {
352 mTextView.setAlpha(1f - mDarkAmount);
353 }
354 }
355
Beverly85499d92018-02-14 15:55:16 -0500356 // animates textView - textView moves up and bounces down
357 private void animateText(KeyguardIndicationTextView textView, String indication) {
358 int yTranslation = mContext.getResources().getInteger(
359 R.integer.wired_charging_keyguard_text_animation_distance);
360 int animateUpDuration = mContext.getResources().getInteger(
361 R.integer.wired_charging_keyguard_text_animation_duration_up);
362 int animateDownDuration = mContext.getResources().getInteger(
363 R.integer.wired_charging_keyguard_text_animation_duration_down);
Lucas Dupindef43692018-07-02 15:22:58 -0700364 textView.animate().cancel();
365 float translation = textView.getTranslationY();
Beverly85499d92018-02-14 15:55:16 -0500366 textView.animate()
367 .translationYBy(yTranslation)
368 .setInterpolator(Interpolators.LINEAR)
369 .setDuration(animateUpDuration)
370 .setListener(new AnimatorListenerAdapter() {
Lucas Dupindef43692018-07-02 15:22:58 -0700371 private boolean mCancelled;
372
Beverly85499d92018-02-14 15:55:16 -0500373 @Override
374 public void onAnimationStart(Animator animation) {
375 textView.switchIndication(indication);
376 }
Lucas Dupindef43692018-07-02 15:22:58 -0700377
378 @Override
379 public void onAnimationCancel(Animator animation) {
380 textView.setTranslationY(translation);
381 mCancelled = true;
382 }
383
Beverly85499d92018-02-14 15:55:16 -0500384 @Override
385 public void onAnimationEnd(Animator animation) {
Lucas Dupindef43692018-07-02 15:22:58 -0700386 if (mCancelled) {
387 return;
388 }
Beverly85499d92018-02-14 15:55:16 -0500389 textView.animate()
390 .setDuration(animateDownDuration)
391 .setInterpolator(Interpolators.BOUNCE)
Lucas Dupindef43692018-07-02 15:22:58 -0700392 .translationY(translation)
393 .setListener(new AnimatorListenerAdapter() {
394 @Override
395 public void onAnimationCancel(Animator animation) {
396 textView.setTranslationY(translation);
397 }
398 });
Beverly85499d92018-02-14 15:55:16 -0500399 }
400 });
401 }
402
Adrian Roos12c1ef52014-06-04 13:54:08 +0200403 private String computePowerIndication() {
404 if (mPowerCharged) {
405 return mContext.getResources().getString(R.string.keyguard_charged);
406 }
407
408 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700409 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200410 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700411 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
412
Adrian Roos12c1ef52014-06-04 13:54:08 +0200413 } catch (RemoteException e) {
414 Log.e(TAG, "Error calling IBatteryStats: ", e);
415 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700416 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200417
Adrian Roos7b043112015-07-10 13:00:33 -0700418 int chargingId;
419 switch (mChargingSpeed) {
420 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
Adrian Roos7e39e592015-09-23 17:03:47 -0700421 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700422 ? R.string.keyguard_indication_charging_time_fast
Adrian Roos7e39e592015-09-23 17:03:47 -0700423 : R.string.keyguard_plugged_in_charging_fast;
Adrian Roos7b043112015-07-10 13:00:33 -0700424 break;
425 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
Adrian Roos7e39e592015-09-23 17:03:47 -0700426 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700427 ? R.string.keyguard_indication_charging_time_slowly
Adrian Roos7e39e592015-09-23 17:03:47 -0700428 : R.string.keyguard_plugged_in_charging_slowly;
Adrian Roos7b043112015-07-10 13:00:33 -0700429 break;
430 default:
Adrian Roos7e39e592015-09-23 17:03:47 -0700431 chargingId = hasChargingTime
432 ? R.string.keyguard_indication_charging_time
433 : R.string.keyguard_plugged_in;
Adrian Roos7b043112015-07-10 13:00:33 -0700434 break;
435 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700436
Lucas Dupin8d595d22018-03-08 10:34:58 -0800437 String percentage = NumberFormat.getPercentInstance()
438 .format(mBatteryLevel / 100f);
Adrian Roos7e39e592015-09-23 17:03:47 -0700439 if (hasChargingTime) {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800440 // We now have battery percentage in these strings and it's expected that all
441 // locales will also have it in the future. For now, we still have to support the old
442 // format until all languages get the new translations.
Adrian Roos7e39e592015-09-23 17:03:47 -0700443 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
444 mContext, chargingTimeRemaining);
Lucas Dupin8d595d22018-03-08 10:34:58 -0800445 try {
446 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
447 percentage);
448 } catch (IllegalFormatConversionException e) {
449 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
450 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700451 } else {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800452 // Same as above
453 try {
454 return mContext.getResources().getString(chargingId, percentage);
455 } catch (IllegalFormatConversionException e) {
456 return mContext.getResources().getString(chargingId);
457 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700458 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200459 }
460
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800461 public void setStatusBarKeyguardViewManager(
462 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
463 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
464 }
465
Adrian Roosaf45b602017-03-14 13:10:25 -0700466 private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800467 @Override
468 public void onReceive(Context context, Intent intent) {
469 mHandler.post(() -> {
470 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500471 updateIndication(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800472 }
473 });
474 }
475 };
476
477 private final Handler mHandler = new Handler() {
478 @Override
479 public void handleMessage(Message msg) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100480 if (msg.what == MSG_HIDE_TRANSIENT) {
481 hideTransientIndication();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200482 } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800483 mLockIcon.setTransientFpError(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800484 }
485 }
486 };
487
Adrian Roos91ba3072017-02-14 16:50:46 +0100488 public void setDozing(boolean dozing) {
Jorim Jaggifabc7432017-05-15 02:40:05 +0200489 if (mDozing == dozing) {
490 return;
491 }
Adrian Roos91ba3072017-02-14 16:50:46 +0100492 mDozing = dozing;
Beverly8c785892018-01-31 17:25:52 -0500493 updateIndication(false);
Adrian Roos91ba3072017-02-14 16:50:46 +0100494 updateDisclosure();
495 }
496
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800497 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
498 pw.println("KeyguardIndicationController:");
Jason Chang2386a372018-04-24 16:05:30 +0800499 pw.println(" mTransientTextColorState: " + mTransientTextColorState);
500 pw.println(" mInitialTextColorState: " + mInitialTextColorState);
Beverly2034c832018-03-19 11:18:51 -0400501 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800502 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
503 pw.println(" mPowerCharged: " + mPowerCharged);
504 pw.println(" mChargingSpeed: " + mChargingSpeed);
505 pw.println(" mChargingWattage: " + mChargingWattage);
506 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
507 pw.println(" mDozing: " + mDozing);
508 pw.println(" mBatteryLevel: " + mBatteryLevel);
509 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
510 pw.println(" computePowerIndication(): " + computePowerIndication());
511 }
512
Lucas Dupinff6628d2018-10-15 10:12:37 -0700513 public void setDarkAmount(float darkAmount) {
514 if (mDarkAmount == darkAmount) {
515 return;
516 }
517 mDarkAmount = darkAmount;
518 updateAlphas();
519 }
520
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800521 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
Adrian Roos56021892017-02-27 20:25:09 +0100522 public static final int HIDE_DELAY_MS = 5000;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800523 private int mLastSuccessiveErrorMessage = -1;
Selim Cinek3e451942016-07-14 18:07:53 -0700524
Adrian Roos12c1ef52014-06-04 13:54:08 +0200525 @Override
526 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100527 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200528 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roos56021892017-02-27 20:25:09 +0100529 boolean wasPluggedIn = mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400530 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100531 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200532 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800533 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700534 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Lucas Dupin4272f442018-01-13 22:00:35 -0800535 mBatteryLevel = status.level;
Beverly2034c832018-03-19 11:18:51 -0400536 updateIndication(!wasPluggedIn && mPowerPluggedInWired);
Adrian Roosc1b50322017-02-27 21:07:58 +0100537 if (mDozing) {
538 if (!wasPluggedIn && mPowerPluggedIn) {
539 showTransientIndication(computePowerIndication());
540 hideTransientIndicationDelayed(HIDE_DELAY_MS);
541 } else if (wasPluggedIn && !mPowerPluggedIn) {
542 hideTransientIndication();
543 }
Adrian Roos56021892017-02-27 20:25:09 +0100544 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200545 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700546
547 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100548 public void onKeyguardVisibilityChanged(boolean showing) {
549 if (showing) {
550 updateDisclosure();
551 }
552 }
553
554 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200555 public void onBiometricHelp(int msgId, String helpString,
556 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700557 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200558 if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700559 return;
560 }
Jason Chang2386a372018-04-24 16:05:30 +0800561 ColorStateList errorColorState = Utils.getColorError(mContext);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700562 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Jason Chang2386a372018-04-24 16:05:30 +0800563 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
Jason Chang1e4a4bd2018-05-22 17:30:19 +0800564 errorColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700565 } else if (updateMonitor.isScreenOn()) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700566 mLockIcon.setTransientFpError(true);
Jason Chang2386a372018-04-24 16:05:30 +0800567 showTransientIndication(helpString, errorColorState);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200568 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
569 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
570 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
571 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700572 }
Selim Cinek3e451942016-07-14 18:07:53 -0700573 // Help messages indicate that there was actually a try since the last error, so those
574 // are not two successive error messages anymore.
575 mLastSuccessiveErrorMessage = -1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700576 }
577
578 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200579 public void onBiometricError(int msgId, String errString,
580 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700581 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200582 if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700583 return;
584 }
Jason Chang2386a372018-04-24 16:05:30 +0800585 ColorStateList errorColorState = Utils.getColorError(mContext);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700586 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200587 // When swiping up right after receiving a biometric error, the bouncer calls
Selim Cinek3e451942016-07-14 18:07:53 -0700588 // authenticate leading to the same message being shown again on the bouncer.
589 // We want to avoid this, as it may confuse the user when the message is too
590 // generic.
591 if (mLastSuccessiveErrorMessage != msgId) {
Jason Chang2386a372018-04-24 16:05:30 +0800592 mStatusBarKeyguardViewManager.showBouncerMessage(errString,
Jason Chang1e4a4bd2018-05-22 17:30:19 +0800593 errorColorState);
Selim Cinek3e451942016-07-14 18:07:53 -0700594 }
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700595 } else if (updateMonitor.isScreenOn()) {
Jason Chang2386a372018-04-24 16:05:30 +0800596 showTransientIndication(errString, errorColorState);
Selim Cinek3e451942016-07-14 18:07:53 -0700597 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100598 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinek3e451942016-07-14 18:07:53 -0700599 } else {
600 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700601 }
Selim Cinek3e451942016-07-14 18:07:53 -0700602 mLastSuccessiveErrorMessage = msgId;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700603 }
604
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200605 private boolean shouldSuppressBiometricError(int msgId,
606 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
607 if (biometricSourceType == BiometricSourceType.FINGERPRINT)
608 return shouldSuppressFingerprintError(msgId, updateMonitor);
609 if (biometricSourceType == BiometricSourceType.FACE)
610 return shouldSuppressFaceError(msgId, updateMonitor);
611 return false;
612 }
613
614 private boolean shouldSuppressFingerprintError(int msgId,
615 KeyguardUpdateMonitor updateMonitor) {
616 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
617 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
618 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
619 }
620
621 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
622 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
623 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
624 || msgId == FaceManager.FACE_ERROR_CANCELED);
625 }
626
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700627 @Override
Lucas Dupinef886542018-01-03 16:03:07 -0800628 public void onTrustAgentErrorMessage(CharSequence message) {
Jason Chang2386a372018-04-24 16:05:30 +0800629 showTransientIndication(message, Utils.getColorError(mContext));
Lucas Dupinef886542018-01-03 16:03:07 -0800630 }
631
632 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700633 public void onScreenTurnedOn() {
634 if (mMessageToShowOnScreenOn != null) {
Jason Chang2386a372018-04-24 16:05:30 +0800635 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700636 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100637 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700638 mMessageToShowOnScreenOn = null;
639 }
640 }
641
642 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200643 public void onBiometricRunningStateChanged(boolean running,
644 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700645 if (running) {
646 mMessageToShowOnScreenOn = null;
647 }
648 }
Selim Cinek3e451942016-07-14 18:07:53 -0700649
650 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200651 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
652 super.onBiometricAuthenticated(userId, biometricSourceType);
Selim Cinek3e451942016-07-14 18:07:53 -0700653 mLastSuccessiveErrorMessage = -1;
654 }
655
656 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200657 public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
658 super.onBiometricAuthFailed(biometricSourceType);
Selim Cinek3e451942016-07-14 18:07:53 -0700659 mLastSuccessiveErrorMessage = -1;
660 }
Jorim Jaggidadafd42016-09-30 07:20:25 -0700661
662 @Override
663 public void onUserUnlocked() {
664 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500665 updateIndication(false);
Jorim Jaggidadafd42016-09-30 07:20:25 -0700666 }
667 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200668 };
Adrian Roos12c1ef52014-06-04 13:54:08 +0200669}