blob: be0923f8ca1ba6f966c1990289da604cba0b28f1 [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.Context;
Jason Chang2386a372018-04-24 16:05:30 +080023import android.content.res.ColorStateList;
Lucas Dupinff6628d2018-10-15 10:12:37 -070024import android.content.res.Resources;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070025import android.graphics.Color;
Lucas Dupinff6628d2018-10-15 10:12:37 -070026import android.hardware.biometrics.BiometricSourceType;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020027import android.hardware.face.FaceManager;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070028import android.hardware.fingerprint.FingerprintManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020029import android.os.BatteryManager;
30import android.os.BatteryStats;
31import android.os.Handler;
32import android.os.Message;
33import android.os.RemoteException;
34import android.os.ServiceManager;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060035import android.os.UserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020036import android.text.TextUtils;
37import android.text.format.Formatter;
38import android.util.Log;
39import android.view.View;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010040import android.view.ViewGroup;
Adrian Roos12c1ef52014-06-04 13:54:08 +020041
Adrian Roosc1b50322017-02-27 21:07:58 +010042import com.android.internal.annotations.VisibleForTesting;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070043import com.android.internal.app.IBatteryStats;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070044import com.android.internal.logging.nano.MetricsProto;
45import com.android.internal.widget.LockPatternUtils;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070046import com.android.keyguard.KeyguardUpdateMonitor;
47import com.android.keyguard.KeyguardUpdateMonitorCallback;
Jason Monk58be7a62017-02-01 20:17:51 -050048import com.android.settingslib.Utils;
Jason Monk9c7844c2017-01-18 15:21:53 -050049import com.android.systemui.Dependency;
Beverly8c785892018-01-31 17:25:52 -050050import com.android.systemui.Interpolators;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070051import com.android.systemui.R;
Beverly8fdb5332019-02-04 14:29:49 -050052import com.android.systemui.plugins.statusbar.StatusBarStateController;
53import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070054import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
55import com.android.systemui.statusbar.phone.LockIcon;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070056import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
Lucas Dupin0df60fe2019-04-23 10:19:27 -070057import com.android.systemui.statusbar.phone.ShadeController;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070058import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
Lucas Dupin0df60fe2019-04-23 10:19:27 -070059import com.android.systemui.statusbar.policy.AccessibilityController;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080060import com.android.systemui.statusbar.policy.UserInfoController;
Adrian Roosc1b50322017-02-27 21:07:58 +010061import com.android.systemui.util.wakelock.SettableWakeLock;
62import com.android.systemui.util.wakelock.WakeLock;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070063
Lucas Dupin3fcdd472018-01-19 19:06:45 -080064import java.io.FileDescriptor;
65import java.io.PrintWriter;
Lucas Dupin4272f442018-01-13 22:00:35 -080066import java.text.NumberFormat;
Lucas Dupin8d595d22018-03-08 10:34:58 -080067import java.util.IllegalFormatConversionException;
Lucas Dupin4272f442018-01-13 22:00:35 -080068
Adrian Roos12c1ef52014-06-04 13:54:08 +020069/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070070 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020071 */
Evan Laird878c8532018-10-15 15:54:29 -040072public class KeyguardIndicationController implements StateListener {
Adrian Roos12c1ef52014-06-04 13:54:08 +020073
Adrian Roos0c859ae2015-11-23 16:47:50 -080074 private static final String TAG = "KeyguardIndication";
75 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020076
77 private static final int MSG_HIDE_TRANSIENT = 1;
Gilad Brettercb51b8b2018-03-22 17:04:51 +020078 private static final int MSG_CLEAR_BIOMETRIC_MSG = 2;
79 private static final long TRANSIENT_BIOMETRIC_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020080
81 private final Context mContext;
Lucas Dupin0df60fe2019-04-23 10:19:27 -070082 private final ShadeController mShadeController;
83 private final AccessibilityController mAccessibilityController;
Lucas Dupin987f1932017-05-13 21:02:52 -070084 private ViewGroup mIndicationArea;
85 private KeyguardIndicationTextView mTextView;
86 private KeyguardIndicationTextView mDisclosure;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060087 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020088 private final IBatteryStats mBatteryInfo;
Adrian Roosc1b50322017-02-27 21:07:58 +010089 private final SettableWakeLock mWakeLock;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070090 private final LockPatternUtils mLockPatternUtils;
Adrian Roos12c1ef52014-06-04 13:54:08 +020091
Adrian Roos7b043112015-07-10 13:00:33 -070092 private final int mSlowThreshold;
93 private final int mFastThreshold;
Lucas Dupin05904652019-04-09 16:16:15 -070094 private final LockIcon mLockIcon;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070095 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070096 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
Adrian Roos7b043112015-07-10 13:00:33 -070097
Adrian Roos12c1ef52014-06-04 13:54:08 +020098 private String mRestingIndication;
Lucas Dupinef886542018-01-03 16:03:07 -080099 private CharSequence mTransientIndication;
Jason Chang2386a372018-04-24 16:05:30 +0800100 private ColorStateList mTransientTextColorState;
101 private ColorStateList mInitialTextColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200102 private boolean mVisible;
103
104 private boolean mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400105 private boolean mPowerPluggedInWired;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200106 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -0700107 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -0800108 private int mChargingWattage;
Lucas Dupin4272f442018-01-13 22:00:35 -0800109 private int mBatteryLevel;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700110 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200111
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700112 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800113
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100114 private final DevicePolicyManager mDevicePolicyManager;
Adrian Roos91ba3072017-02-14 16:50:46 +0100115 private boolean mDozing;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100116
Adrian Roosaf45b602017-03-14 13:10:25 -0700117 /**
118 * Creates a new KeyguardIndicationController and registers callbacks.
119 */
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100120 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
121 LockIcon lockIcon) {
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700122 this(context, indicationArea, lockIcon, new LockPatternUtils(context),
Lucas Dupin0df60fe2019-04-23 10:19:27 -0700123 WakeLock.createPartial(context, "Doze:KeyguardIndication"),
124 Dependency.get(ShadeController.class),
125 Dependency.get(AccessibilityController.class));
Adrian Roosaf45b602017-03-14 13:10:25 -0700126
127 registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
Adrian Roosc1b50322017-02-27 21:07:58 +0100128 }
129
Adrian Roosaf45b602017-03-14 13:10:25 -0700130 /**
131 * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
132 */
Adrian Roosc1b50322017-02-27 21:07:58 +0100133 @VisibleForTesting
134 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
Lucas Dupin0df60fe2019-04-23 10:19:27 -0700135 LockPatternUtils lockPatternUtils, WakeLock wakeLock, ShadeController shadeController,
136 AccessibilityController accessibilityController) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200137 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100138 mIndicationArea = indicationArea;
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800139 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
Jason Chang2386a372018-04-24 16:05:30 +0800140 mInitialTextColorState = mTextView != null ?
141 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800142 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700143 mLockIcon = lockIcon;
Lucas Dupin0df60fe2019-04-23 10:19:27 -0700144 mShadeController = shadeController;
145 mAccessibilityController = accessibilityController;
Brad Stenning691c2742019-04-19 11:48:00 -0700146 // lock icon is not used on all form factors.
147 if (mLockIcon != null) {
Lucas Dupin0df60fe2019-04-23 10:19:27 -0700148 mLockIcon.setOnLongClickListener(this::handleLockLongClick);
149 mLockIcon.setOnClickListener(this::handleLockClick);
Brad Stenning691c2742019-04-19 11:48:00 -0700150 }
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800151 mWakeLock = new SettableWakeLock(wakeLock, TAG);
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700152 mLockPatternUtils = lockPatternUtils;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200153
Adrian Roos7b043112015-07-10 13:00:33 -0700154 Resources res = context.getResources();
155 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
156 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
157
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600158 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200159 mBatteryInfo = IBatteryStats.Stub.asInterface(
160 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600161
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100162 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
163 Context.DEVICE_POLICY_SERVICE);
164
Adrian Roosaf45b602017-03-14 13:10:25 -0700165 updateDisclosure();
166 }
167
168 private void registerCallbacks(KeyguardUpdateMonitor monitor) {
169 monitor.registerCallback(getKeyguardCallback());
170
Lucas Dupin7e171e22018-12-20 11:29:35 -0800171 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mTickReceiver);
Jason Monkaf08c152018-12-04 11:12:39 -0500172 Dependency.get(StatusBarStateController.class).addCallback(this);
Evan Laird878c8532018-10-15 15:54:29 -0400173 }
174
175 /**
176 * Used by {@link com.android.systemui.statusbar.phone.StatusBar} to give the indication
177 * controller a chance to unregister itself as a receiver.
178 *
179 * //TODO: This can probably be converted to a fragment and not have to be manually recreated
180 */
181 public void destroy() {
Lucas Dupin7e171e22018-12-20 11:29:35 -0800182 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mTickReceiver);
Jason Monkaf08c152018-12-04 11:12:39 -0500183 Dependency.get(StatusBarStateController.class).removeCallback(this);
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100184 }
185
Lucas Dupin0df60fe2019-04-23 10:19:27 -0700186 private boolean handleLockLongClick(View view) {
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700187 mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
188 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
189 showTransientIndication(R.string.keyguard_indication_trust_disabled);
190 mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
191
192 return true;
193 }
194
Lucas Dupin0df60fe2019-04-23 10:19:27 -0700195 private void handleLockClick(View view) {
196 if (!mAccessibilityController.isAccessibilityEnabled()) {
197 return;
198 }
199 mShadeController.showBouncer(false /* scrimmed */);
200 }
201
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800202 /**
203 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
204 * {@link KeyguardIndicationController}.
205 *
206 * <p>Subclasses may override this method to extend or change the callback behavior by extending
207 * the {@link BaseKeyguardCallback}.
208 *
209 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
210 * same instance.
211 */
212 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700213 if (mUpdateMonitorCallback == null) {
214 mUpdateMonitorCallback = new BaseKeyguardCallback();
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800215 }
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700216 return mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800217 }
218
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100219 private void updateDisclosure() {
220 if (mDevicePolicyManager == null) {
221 return;
222 }
223
Adrian Roos91ba3072017-02-14 16:50:46 +0100224 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100225 final CharSequence organizationName =
226 mDevicePolicyManager.getDeviceOwnerOrganizationName();
227 if (organizationName != null) {
228 mDisclosure.switchIndication(mContext.getResources().getString(
229 R.string.do_disclosure_with_name, organizationName));
230 } else {
231 mDisclosure.switchIndication(R.string.do_disclosure_generic);
232 }
233 mDisclosure.setVisibility(View.VISIBLE);
234 } else {
235 mDisclosure.setVisibility(View.GONE);
236 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200237 }
238
239 public void setVisible(boolean visible) {
240 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100241 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200242 if (visible) {
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700243 // If this is called after an error message was already shown, we should not clear it.
244 // Otherwise the error message won't be shown
245 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
246 hideTransientIndication();
247 }
Beverly8c785892018-01-31 17:25:52 -0500248 updateIndication(false);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700249 } else if (!visible) {
250 // If we unlock and return to keyguard quickly, previous error should not be shown
251 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200252 }
253 }
254
255 /**
256 * Sets the indication that is shown if nothing else is showing.
257 */
258 public void setRestingIndication(String restingIndication) {
259 mRestingIndication = restingIndication;
Beverly8c785892018-01-31 17:25:52 -0500260 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200261 }
262
263 /**
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800264 * Sets the active controller managing changes and callbacks to user information.
265 */
266 public void setUserInfoController(UserInfoController userInfoController) {
267 }
268
269 /**
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700270 * Returns the indication text indicating that trust has been granted.
271 *
272 * @return {@code null} or an empty string if a trust indication text should not be shown.
273 */
Lucas Dupin859f2c82019-04-17 18:30:00 -0700274 private String getTrustGrantedIndication() {
275 return mContext.getString(R.string.keyguard_indication_trust_unlocked);
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700276 }
277
278 /**
279 * Returns the indication text indicating that trust is currently being managed.
280 *
281 * @return {@code null} or an empty string if a trust managed text should not be shown.
282 */
Lucas Dupin859f2c82019-04-17 18:30:00 -0700283 private String getTrustManagedIndication() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700284 return null;
285 }
286
287 /**
Adrian Roos12c1ef52014-06-04 13:54:08 +0200288 * Hides transient indication in {@param delayMs}.
289 */
290 public void hideTransientIndicationDelayed(long delayMs) {
291 mHandler.sendMessageDelayed(
292 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
293 }
294
295 /**
296 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
297 */
298 public void showTransientIndication(int transientIndication) {
299 showTransientIndication(mContext.getResources().getString(transientIndication));
300 }
301
302 /**
303 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
304 */
Lucas Dupinef886542018-01-03 16:03:07 -0800305 public void showTransientIndication(CharSequence transientIndication) {
Jason Chang2386a372018-04-24 16:05:30 +0800306 showTransientIndication(transientIndication, mInitialTextColorState);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700307 }
308
309 /**
310 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
311 */
Jason Chang2386a372018-04-24 16:05:30 +0800312 public void showTransientIndication(CharSequence transientIndication,
313 ColorStateList textColorState) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200314 mTransientIndication = transientIndication;
Jason Chang2386a372018-04-24 16:05:30 +0800315 mTransientTextColorState = textColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200316 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Adrian Roosc1b50322017-02-27 21:07:58 +0100317 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
318 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
319 mWakeLock.setAcquired(true);
320 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
321 }
Beverly8c785892018-01-31 17:25:52 -0500322
323 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200324 }
325
326 /**
327 * Hides transient indication.
328 */
329 public void hideTransientIndication() {
330 if (mTransientIndication != null) {
331 mTransientIndication = null;
332 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Beverly8c785892018-01-31 17:25:52 -0500333 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200334 }
335 }
336
Beverly8c785892018-01-31 17:25:52 -0500337 protected final void updateIndication(boolean animate) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100338 if (TextUtils.isEmpty(mTransientIndication)) {
339 mWakeLock.setAcquired(false);
340 }
341
Adrian Roos12c1ef52014-06-04 13:54:08 +0200342 if (mVisible) {
Lucas Dupin53d50622017-05-13 15:54:14 -0700343 // Walk down a precedence-ordered list of what indication
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600344 // should be shown based on user or device state
Lucas Dupinff6628d2018-10-15 10:12:37 -0700345 if (mDozing) {
Lucas Dupin07ed3df2019-03-26 21:23:59 -0700346 // When dozing we ignore any text color and use white instead, because
347 // colors can be hard to read in low brightness.
348 mTextView.setTextColor(Color.WHITE);
Lucas Dupinff6628d2018-10-15 10:12:37 -0700349 if (!TextUtils.isEmpty(mTransientIndication)) {
Lucas Dupinff6628d2018-10-15 10:12:37 -0700350 mTextView.switchIndication(mTransientIndication);
Lucas Dupin07ed3df2019-03-26 21:23:59 -0700351 } else if (mPowerPluggedIn) {
352 String indication = computePowerIndication();
353 if (animate) {
354 animateText(mTextView, indication);
355 } else {
356 mTextView.switchIndication(indication);
357 }
358 } else {
359 String percentage = NumberFormat.getPercentInstance()
360 .format(mBatteryLevel / 100f);
361 mTextView.switchIndication(percentage);
Lucas Dupinff6628d2018-10-15 10:12:37 -0700362 }
Lucas Dupinff6628d2018-10-15 10:12:37 -0700363 return;
364 }
365
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700366 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Jorim Jaggifabc7432017-05-15 02:40:05 +0200367 int userId = KeyguardUpdateMonitor.getCurrentUser();
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700368 String trustGrantedIndication = getTrustGrantedIndication();
369 String trustManagedIndication = getTrustManagedIndication();
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700370 if (!mUserManager.isUserUnlocked(userId)) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600371 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
Jason Chang2386a372018-04-24 16:05:30 +0800372 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600373 } else if (!TextUtils.isEmpty(mTransientIndication)) {
374 mTextView.switchIndication(mTransientIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800375 mTextView.setTextColor(mTransientTextColorState);
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700376 } else if (!TextUtils.isEmpty(trustGrantedIndication)
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700377 && updateMonitor.getUserHasTrust(userId)) {
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700378 mTextView.switchIndication(trustGrantedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800379 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600380 } else if (mPowerPluggedIn) {
381 String indication = computePowerIndication();
382 if (DEBUG_CHARGING_SPEED) {
383 indication += ", " + (mChargingWattage / 1000) + " mW";
384 }
Jason Chang2386a372018-04-24 16:05:30 +0800385 mTextView.setTextColor(mInitialTextColorState);
Beverly85499d92018-02-14 15:55:16 -0500386 if (animate) {
387 animateText(mTextView, indication);
388 } else {
389 mTextView.switchIndication(indication);
390 }
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700391 } else if (!TextUtils.isEmpty(trustManagedIndication)
392 && updateMonitor.getUserTrustIsManaged(userId)
393 && !updateMonitor.getUserHasTrust(userId)) {
394 mTextView.switchIndication(trustManagedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800395 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600396 } else {
397 mTextView.switchIndication(mRestingIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800398 mTextView.setTextColor(mInitialTextColorState);
Adrian Roos7b043112015-07-10 13:00:33 -0700399 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200400 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200401 }
402
Beverly85499d92018-02-14 15:55:16 -0500403 // animates textView - textView moves up and bounces down
404 private void animateText(KeyguardIndicationTextView textView, String indication) {
405 int yTranslation = mContext.getResources().getInteger(
406 R.integer.wired_charging_keyguard_text_animation_distance);
407 int animateUpDuration = mContext.getResources().getInteger(
408 R.integer.wired_charging_keyguard_text_animation_duration_up);
409 int animateDownDuration = mContext.getResources().getInteger(
410 R.integer.wired_charging_keyguard_text_animation_duration_down);
Lucas Dupindef43692018-07-02 15:22:58 -0700411 textView.animate().cancel();
412 float translation = textView.getTranslationY();
Beverly85499d92018-02-14 15:55:16 -0500413 textView.animate()
414 .translationYBy(yTranslation)
415 .setInterpolator(Interpolators.LINEAR)
416 .setDuration(animateUpDuration)
417 .setListener(new AnimatorListenerAdapter() {
Lucas Dupindef43692018-07-02 15:22:58 -0700418 private boolean mCancelled;
419
Beverly85499d92018-02-14 15:55:16 -0500420 @Override
421 public void onAnimationStart(Animator animation) {
422 textView.switchIndication(indication);
423 }
Lucas Dupindef43692018-07-02 15:22:58 -0700424
425 @Override
426 public void onAnimationCancel(Animator animation) {
427 textView.setTranslationY(translation);
428 mCancelled = true;
429 }
430
Beverly85499d92018-02-14 15:55:16 -0500431 @Override
432 public void onAnimationEnd(Animator animation) {
Lucas Dupindef43692018-07-02 15:22:58 -0700433 if (mCancelled) {
434 return;
435 }
Beverly85499d92018-02-14 15:55:16 -0500436 textView.animate()
437 .setDuration(animateDownDuration)
438 .setInterpolator(Interpolators.BOUNCE)
Lucas Dupindef43692018-07-02 15:22:58 -0700439 .translationY(translation)
440 .setListener(new AnimatorListenerAdapter() {
441 @Override
442 public void onAnimationCancel(Animator animation) {
443 textView.setTranslationY(translation);
444 }
445 });
Beverly85499d92018-02-14 15:55:16 -0500446 }
447 });
448 }
449
Adrian Roos12c1ef52014-06-04 13:54:08 +0200450 private String computePowerIndication() {
451 if (mPowerCharged) {
452 return mContext.getResources().getString(R.string.keyguard_charged);
453 }
454
455 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700456 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200457 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700458 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
459
Adrian Roos12c1ef52014-06-04 13:54:08 +0200460 } catch (RemoteException e) {
461 Log.e(TAG, "Error calling IBatteryStats: ", e);
462 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700463 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200464
Adrian Roos7b043112015-07-10 13:00:33 -0700465 int chargingId;
Beverly7d7f6992019-02-11 13:58:27 -0500466 if (mPowerPluggedInWired) {
467 switch (mChargingSpeed) {
468 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
469 chargingId = hasChargingTime
470 ? R.string.keyguard_indication_charging_time_fast
471 : R.string.keyguard_plugged_in_charging_fast;
472 break;
473 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
474 chargingId = hasChargingTime
475 ? R.string.keyguard_indication_charging_time_slowly
476 : R.string.keyguard_plugged_in_charging_slowly;
477 break;
478 default:
479 chargingId = hasChargingTime
480 ? R.string.keyguard_indication_charging_time
481 : R.string.keyguard_plugged_in;
482 break;
483 }
484 } else {
485 chargingId = hasChargingTime
486 ? R.string.keyguard_indication_charging_time_wireless
487 : R.string.keyguard_plugged_in_wireless;
Adrian Roos7b043112015-07-10 13:00:33 -0700488 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700489
Lucas Dupin8d595d22018-03-08 10:34:58 -0800490 String percentage = NumberFormat.getPercentInstance()
491 .format(mBatteryLevel / 100f);
Adrian Roos7e39e592015-09-23 17:03:47 -0700492 if (hasChargingTime) {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800493 // We now have battery percentage in these strings and it's expected that all
494 // locales will also have it in the future. For now, we still have to support the old
495 // format until all languages get the new translations.
Adrian Roos7e39e592015-09-23 17:03:47 -0700496 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
497 mContext, chargingTimeRemaining);
Lucas Dupin8d595d22018-03-08 10:34:58 -0800498 try {
499 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
500 percentage);
501 } catch (IllegalFormatConversionException e) {
502 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
503 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700504 } else {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800505 // Same as above
506 try {
507 return mContext.getResources().getString(chargingId, percentage);
508 } catch (IllegalFormatConversionException e) {
509 return mContext.getResources().getString(chargingId);
510 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700511 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200512 }
513
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800514 public void setStatusBarKeyguardViewManager(
515 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
516 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
517 }
518
Lucas Dupin7e171e22018-12-20 11:29:35 -0800519 private final KeyguardUpdateMonitorCallback mTickReceiver =
520 new KeyguardUpdateMonitorCallback() {
521 @Override
522 public void onTimeChanged() {
523 if (mVisible) {
524 updateIndication(false /* animate */);
525 }
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800526 }
Lucas Dupin7e171e22018-12-20 11:29:35 -0800527 };
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800528
529 private final Handler mHandler = new Handler() {
530 @Override
531 public void handleMessage(Message msg) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100532 if (msg.what == MSG_HIDE_TRANSIENT) {
533 hideTransientIndication();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200534 } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
Lucas Dupinc9e5d762019-01-28 09:34:30 -0800535 mLockIcon.setTransientBiometricsError(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800536 }
537 }
538 };
539
Adrian Roos91ba3072017-02-14 16:50:46 +0100540 public void setDozing(boolean dozing) {
Jorim Jaggifabc7432017-05-15 02:40:05 +0200541 if (mDozing == dozing) {
542 return;
543 }
Adrian Roos91ba3072017-02-14 16:50:46 +0100544 mDozing = dozing;
Beverly8c785892018-01-31 17:25:52 -0500545 updateIndication(false);
Adrian Roos91ba3072017-02-14 16:50:46 +0100546 updateDisclosure();
547 }
548
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800549 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
550 pw.println("KeyguardIndicationController:");
Jason Chang2386a372018-04-24 16:05:30 +0800551 pw.println(" mTransientTextColorState: " + mTransientTextColorState);
552 pw.println(" mInitialTextColorState: " + mInitialTextColorState);
Beverly2034c832018-03-19 11:18:51 -0400553 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800554 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
555 pw.println(" mPowerCharged: " + mPowerCharged);
556 pw.println(" mChargingSpeed: " + mChargingSpeed);
557 pw.println(" mChargingWattage: " + mChargingWattage);
558 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
559 pw.println(" mDozing: " + mDozing);
560 pw.println(" mBatteryLevel: " + mBatteryLevel);
561 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
562 pw.println(" computePowerIndication(): " + computePowerIndication());
563 }
564
Evan Laird878c8532018-10-15 15:54:29 -0400565 @Override
566 public void onStateChanged(int newState) {
567 // don't care
568 }
569
570 @Override
571 public void onDozingChanged(boolean isDozing) {
572 setDozing(isDozing);
573 }
574
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800575 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
Adrian Roos56021892017-02-27 20:25:09 +0100576 public static final int HIDE_DELAY_MS = 5000;
Selim Cinek3e451942016-07-14 18:07:53 -0700577
Adrian Roos12c1ef52014-06-04 13:54:08 +0200578 @Override
579 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100580 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200581 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roos56021892017-02-27 20:25:09 +0100582 boolean wasPluggedIn = mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400583 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100584 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200585 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800586 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700587 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Lucas Dupin4272f442018-01-13 22:00:35 -0800588 mBatteryLevel = status.level;
Beverly2034c832018-03-19 11:18:51 -0400589 updateIndication(!wasPluggedIn && mPowerPluggedInWired);
Adrian Roosc1b50322017-02-27 21:07:58 +0100590 if (mDozing) {
591 if (!wasPluggedIn && mPowerPluggedIn) {
592 showTransientIndication(computePowerIndication());
593 hideTransientIndicationDelayed(HIDE_DELAY_MS);
594 } else if (wasPluggedIn && !mPowerPluggedIn) {
595 hideTransientIndication();
596 }
Adrian Roos56021892017-02-27 20:25:09 +0100597 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200598 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700599
600 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100601 public void onKeyguardVisibilityChanged(boolean showing) {
602 if (showing) {
603 updateDisclosure();
604 }
605 }
606
607 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200608 public void onBiometricHelp(int msgId, String helpString,
609 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700610 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200611 if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700612 return;
613 }
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700614 animatePadlockError();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700615 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Jason Chang2386a372018-04-24 16:05:30 +0800616 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
Lucas Dupinc2d11b32019-03-06 16:02:18 -0800617 mInitialTextColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700618 } else if (updateMonitor.isScreenOn()) {
Lucas Dupinc2d11b32019-03-06 16:02:18 -0800619 showTransientIndication(helpString);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200620 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700621 }
622 }
623
624 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200625 public void onBiometricError(int msgId, String errString,
626 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700627 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200628 if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700629 return;
630 }
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700631 animatePadlockError();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700632 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700633 mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700634 } else if (updateMonitor.isScreenOn()) {
Lucas Dupinc2d11b32019-03-06 16:02:18 -0800635 showTransientIndication(errString);
Selim Cinek3e451942016-07-14 18:07:53 -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 Cinek3e451942016-07-14 18:07:53 -0700638 } else {
639 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700640 }
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700641 }
642
643 private void animatePadlockError() {
644 mLockIcon.setTransientBiometricsError(true);
645 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
646 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
647 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700648 }
649
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200650 private boolean shouldSuppressBiometricError(int msgId,
651 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
652 if (biometricSourceType == BiometricSourceType.FINGERPRINT)
653 return shouldSuppressFingerprintError(msgId, updateMonitor);
654 if (biometricSourceType == BiometricSourceType.FACE)
655 return shouldSuppressFaceError(msgId, updateMonitor);
656 return false;
657 }
658
659 private boolean shouldSuppressFingerprintError(int msgId,
660 KeyguardUpdateMonitor updateMonitor) {
661 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
662 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
663 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
664 }
665
666 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
667 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
668 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
669 || msgId == FaceManager.FACE_ERROR_CANCELED);
670 }
671
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700672 @Override
Lucas Dupinef886542018-01-03 16:03:07 -0800673 public void onTrustAgentErrorMessage(CharSequence message) {
Jason Chang2386a372018-04-24 16:05:30 +0800674 showTransientIndication(message, Utils.getColorError(mContext));
Lucas Dupinef886542018-01-03 16:03:07 -0800675 }
676
677 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700678 public void onScreenTurnedOn() {
679 if (mMessageToShowOnScreenOn != null) {
Jason Chang2386a372018-04-24 16:05:30 +0800680 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700681 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100682 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700683 mMessageToShowOnScreenOn = null;
684 }
685 }
686
687 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200688 public void onBiometricRunningStateChanged(boolean running,
689 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700690 if (running) {
Lucas Dupin8f3faac2019-03-12 15:28:49 -0700691 // Let's hide any previous messages when authentication starts, otherwise
692 // multiple auth attempts would overlap.
693 hideTransientIndication();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700694 mMessageToShowOnScreenOn = null;
695 }
696 }
Selim Cinek3e451942016-07-14 18:07:53 -0700697
698 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200699 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
700 super.onBiometricAuthenticated(userId, biometricSourceType);
Kevin Chyn6d793bd2019-03-27 15:36:13 -0700701 mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
Selim Cinek3e451942016-07-14 18:07:53 -0700702 }
703
704 @Override
Jorim Jaggidadafd42016-09-30 07:20:25 -0700705 public void onUserUnlocked() {
706 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500707 updateIndication(false);
Jorim Jaggidadafd42016-09-30 07:20:25 -0700708 }
709 }
Lucas Dupin05904652019-04-09 16:16:15 -0700710
711 @Override
712 public void onKeyguardBouncerChanged(boolean bouncer) {
713 mLockIcon.setBouncerVisible(bouncer);
714 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200715 };
Adrian Roos12c1ef52014-06-04 13:54:08 +0200716}