blob: 1a54f653b7d00ee8de328a8da632aedab56699ef [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;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070057import 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;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070086 private final LockPatternUtils mLockPatternUtils;
Adrian Roos12c1ef52014-06-04 13:54:08 +020087
Adrian Roos7b043112015-07-10 13:00:33 -070088 private final int mSlowThreshold;
89 private final int mFastThreshold;
Lucas Dupin05904652019-04-09 16:16:15 -070090 private final LockIcon mLockIcon;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070091 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Lucas Dupin2e838ac2019-04-17 16:50:58 -070092 private LockscreenGestureLogger mLockscreenGestureLogger = new LockscreenGestureLogger();
Adrian Roos7b043112015-07-10 13:00:33 -070093
Adrian Roos12c1ef52014-06-04 13:54:08 +020094 private String mRestingIndication;
Lucas Dupinef886542018-01-03 16:03:07 -080095 private CharSequence mTransientIndication;
Jason Chang2386a372018-04-24 16:05:30 +080096 private ColorStateList mTransientTextColorState;
97 private ColorStateList mInitialTextColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +020098 private boolean mVisible;
99
100 private boolean mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400101 private boolean mPowerPluggedInWired;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200102 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -0700103 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -0800104 private int mChargingWattage;
Lucas Dupin4272f442018-01-13 22:00:35 -0800105 private int mBatteryLevel;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700106 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200107
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700108 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800109
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100110 private final DevicePolicyManager mDevicePolicyManager;
Adrian Roos91ba3072017-02-14 16:50:46 +0100111 private boolean mDozing;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100112
Adrian Roosaf45b602017-03-14 13:10:25 -0700113 /**
114 * Creates a new KeyguardIndicationController and registers callbacks.
115 */
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100116 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
117 LockIcon lockIcon) {
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700118 this(context, indicationArea, lockIcon, new LockPatternUtils(context),
Adrian Roosc1b50322017-02-27 21:07:58 +0100119 WakeLock.createPartial(context, "Doze:KeyguardIndication"));
Adrian Roosaf45b602017-03-14 13:10:25 -0700120
121 registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
Adrian Roosc1b50322017-02-27 21:07:58 +0100122 }
123
Adrian Roosaf45b602017-03-14 13:10:25 -0700124 /**
125 * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
126 */
Adrian Roosc1b50322017-02-27 21:07:58 +0100127 @VisibleForTesting
128 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700129 LockPatternUtils lockPatternUtils, WakeLock wakeLock) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200130 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100131 mIndicationArea = indicationArea;
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800132 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
Jason Chang2386a372018-04-24 16:05:30 +0800133 mInitialTextColorState = mTextView != null ?
134 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800135 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700136 mLockIcon = lockIcon;
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700137 mLockIcon.setOnLongClickListener(this::handleTrustCircleClick);
Lucas Dupinee4c9b72019-02-18 17:04:58 -0800138 mWakeLock = new SettableWakeLock(wakeLock, TAG);
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700139 mLockPatternUtils = lockPatternUtils;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200140
Adrian Roos7b043112015-07-10 13:00:33 -0700141 Resources res = context.getResources();
142 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
143 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
144
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600145 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200146 mBatteryInfo = IBatteryStats.Stub.asInterface(
147 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600148
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100149 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
150 Context.DEVICE_POLICY_SERVICE);
151
Adrian Roosaf45b602017-03-14 13:10:25 -0700152 updateDisclosure();
153 }
154
155 private void registerCallbacks(KeyguardUpdateMonitor monitor) {
156 monitor.registerCallback(getKeyguardCallback());
157
Lucas Dupin7e171e22018-12-20 11:29:35 -0800158 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mTickReceiver);
Jason Monkaf08c152018-12-04 11:12:39 -0500159 Dependency.get(StatusBarStateController.class).addCallback(this);
Evan Laird878c8532018-10-15 15:54:29 -0400160 }
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() {
Lucas Dupin7e171e22018-12-20 11:29:35 -0800169 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mTickReceiver);
Jason Monkaf08c152018-12-04 11:12:39 -0500170 Dependency.get(StatusBarStateController.class).removeCallback(this);
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100171 }
172
Lucas Dupin2e838ac2019-04-17 16:50:58 -0700173 private boolean handleTrustCircleClick(View view) {
174 mLockscreenGestureLogger.write(MetricsProto.MetricsEvent.ACTION_LS_LOCK,
175 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */);
176 showTransientIndication(R.string.keyguard_indication_trust_disabled);
177 mLockPatternUtils.requireCredentialEntry(KeyguardUpdateMonitor.getCurrentUser());
178
179 return true;
180 }
181
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800182 /**
183 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
184 * {@link KeyguardIndicationController}.
185 *
186 * <p>Subclasses may override this method to extend or change the callback behavior by extending
187 * the {@link BaseKeyguardCallback}.
188 *
189 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
190 * same instance.
191 */
192 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700193 if (mUpdateMonitorCallback == null) {
194 mUpdateMonitorCallback = new BaseKeyguardCallback();
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800195 }
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700196 return mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800197 }
198
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100199 private void updateDisclosure() {
200 if (mDevicePolicyManager == null) {
201 return;
202 }
203
Adrian Roos91ba3072017-02-14 16:50:46 +0100204 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100205 final CharSequence organizationName =
206 mDevicePolicyManager.getDeviceOwnerOrganizationName();
207 if (organizationName != null) {
208 mDisclosure.switchIndication(mContext.getResources().getString(
209 R.string.do_disclosure_with_name, organizationName));
210 } else {
211 mDisclosure.switchIndication(R.string.do_disclosure_generic);
212 }
213 mDisclosure.setVisibility(View.VISIBLE);
214 } else {
215 mDisclosure.setVisibility(View.GONE);
216 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200217 }
218
219 public void setVisible(boolean visible) {
220 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100221 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200222 if (visible) {
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700223 // If this is called after an error message was already shown, we should not clear it.
224 // Otherwise the error message won't be shown
225 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
226 hideTransientIndication();
227 }
Beverly8c785892018-01-31 17:25:52 -0500228 updateIndication(false);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700229 } else if (!visible) {
230 // If we unlock and return to keyguard quickly, previous error should not be shown
231 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200232 }
233 }
234
235 /**
236 * Sets the indication that is shown if nothing else is showing.
237 */
238 public void setRestingIndication(String restingIndication) {
239 mRestingIndication = restingIndication;
Beverly8c785892018-01-31 17:25:52 -0500240 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200241 }
242
243 /**
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800244 * Sets the active controller managing changes and callbacks to user information.
245 */
246 public void setUserInfoController(UserInfoController userInfoController) {
247 }
248
249 /**
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700250 * Returns the indication text indicating that trust has been granted.
251 *
252 * @return {@code null} or an empty string if a trust indication text should not be shown.
253 */
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700254 protected String getTrustGrantedIndication() {
255 return null;
256 }
257
258 /**
259 * Returns the indication text indicating that trust is currently being managed.
260 *
261 * @return {@code null} or an empty string if a trust managed text should not be shown.
262 */
263 protected String getTrustManagedIndication() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700264 return null;
265 }
266
267 /**
Adrian Roos12c1ef52014-06-04 13:54:08 +0200268 * Hides transient indication in {@param delayMs}.
269 */
270 public void hideTransientIndicationDelayed(long delayMs) {
271 mHandler.sendMessageDelayed(
272 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
273 }
274
275 /**
276 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
277 */
278 public void showTransientIndication(int transientIndication) {
279 showTransientIndication(mContext.getResources().getString(transientIndication));
280 }
281
282 /**
283 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
284 */
Lucas Dupinef886542018-01-03 16:03:07 -0800285 public void showTransientIndication(CharSequence transientIndication) {
Jason Chang2386a372018-04-24 16:05:30 +0800286 showTransientIndication(transientIndication, mInitialTextColorState);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700287 }
288
289 /**
290 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
291 */
Jason Chang2386a372018-04-24 16:05:30 +0800292 public void showTransientIndication(CharSequence transientIndication,
293 ColorStateList textColorState) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200294 mTransientIndication = transientIndication;
Jason Chang2386a372018-04-24 16:05:30 +0800295 mTransientTextColorState = textColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200296 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Adrian Roosc1b50322017-02-27 21:07:58 +0100297 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
298 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
299 mWakeLock.setAcquired(true);
300 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
301 }
Beverly8c785892018-01-31 17:25:52 -0500302
303 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200304 }
305
306 /**
307 * Hides transient indication.
308 */
309 public void hideTransientIndication() {
310 if (mTransientIndication != null) {
311 mTransientIndication = null;
312 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Beverly8c785892018-01-31 17:25:52 -0500313 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200314 }
315 }
316
Beverly8c785892018-01-31 17:25:52 -0500317 protected final void updateIndication(boolean animate) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100318 if (TextUtils.isEmpty(mTransientIndication)) {
319 mWakeLock.setAcquired(false);
320 }
321
Adrian Roos12c1ef52014-06-04 13:54:08 +0200322 if (mVisible) {
Lucas Dupin53d50622017-05-13 15:54:14 -0700323 // Walk down a precedence-ordered list of what indication
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600324 // should be shown based on user or device state
Lucas Dupinff6628d2018-10-15 10:12:37 -0700325 if (mDozing) {
Lucas Dupin07ed3df2019-03-26 21:23:59 -0700326 // When dozing we ignore any text color and use white instead, because
327 // colors can be hard to read in low brightness.
328 mTextView.setTextColor(Color.WHITE);
Lucas Dupinff6628d2018-10-15 10:12:37 -0700329 if (!TextUtils.isEmpty(mTransientIndication)) {
Lucas Dupinff6628d2018-10-15 10:12:37 -0700330 mTextView.switchIndication(mTransientIndication);
Lucas Dupin07ed3df2019-03-26 21:23:59 -0700331 } else if (mPowerPluggedIn) {
332 String indication = computePowerIndication();
333 if (animate) {
334 animateText(mTextView, indication);
335 } else {
336 mTextView.switchIndication(indication);
337 }
338 } else {
339 String percentage = NumberFormat.getPercentInstance()
340 .format(mBatteryLevel / 100f);
341 mTextView.switchIndication(percentage);
Lucas Dupinff6628d2018-10-15 10:12:37 -0700342 }
Lucas Dupinff6628d2018-10-15 10:12:37 -0700343 return;
344 }
345
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700346 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Jorim Jaggifabc7432017-05-15 02:40:05 +0200347 int userId = KeyguardUpdateMonitor.getCurrentUser();
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700348 String trustGrantedIndication = getTrustGrantedIndication();
349 String trustManagedIndication = getTrustManagedIndication();
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700350 if (!mUserManager.isUserUnlocked(userId)) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600351 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
Jason Chang2386a372018-04-24 16:05:30 +0800352 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600353 } else if (!TextUtils.isEmpty(mTransientIndication)) {
354 mTextView.switchIndication(mTransientIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800355 mTextView.setTextColor(mTransientTextColorState);
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700356 } else if (!TextUtils.isEmpty(trustGrantedIndication)
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700357 && updateMonitor.getUserHasTrust(userId)) {
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700358 mTextView.switchIndication(trustGrantedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800359 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600360 } else if (mPowerPluggedIn) {
361 String indication = computePowerIndication();
362 if (DEBUG_CHARGING_SPEED) {
363 indication += ", " + (mChargingWattage / 1000) + " mW";
364 }
Jason Chang2386a372018-04-24 16:05:30 +0800365 mTextView.setTextColor(mInitialTextColorState);
Beverly85499d92018-02-14 15:55:16 -0500366 if (animate) {
367 animateText(mTextView, indication);
368 } else {
369 mTextView.switchIndication(indication);
370 }
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700371 } else if (!TextUtils.isEmpty(trustManagedIndication)
372 && updateMonitor.getUserTrustIsManaged(userId)
373 && !updateMonitor.getUserHasTrust(userId)) {
374 mTextView.switchIndication(trustManagedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800375 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600376 } else {
377 mTextView.switchIndication(mRestingIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800378 mTextView.setTextColor(mInitialTextColorState);
Adrian Roos7b043112015-07-10 13:00:33 -0700379 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200380 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200381 }
382
Beverly85499d92018-02-14 15:55:16 -0500383 // animates textView - textView moves up and bounces down
384 private void animateText(KeyguardIndicationTextView textView, String indication) {
385 int yTranslation = mContext.getResources().getInteger(
386 R.integer.wired_charging_keyguard_text_animation_distance);
387 int animateUpDuration = mContext.getResources().getInteger(
388 R.integer.wired_charging_keyguard_text_animation_duration_up);
389 int animateDownDuration = mContext.getResources().getInteger(
390 R.integer.wired_charging_keyguard_text_animation_duration_down);
Lucas Dupindef43692018-07-02 15:22:58 -0700391 textView.animate().cancel();
392 float translation = textView.getTranslationY();
Beverly85499d92018-02-14 15:55:16 -0500393 textView.animate()
394 .translationYBy(yTranslation)
395 .setInterpolator(Interpolators.LINEAR)
396 .setDuration(animateUpDuration)
397 .setListener(new AnimatorListenerAdapter() {
Lucas Dupindef43692018-07-02 15:22:58 -0700398 private boolean mCancelled;
399
Beverly85499d92018-02-14 15:55:16 -0500400 @Override
401 public void onAnimationStart(Animator animation) {
402 textView.switchIndication(indication);
403 }
Lucas Dupindef43692018-07-02 15:22:58 -0700404
405 @Override
406 public void onAnimationCancel(Animator animation) {
407 textView.setTranslationY(translation);
408 mCancelled = true;
409 }
410
Beverly85499d92018-02-14 15:55:16 -0500411 @Override
412 public void onAnimationEnd(Animator animation) {
Lucas Dupindef43692018-07-02 15:22:58 -0700413 if (mCancelled) {
414 return;
415 }
Beverly85499d92018-02-14 15:55:16 -0500416 textView.animate()
417 .setDuration(animateDownDuration)
418 .setInterpolator(Interpolators.BOUNCE)
Lucas Dupindef43692018-07-02 15:22:58 -0700419 .translationY(translation)
420 .setListener(new AnimatorListenerAdapter() {
421 @Override
422 public void onAnimationCancel(Animator animation) {
423 textView.setTranslationY(translation);
424 }
425 });
Beverly85499d92018-02-14 15:55:16 -0500426 }
427 });
428 }
429
Adrian Roos12c1ef52014-06-04 13:54:08 +0200430 private String computePowerIndication() {
431 if (mPowerCharged) {
432 return mContext.getResources().getString(R.string.keyguard_charged);
433 }
434
435 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700436 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200437 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700438 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
439
Adrian Roos12c1ef52014-06-04 13:54:08 +0200440 } catch (RemoteException e) {
441 Log.e(TAG, "Error calling IBatteryStats: ", e);
442 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700443 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200444
Adrian Roos7b043112015-07-10 13:00:33 -0700445 int chargingId;
Beverly7d7f6992019-02-11 13:58:27 -0500446 if (mPowerPluggedInWired) {
447 switch (mChargingSpeed) {
448 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
449 chargingId = hasChargingTime
450 ? R.string.keyguard_indication_charging_time_fast
451 : R.string.keyguard_plugged_in_charging_fast;
452 break;
453 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
454 chargingId = hasChargingTime
455 ? R.string.keyguard_indication_charging_time_slowly
456 : R.string.keyguard_plugged_in_charging_slowly;
457 break;
458 default:
459 chargingId = hasChargingTime
460 ? R.string.keyguard_indication_charging_time
461 : R.string.keyguard_plugged_in;
462 break;
463 }
464 } else {
465 chargingId = hasChargingTime
466 ? R.string.keyguard_indication_charging_time_wireless
467 : R.string.keyguard_plugged_in_wireless;
Adrian Roos7b043112015-07-10 13:00:33 -0700468 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700469
Lucas Dupin8d595d22018-03-08 10:34:58 -0800470 String percentage = NumberFormat.getPercentInstance()
471 .format(mBatteryLevel / 100f);
Adrian Roos7e39e592015-09-23 17:03:47 -0700472 if (hasChargingTime) {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800473 // We now have battery percentage in these strings and it's expected that all
474 // locales will also have it in the future. For now, we still have to support the old
475 // format until all languages get the new translations.
Adrian Roos7e39e592015-09-23 17:03:47 -0700476 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
477 mContext, chargingTimeRemaining);
Lucas Dupin8d595d22018-03-08 10:34:58 -0800478 try {
479 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
480 percentage);
481 } catch (IllegalFormatConversionException e) {
482 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
483 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700484 } else {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800485 // Same as above
486 try {
487 return mContext.getResources().getString(chargingId, percentage);
488 } catch (IllegalFormatConversionException e) {
489 return mContext.getResources().getString(chargingId);
490 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700491 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200492 }
493
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800494 public void setStatusBarKeyguardViewManager(
495 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
496 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
497 }
498
Lucas Dupin7e171e22018-12-20 11:29:35 -0800499 private final KeyguardUpdateMonitorCallback mTickReceiver =
500 new KeyguardUpdateMonitorCallback() {
501 @Override
502 public void onTimeChanged() {
503 if (mVisible) {
504 updateIndication(false /* animate */);
505 }
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800506 }
Lucas Dupin7e171e22018-12-20 11:29:35 -0800507 };
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800508
509 private final Handler mHandler = new Handler() {
510 @Override
511 public void handleMessage(Message msg) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100512 if (msg.what == MSG_HIDE_TRANSIENT) {
513 hideTransientIndication();
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200514 } else if (msg.what == MSG_CLEAR_BIOMETRIC_MSG) {
Lucas Dupinc9e5d762019-01-28 09:34:30 -0800515 mLockIcon.setTransientBiometricsError(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800516 }
517 }
518 };
519
Adrian Roos91ba3072017-02-14 16:50:46 +0100520 public void setDozing(boolean dozing) {
Jorim Jaggifabc7432017-05-15 02:40:05 +0200521 if (mDozing == dozing) {
522 return;
523 }
Adrian Roos91ba3072017-02-14 16:50:46 +0100524 mDozing = dozing;
Beverly8c785892018-01-31 17:25:52 -0500525 updateIndication(false);
Adrian Roos91ba3072017-02-14 16:50:46 +0100526 updateDisclosure();
527 }
528
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800529 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
530 pw.println("KeyguardIndicationController:");
Jason Chang2386a372018-04-24 16:05:30 +0800531 pw.println(" mTransientTextColorState: " + mTransientTextColorState);
532 pw.println(" mInitialTextColorState: " + mInitialTextColorState);
Beverly2034c832018-03-19 11:18:51 -0400533 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800534 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
535 pw.println(" mPowerCharged: " + mPowerCharged);
536 pw.println(" mChargingSpeed: " + mChargingSpeed);
537 pw.println(" mChargingWattage: " + mChargingWattage);
538 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
539 pw.println(" mDozing: " + mDozing);
540 pw.println(" mBatteryLevel: " + mBatteryLevel);
541 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
542 pw.println(" computePowerIndication(): " + computePowerIndication());
543 }
544
Evan Laird878c8532018-10-15 15:54:29 -0400545 @Override
546 public void onStateChanged(int newState) {
547 // don't care
548 }
549
550 @Override
551 public void onDozingChanged(boolean isDozing) {
552 setDozing(isDozing);
553 }
554
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800555 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
Adrian Roos56021892017-02-27 20:25:09 +0100556 public static final int HIDE_DELAY_MS = 5000;
Selim Cinek3e451942016-07-14 18:07:53 -0700557
Adrian Roos12c1ef52014-06-04 13:54:08 +0200558 @Override
559 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100560 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200561 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roos56021892017-02-27 20:25:09 +0100562 boolean wasPluggedIn = mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400563 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100564 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200565 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800566 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700567 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Lucas Dupin4272f442018-01-13 22:00:35 -0800568 mBatteryLevel = status.level;
Beverly2034c832018-03-19 11:18:51 -0400569 updateIndication(!wasPluggedIn && mPowerPluggedInWired);
Adrian Roosc1b50322017-02-27 21:07:58 +0100570 if (mDozing) {
571 if (!wasPluggedIn && mPowerPluggedIn) {
572 showTransientIndication(computePowerIndication());
573 hideTransientIndicationDelayed(HIDE_DELAY_MS);
574 } else if (wasPluggedIn && !mPowerPluggedIn) {
575 hideTransientIndication();
576 }
Adrian Roos56021892017-02-27 20:25:09 +0100577 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200578 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700579
580 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100581 public void onKeyguardVisibilityChanged(boolean showing) {
582 if (showing) {
583 updateDisclosure();
584 }
585 }
586
587 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200588 public void onBiometricHelp(int msgId, String helpString,
589 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700590 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200591 if (!updateMonitor.isUnlockingWithBiometricAllowed()) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700592 return;
593 }
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700594 animatePadlockError();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700595 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Jason Chang2386a372018-04-24 16:05:30 +0800596 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
Lucas Dupinc2d11b32019-03-06 16:02:18 -0800597 mInitialTextColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700598 } else if (updateMonitor.isScreenOn()) {
Lucas Dupinc2d11b32019-03-06 16:02:18 -0800599 showTransientIndication(helpString);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200600 hideTransientIndicationDelayed(TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700601 }
602 }
603
604 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200605 public void onBiometricError(int msgId, String errString,
606 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700607 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200608 if (shouldSuppressBiometricError(msgId, biometricSourceType, updateMonitor)) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700609 return;
610 }
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700611 animatePadlockError();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700612 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700613 mStatusBarKeyguardViewManager.showBouncerMessage(errString, mInitialTextColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700614 } else if (updateMonitor.isScreenOn()) {
Lucas Dupinc2d11b32019-03-06 16:02:18 -0800615 showTransientIndication(errString);
Selim Cinek3e451942016-07-14 18:07:53 -0700616 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100617 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinek3e451942016-07-14 18:07:53 -0700618 } else {
619 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700620 }
Lucas Dupine54ad1d2019-04-09 17:08:46 -0700621 }
622
623 private void animatePadlockError() {
624 mLockIcon.setTransientBiometricsError(true);
625 mHandler.removeMessages(MSG_CLEAR_BIOMETRIC_MSG);
626 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_BIOMETRIC_MSG),
627 TRANSIENT_BIOMETRIC_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700628 }
629
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200630 private boolean shouldSuppressBiometricError(int msgId,
631 BiometricSourceType biometricSourceType, KeyguardUpdateMonitor updateMonitor) {
632 if (biometricSourceType == BiometricSourceType.FINGERPRINT)
633 return shouldSuppressFingerprintError(msgId, updateMonitor);
634 if (biometricSourceType == BiometricSourceType.FACE)
635 return shouldSuppressFaceError(msgId, updateMonitor);
636 return false;
637 }
638
639 private boolean shouldSuppressFingerprintError(int msgId,
640 KeyguardUpdateMonitor updateMonitor) {
641 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
642 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
643 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
644 }
645
646 private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
647 return ((!updateMonitor.isUnlockingWithBiometricAllowed()
648 && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
649 || msgId == FaceManager.FACE_ERROR_CANCELED);
650 }
651
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700652 @Override
Lucas Dupinef886542018-01-03 16:03:07 -0800653 public void onTrustAgentErrorMessage(CharSequence message) {
Jason Chang2386a372018-04-24 16:05:30 +0800654 showTransientIndication(message, Utils.getColorError(mContext));
Lucas Dupinef886542018-01-03 16:03:07 -0800655 }
656
657 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700658 public void onScreenTurnedOn() {
659 if (mMessageToShowOnScreenOn != null) {
Jason Chang2386a372018-04-24 16:05:30 +0800660 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700661 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100662 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700663 mMessageToShowOnScreenOn = null;
664 }
665 }
666
667 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200668 public void onBiometricRunningStateChanged(boolean running,
669 BiometricSourceType biometricSourceType) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700670 if (running) {
Lucas Dupin8f3faac2019-03-12 15:28:49 -0700671 // Let's hide any previous messages when authentication starts, otherwise
672 // multiple auth attempts would overlap.
673 hideTransientIndication();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700674 mMessageToShowOnScreenOn = null;
675 }
676 }
Selim Cinek3e451942016-07-14 18:07:53 -0700677
678 @Override
Gilad Brettercb51b8b2018-03-22 17:04:51 +0200679 public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
680 super.onBiometricAuthenticated(userId, biometricSourceType);
Kevin Chyn6d793bd2019-03-27 15:36:13 -0700681 mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
Selim Cinek3e451942016-07-14 18:07:53 -0700682 }
683
684 @Override
Jorim Jaggidadafd42016-09-30 07:20:25 -0700685 public void onUserUnlocked() {
686 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500687 updateIndication(false);
Jorim Jaggidadafd42016-09-30 07:20:25 -0700688 }
689 }
Lucas Dupin05904652019-04-09 16:16:15 -0700690
691 @Override
692 public void onKeyguardBouncerChanged(boolean bouncer) {
693 mLockIcon.setBouncerVisible(bouncer);
694 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200695 };
Adrian Roos12c1ef52014-06-04 13:54:08 +0200696}