blob: 960d2218565220f55b8a7ecfa453b2fa46c9ab4e [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;
Evan Laird878c8532018-10-15 15:54:29 -040054import com.android.systemui.statusbar.StatusBarStateController.StateListener;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070055import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
56import com.android.systemui.statusbar.phone.LockIcon;
57import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080058import com.android.systemui.statusbar.policy.UserInfoController;
Adrian Roosc1b50322017-02-27 21:07:58 +010059import com.android.systemui.util.wakelock.SettableWakeLock;
60import com.android.systemui.util.wakelock.WakeLock;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070061
Lucas Dupin3fcdd472018-01-19 19:06:45 -080062import java.io.FileDescriptor;
63import java.io.PrintWriter;
Lucas Dupin4272f442018-01-13 22:00:35 -080064import java.text.NumberFormat;
Lucas Dupin8d595d22018-03-08 10:34:58 -080065import java.util.IllegalFormatConversionException;
Lucas Dupin4272f442018-01-13 22:00:35 -080066
Adrian Roos12c1ef52014-06-04 13:54:08 +020067/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070068 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020069 */
Evan Laird878c8532018-10-15 15:54:29 -040070public class KeyguardIndicationController implements StateListener {
Adrian Roos12c1ef52014-06-04 13:54:08 +020071
Adrian Roos0c859ae2015-11-23 16:47:50 -080072 private static final String TAG = "KeyguardIndication";
73 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020074
75 private static final int MSG_HIDE_TRANSIENT = 1;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020076 private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
77 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020078
79 private final Context mContext;
Lucas Dupin987f1932017-05-13 21:02:52 -070080 private ViewGroup mIndicationArea;
81 private KeyguardIndicationTextView mTextView;
82 private KeyguardIndicationTextView mDisclosure;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060083 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020084 private final IBatteryStats mBatteryInfo;
Adrian Roosc1b50322017-02-27 21:07:58 +010085 private final SettableWakeLock mWakeLock;
Adrian Roos12c1ef52014-06-04 13:54:08 +020086
Adrian Roos7b043112015-07-10 13:00:33 -070087 private final int mSlowThreshold;
88 private final int mFastThreshold;
Lucas Dupin987f1932017-05-13 21:02:52 -070089 private LockIcon mLockIcon;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070090 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Adrian Roos7b043112015-07-10 13:00:33 -070091
Adrian Roos12c1ef52014-06-04 13:54:08 +020092 private String mRestingIndication;
Lucas Dupinef886542018-01-03 16:03:07 -080093 private CharSequence mTransientIndication;
Jason Chang2386a372018-04-24 16:05:30 +080094 private ColorStateList mTransientTextColorState;
95 private ColorStateList mInitialTextColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +020096 private boolean mVisible;
97
98 private boolean mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -040099 private boolean mPowerPluggedInWired;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200100 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -0700101 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -0800102 private int mChargingWattage;
Lucas Dupin4272f442018-01-13 22:00:35 -0800103 private int mBatteryLevel;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700104 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200105
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700106 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800107
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100108 private final DevicePolicyManager mDevicePolicyManager;
Adrian Roos91ba3072017-02-14 16:50:46 +0100109 private boolean mDozing;
Lucas Dupinff6628d2018-10-15 10:12:37 -0700110 private float mDarkAmount;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100111
Adrian Roosaf45b602017-03-14 13:10:25 -0700112 /**
113 * Creates a new KeyguardIndicationController and registers callbacks.
114 */
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100115 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
116 LockIcon lockIcon) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100117 this(context, indicationArea, lockIcon,
118 WakeLock.createPartial(context, "Doze:KeyguardIndication"));
Adrian Roosaf45b602017-03-14 13:10:25 -0700119
120 registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
Adrian Roosc1b50322017-02-27 21:07:58 +0100121 }
122
Adrian Roosaf45b602017-03-14 13:10:25 -0700123 /**
124 * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
125 */
Adrian Roosc1b50322017-02-27 21:07:58 +0100126 @VisibleForTesting
127 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
128 WakeLock wakeLock) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200129 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100130 mIndicationArea = indicationArea;
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800131 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
Jason Chang2386a372018-04-24 16:05:30 +0800132 mInitialTextColorState = mTextView != null ?
133 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800134 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700135 mLockIcon = lockIcon;
Adrian Roosc1b50322017-02-27 21:07:58 +0100136 mWakeLock = new SettableWakeLock(wakeLock);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200137
Adrian Roos7b043112015-07-10 13:00:33 -0700138 Resources res = context.getResources();
139 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
140 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
141
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600142 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200143 mBatteryInfo = IBatteryStats.Stub.asInterface(
144 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600145
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100146 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
147 Context.DEVICE_POLICY_SERVICE);
148
Adrian Roosaf45b602017-03-14 13:10:25 -0700149 updateDisclosure();
150 }
151
152 private void registerCallbacks(KeyguardUpdateMonitor monitor) {
153 monitor.registerCallback(getKeyguardCallback());
154
155 mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
Jason Monkcd26af72017-01-11 14:32:58 -0500156 new IntentFilter(Intent.ACTION_TIME_TICK), null,
Jason Monk9c7844c2017-01-18 15:21:53 -0500157 Dependency.get(Dependency.TIME_TICK_HANDLER));
Evan Laird878c8532018-10-15 15:54:29 -0400158
159 Dependency.get(StatusBarStateController.class).addListener(this);
160 }
161
162 /**
163 * Used by {@link com.android.systemui.statusbar.phone.StatusBar} to give the indication
164 * controller a chance to unregister itself as a receiver.
165 *
166 * //TODO: This can probably be converted to a fragment and not have to be manually recreated
167 */
168 public void destroy() {
169 mContext.unregisterReceiver(mTickReceiver);
170 Dependency.get(StatusBarStateController.class).removeListener(this);
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100171 }
172
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800173 /**
174 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
175 * {@link KeyguardIndicationController}.
176 *
177 * <p>Subclasses may override this method to extend or change the callback behavior by extending
178 * the {@link BaseKeyguardCallback}.
179 *
180 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
181 * same instance.
182 */
183 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700184 if (mUpdateMonitorCallback == null) {
185 mUpdateMonitorCallback = new BaseKeyguardCallback();
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800186 }
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700187 return mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800188 }
189
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100190 private void updateDisclosure() {
191 if (mDevicePolicyManager == null) {
192 return;
193 }
194
Adrian Roos91ba3072017-02-14 16:50:46 +0100195 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100196 final CharSequence organizationName =
197 mDevicePolicyManager.getDeviceOwnerOrganizationName();
198 if (organizationName != null) {
199 mDisclosure.switchIndication(mContext.getResources().getString(
200 R.string.do_disclosure_with_name, organizationName));
201 } else {
202 mDisclosure.switchIndication(R.string.do_disclosure_generic);
203 }
204 mDisclosure.setVisibility(View.VISIBLE);
205 } else {
206 mDisclosure.setVisibility(View.GONE);
207 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200208 }
209
210 public void setVisible(boolean visible) {
211 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100212 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200213 if (visible) {
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700214 // If this is called after an error message was already shown, we should not clear it.
215 // Otherwise the error message won't be shown
216 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
217 hideTransientIndication();
218 }
Beverly8c785892018-01-31 17:25:52 -0500219 updateIndication(false);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700220 } else if (!visible) {
221 // If we unlock and return to keyguard quickly, previous error should not be shown
222 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200223 }
224 }
225
226 /**
227 * Sets the indication that is shown if nothing else is showing.
228 */
229 public void setRestingIndication(String restingIndication) {
230 mRestingIndication = restingIndication;
Beverly8c785892018-01-31 17:25:52 -0500231 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200232 }
233
234 /**
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800235 * Sets the active controller managing changes and callbacks to user information.
236 */
237 public void setUserInfoController(UserInfoController userInfoController) {
238 }
239
240 /**
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700241 * Returns the indication text indicating that trust has been granted.
242 *
243 * @return {@code null} or an empty string if a trust indication text should not be shown.
244 */
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700245 protected String getTrustGrantedIndication() {
246 return null;
247 }
248
249 /**
250 * Returns the indication text indicating that trust is currently being managed.
251 *
252 * @return {@code null} or an empty string if a trust managed text should not be shown.
253 */
254 protected String getTrustManagedIndication() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700255 return null;
256 }
257
258 /**
Adrian Roos12c1ef52014-06-04 13:54:08 +0200259 * Hides transient indication in {@param delayMs}.
260 */
261 public void hideTransientIndicationDelayed(long delayMs) {
262 mHandler.sendMessageDelayed(
263 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
264 }
265
266 /**
267 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
268 */
269 public void showTransientIndication(int transientIndication) {
270 showTransientIndication(mContext.getResources().getString(transientIndication));
271 }
272
273 /**
274 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
275 */
Lucas Dupinef886542018-01-03 16:03:07 -0800276 public void showTransientIndication(CharSequence transientIndication) {
Jason Chang2386a372018-04-24 16:05:30 +0800277 showTransientIndication(transientIndication, mInitialTextColorState);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700278 }
279
280 /**
281 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
282 */
Jason Chang2386a372018-04-24 16:05:30 +0800283 public void showTransientIndication(CharSequence transientIndication,
284 ColorStateList textColorState) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200285 mTransientIndication = transientIndication;
Jason Chang2386a372018-04-24 16:05:30 +0800286 mTransientTextColorState = textColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200287 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Adrian Roosc1b50322017-02-27 21:07:58 +0100288 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
289 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
290 mWakeLock.setAcquired(true);
291 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
292 }
Beverly8c785892018-01-31 17:25:52 -0500293
294 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200295 }
296
297 /**
298 * Hides transient indication.
299 */
300 public void hideTransientIndication() {
301 if (mTransientIndication != null) {
302 mTransientIndication = null;
303 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Beverly8c785892018-01-31 17:25:52 -0500304 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200305 }
306 }
307
Beverly8c785892018-01-31 17:25:52 -0500308 protected final void updateIndication(boolean animate) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100309 if (TextUtils.isEmpty(mTransientIndication)) {
310 mWakeLock.setAcquired(false);
311 }
312
Adrian Roos12c1ef52014-06-04 13:54:08 +0200313 if (mVisible) {
Lucas Dupin53d50622017-05-13 15:54:14 -0700314 // Walk down a precedence-ordered list of what indication
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600315 // should be shown based on user or device state
Lucas Dupinff6628d2018-10-15 10:12:37 -0700316 if (mDozing) {
317 if (!TextUtils.isEmpty(mTransientIndication)) {
318 mTextView.setTextColor(Color.WHITE);
319 mTextView.switchIndication(mTransientIndication);
320 }
321 updateAlphas();
322 return;
323 }
324
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700325 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Jorim Jaggifabc7432017-05-15 02:40:05 +0200326 int userId = KeyguardUpdateMonitor.getCurrentUser();
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700327 String trustGrantedIndication = getTrustGrantedIndication();
328 String trustManagedIndication = getTrustManagedIndication();
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700329 if (!mUserManager.isUserUnlocked(userId)) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600330 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
Jason Chang2386a372018-04-24 16:05:30 +0800331 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600332 } else if (!TextUtils.isEmpty(mTransientIndication)) {
333 mTextView.switchIndication(mTransientIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800334 mTextView.setTextColor(mTransientTextColorState);
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700335 } else if (!TextUtils.isEmpty(trustGrantedIndication)
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700336 && updateMonitor.getUserHasTrust(userId)) {
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700337 mTextView.switchIndication(trustGrantedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800338 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600339 } else if (mPowerPluggedIn) {
340 String indication = computePowerIndication();
341 if (DEBUG_CHARGING_SPEED) {
342 indication += ", " + (mChargingWattage / 1000) + " mW";
343 }
Jason Chang2386a372018-04-24 16:05:30 +0800344 mTextView.setTextColor(mInitialTextColorState);
Beverly85499d92018-02-14 15:55:16 -0500345 if (animate) {
346 animateText(mTextView, indication);
347 } else {
348 mTextView.switchIndication(indication);
349 }
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700350 } else if (!TextUtils.isEmpty(trustManagedIndication)
351 && updateMonitor.getUserTrustIsManaged(userId)
352 && !updateMonitor.getUserHasTrust(userId)) {
353 mTextView.switchIndication(trustManagedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800354 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600355 } else {
356 mTextView.switchIndication(mRestingIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800357 mTextView.setTextColor(mInitialTextColorState);
Adrian Roos7b043112015-07-10 13:00:33 -0700358 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200359 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200360 }
361
Lucas Dupinff6628d2018-10-15 10:12:37 -0700362 private void updateAlphas() {
363 if (!TextUtils.isEmpty(mTransientIndication)) {
364 mTextView.setAlpha(1f);
365 } else {
366 mTextView.setAlpha(1f - mDarkAmount);
367 }
368 }
369
Beverly85499d92018-02-14 15:55:16 -0500370 // animates textView - textView moves up and bounces down
371 private void animateText(KeyguardIndicationTextView textView, String indication) {
372 int yTranslation = mContext.getResources().getInteger(
373 R.integer.wired_charging_keyguard_text_animation_distance);
374 int animateUpDuration = mContext.getResources().getInteger(
375 R.integer.wired_charging_keyguard_text_animation_duration_up);
376 int animateDownDuration = mContext.getResources().getInteger(
377 R.integer.wired_charging_keyguard_text_animation_duration_down);
Lucas Dupindef43692018-07-02 15:22:58 -0700378 textView.animate().cancel();
379 float translation = textView.getTranslationY();
Beverly85499d92018-02-14 15:55:16 -0500380 textView.animate()
381 .translationYBy(yTranslation)
382 .setInterpolator(Interpolators.LINEAR)
383 .setDuration(animateUpDuration)
384 .setListener(new AnimatorListenerAdapter() {
Lucas Dupindef43692018-07-02 15:22:58 -0700385 private boolean mCancelled;
386
Beverly85499d92018-02-14 15:55:16 -0500387 @Override
388 public void onAnimationStart(Animator animation) {
389 textView.switchIndication(indication);
390 }
Lucas Dupindef43692018-07-02 15:22:58 -0700391
392 @Override
393 public void onAnimationCancel(Animator animation) {
394 textView.setTranslationY(translation);
395 mCancelled = true;
396 }
397
Beverly85499d92018-02-14 15:55:16 -0500398 @Override
399 public void onAnimationEnd(Animator animation) {
Lucas Dupindef43692018-07-02 15:22:58 -0700400 if (mCancelled) {
401 return;
402 }
Beverly85499d92018-02-14 15:55:16 -0500403 textView.animate()
404 .setDuration(animateDownDuration)
405 .setInterpolator(Interpolators.BOUNCE)
Lucas Dupindef43692018-07-02 15:22:58 -0700406 .translationY(translation)
407 .setListener(new AnimatorListenerAdapter() {
408 @Override
409 public void onAnimationCancel(Animator animation) {
410 textView.setTranslationY(translation);
411 }
412 });
Beverly85499d92018-02-14 15:55:16 -0500413 }
414 });
415 }
416
Adrian Roos12c1ef52014-06-04 13:54:08 +0200417 private String computePowerIndication() {
418 if (mPowerCharged) {
419 return mContext.getResources().getString(R.string.keyguard_charged);
420 }
421
422 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700423 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200424 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700425 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
426
Adrian Roos12c1ef52014-06-04 13:54:08 +0200427 } catch (RemoteException e) {
428 Log.e(TAG, "Error calling IBatteryStats: ", e);
429 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700430 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200431
Adrian Roos7b043112015-07-10 13:00:33 -0700432 int chargingId;
433 switch (mChargingSpeed) {
434 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
Adrian Roos7e39e592015-09-23 17:03:47 -0700435 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700436 ? R.string.keyguard_indication_charging_time_fast
Adrian Roos7e39e592015-09-23 17:03:47 -0700437 : R.string.keyguard_plugged_in_charging_fast;
Adrian Roos7b043112015-07-10 13:00:33 -0700438 break;
439 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
Adrian Roos7e39e592015-09-23 17:03:47 -0700440 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700441 ? R.string.keyguard_indication_charging_time_slowly
Adrian Roos7e39e592015-09-23 17:03:47 -0700442 : R.string.keyguard_plugged_in_charging_slowly;
Adrian Roos7b043112015-07-10 13:00:33 -0700443 break;
444 default:
Adrian Roos7e39e592015-09-23 17:03:47 -0700445 chargingId = hasChargingTime
446 ? R.string.keyguard_indication_charging_time
447 : R.string.keyguard_plugged_in;
Adrian Roos7b043112015-07-10 13:00:33 -0700448 break;
449 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700450
Lucas Dupin8d595d22018-03-08 10:34:58 -0800451 String percentage = NumberFormat.getPercentInstance()
452 .format(mBatteryLevel / 100f);
Adrian Roos7e39e592015-09-23 17:03:47 -0700453 if (hasChargingTime) {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800454 // We now have battery percentage in these strings and it's expected that all
455 // locales will also have it in the future. For now, we still have to support the old
456 // format until all languages get the new translations.
Adrian Roos7e39e592015-09-23 17:03:47 -0700457 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
458 mContext, chargingTimeRemaining);
Lucas Dupin8d595d22018-03-08 10:34:58 -0800459 try {
460 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
461 percentage);
462 } catch (IllegalFormatConversionException e) {
463 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
464 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700465 } else {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800466 // Same as above
467 try {
468 return mContext.getResources().getString(chargingId, percentage);
469 } catch (IllegalFormatConversionException e) {
470 return mContext.getResources().getString(chargingId);
471 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700472 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200473 }
474
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800475 public void setStatusBarKeyguardViewManager(
476 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
477 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
478 }
479
Adrian Roosaf45b602017-03-14 13:10:25 -0700480 private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800481 @Override
482 public void onReceive(Context context, Intent intent) {
483 mHandler.post(() -> {
484 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500485 updateIndication(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800486 }
487 });
488 }
489 };
490
491 private final Handler mHandler = new Handler() {
492 @Override
493 public void handleMessage(Message msg) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100494 if (msg.what == MSG_HIDE_TRANSIENT) {
495 hideTransientIndication();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200496 } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800497 mLockIcon.setTransientFpError(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800498 }
499 }
500 };
501
Adrian Roos91ba3072017-02-14 16:50:46 +0100502 public void setDozing(boolean dozing) {
Jorim Jaggifabc7432017-05-15 02:40:05 +0200503 if (mDozing == dozing) {
504 return;
505 }
Adrian Roos91ba3072017-02-14 16:50:46 +0100506 mDozing = dozing;
Beverly8c785892018-01-31 17:25:52 -0500507 updateIndication(false);
Adrian Roos91ba3072017-02-14 16:50:46 +0100508 updateDisclosure();
509 }
510
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800511 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
512 pw.println("KeyguardIndicationController:");
Jason Chang2386a372018-04-24 16:05:30 +0800513 pw.println(" mTransientTextColorState: " + mTransientTextColorState);
514 pw.println(" mInitialTextColorState: " + mInitialTextColorState);
Beverly2034c832018-03-19 11:18:51 -0400515 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800516 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
517 pw.println(" mPowerCharged: " + mPowerCharged);
518 pw.println(" mChargingSpeed: " + mChargingSpeed);
519 pw.println(" mChargingWattage: " + mChargingWattage);
520 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
521 pw.println(" mDozing: " + mDozing);
522 pw.println(" mBatteryLevel: " + mBatteryLevel);
523 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
524 pw.println(" computePowerIndication(): " + computePowerIndication());
525 }
526
Lucas Dupinff6628d2018-10-15 10:12:37 -0700527 public void setDarkAmount(float darkAmount) {
528 if (mDarkAmount == darkAmount) {
529 return;
530 }
531 mDarkAmount = darkAmount;
532 updateAlphas();
533 }
534
Evan Laird878c8532018-10-15 15:54:29 -0400535 @Override
536 public void onStateChanged(int newState) {
537 // don't care
538 }
539
540 @Override
541 public void onDozingChanged(boolean isDozing) {
542 setDozing(isDozing);
543 }
544
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800545 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
Adrian Roos56021892017-02-27 20:25:09 +0100546 public static final int HIDE_DELAY_MS = 5000;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800547 private int mLastSuccessiveErrorMessage = -1;
Selim Cinek3e451942016-07-14 18:07:53 -0700548
Adrian Roos12c1ef52014-06-04 13:54:08 +0200549 @Override
550 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100551 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200552 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roos56021892017-02-27 20:25:09 +0100553 boolean wasPluggedIn = mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400554 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100555 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200556 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800557 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700558 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Lucas Dupin4272f442018-01-13 22:00:35 -0800559 mBatteryLevel = status.level;
Beverly2034c832018-03-19 11:18:51 -0400560 updateIndication(!wasPluggedIn && mPowerPluggedInWired);
Adrian Roosc1b50322017-02-27 21:07:58 +0100561 if (mDozing) {
562 if (!wasPluggedIn && mPowerPluggedIn) {
563 showTransientIndication(computePowerIndication());
564 hideTransientIndicationDelayed(HIDE_DELAY_MS);
565 } else if (wasPluggedIn && !mPowerPluggedIn) {
566 hideTransientIndication();
567 }
Adrian Roos56021892017-02-27 20:25:09 +0100568 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200569 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700570
571 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100572 public void onKeyguardVisibilityChanged(boolean showing) {
573 if (showing) {
574 updateDisclosure();
575 }
576 }
577
578 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200579 public void onBiometricHelp(int msgId, String helpString,
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 (!updateMonitor.isUnlockingWithBiometricAllowed()) {
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()) {
Jason Chang2386a372018-04-24 16:05:30 +0800587 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
Jason Chang1e4a4bd2018-05-22 17:30:19 +0800588 errorColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700589 } else if (updateMonitor.isScreenOn()) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700590 mLockIcon.setTransientFpError(true);
Jason Chang2386a372018-04-24 16:05:30 +0800591 showTransientIndication(helpString, errorColorState);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200592 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
593 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
594 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
595 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700596 }
Selim Cinek3e451942016-07-14 18:07:53 -0700597 // Help messages indicate that there was actually a try since the last error, so those
598 // are not two successive error messages anymore.
599 mLastSuccessiveErrorMessage = -1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700600 }
601
602 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200603 public void onBiometricError(int msgId, String errString,
604 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700605 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200606 if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700607 return;
608 }
Jason Chang2386a372018-04-24 16:05:30 +0800609 ColorStateList errorColorState = Utils.getColorError(mContext);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700610 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200611 // When swiping up right after receiving a biometric error, the bouncer calls
Selim Cinek3e451942016-07-14 18:07:53 -0700612 // authenticate leading to the same message being shown again on the bouncer.
613 // We want to avoid this, as it may confuse the user when the message is too
614 // generic.
615 if (mLastSuccessiveErrorMessage != msgId) {
Jason Chang2386a372018-04-24 16:05:30 +0800616 mStatusBarKeyguardViewManager.showBouncerMessage(errString,
Jason Chang1e4a4bd2018-05-22 17:30:19 +0800617 errorColorState);
Selim Cinek3e451942016-07-14 18:07:53 -0700618 }
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700619 } else if (updateMonitor.isScreenOn()) {
Jason Chang2386a372018-04-24 16:05:30 +0800620 showTransientIndication(errString, errorColorState);
Selim Cinek3e451942016-07-14 18:07:53 -0700621 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100622 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinek3e451942016-07-14 18:07:53 -0700623 } else {
624 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700625 }
Selim Cinek3e451942016-07-14 18:07:53 -0700626 mLastSuccessiveErrorMessage = msgId;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700627 }
628
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200629 private boolean shouldSuppressBiometricError(int msgId,
630 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
631 if (biometricSourceType == BiometricSourceType.FINGERPRINT)
632 return shouldSuppressFingerprintError(msgId, updateMonitor);
633 if (biometricSourceType == BiometricSourceType.FACE)
634 return shouldSuppressFaceError(msgId, updateMonitor);
635 return false;
636 }
637
638 private boolean shouldSuppressFingerprintError(int msgId,
639 KeyguardUpdateMonitor updateMonitor) {
640 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
641 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
642 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
643 }
644
645 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
646 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
647 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
648 || msgId == FaceManager.FACE_ERROR_CANCELED);
649 }
650
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700651 @Override
Lucas Dupinef886542018-01-03 16:03:07 -0800652 public void onTrustAgentErrorMessage(CharSequence message) {
Jason Chang2386a372018-04-24 16:05:30 +0800653 showTransientIndication(message, Utils.getColorError(mContext));
Lucas Dupinef886542018-01-03 16:03:07 -0800654 }
655
656 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700657 public void onScreenTurnedOn() {
658 if (mMessageToShowOnScreenOn != null) {
Jason Chang2386a372018-04-24 16:05:30 +0800659 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700660 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100661 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700662 mMessageToShowOnScreenOn = null;
663 }
664 }
665
666 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200667 public void onBiometricRunningStateChanged(boolean running,
668 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700669 if (running) {
670 mMessageToShowOnScreenOn = null;
671 }
672 }
Selim Cinek3e451942016-07-14 18:07:53 -0700673
674 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200675 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
676 super.onBiometricAuthenticated(userId, biometricSourceType);
Selim Cinek3e451942016-07-14 18:07:53 -0700677 mLastSuccessiveErrorMessage = -1;
678 }
679
680 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200681 public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
682 super.onBiometricAuthFailed(biometricSourceType);
Selim Cinek3e451942016-07-14 18:07:53 -0700683 mLastSuccessiveErrorMessage = -1;
684 }
Jorim Jaggidadafd42016-09-30 07:20:25 -0700685
686 @Override
687 public void onUserUnlocked() {
688 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500689 updateIndication(false);
Jorim Jaggidadafd42016-09-30 07:20:25 -0700690 }
691 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200692 };
Adrian Roos12c1ef52014-06-04 13:54:08 +0200693}