blob: 67773f9300de98f1f1a75e80d23d02a9f36f5f18 [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;
Adrian Roos7b043112015-07-10 13:00:33 -070026import android.content.res.Resources;
Jason Chang2386a372018-04-24 16:05:30 +080027import android.content.res.ColorStateList;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070028import android.graphics.Color;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070029import android.hardware.fingerprint.FingerprintManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020030import android.os.BatteryManager;
31import android.os.BatteryStats;
32import android.os.Handler;
33import android.os.Message;
34import android.os.RemoteException;
35import android.os.ServiceManager;
36import android.os.UserHandle;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060037import android.os.UserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020038import android.text.TextUtils;
39import android.text.format.Formatter;
40import android.util.Log;
41import android.view.View;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010042import android.view.ViewGroup;
Adrian Roos12c1ef52014-06-04 13:54:08 +020043
Adrian Roosc1b50322017-02-27 21:07:58 +010044import com.android.internal.annotations.VisibleForTesting;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070045import com.android.internal.app.IBatteryStats;
46import 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;
52import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
53import com.android.systemui.statusbar.phone.LockIcon;
54import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080055import com.android.systemui.statusbar.policy.UserInfoController;
Adrian Roosc1b50322017-02-27 21:07:58 +010056import com.android.systemui.util.wakelock.SettableWakeLock;
57import com.android.systemui.util.wakelock.WakeLock;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070058
Lucas Dupin3fcdd472018-01-19 19:06:45 -080059import java.io.FileDescriptor;
60import java.io.PrintWriter;
Lucas Dupin4272f442018-01-13 22:00:35 -080061import java.text.NumberFormat;
Lucas Dupin8d595d22018-03-08 10:34:58 -080062import java.util.IllegalFormatConversionException;
Lucas Dupin4272f442018-01-13 22:00:35 -080063
Adrian Roos12c1ef52014-06-04 13:54:08 +020064/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070065 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020066 */
67public class KeyguardIndicationController {
68
Adrian Roos0c859ae2015-11-23 16:47:50 -080069 private static final String TAG = "KeyguardIndication";
70 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020071
72 private static final int MSG_HIDE_TRANSIENT = 1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070073 private static final int MSG_CLEAR_FP_MSG = 2;
74 private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020075
76 private final Context mContext;
Lucas Dupin987f1932017-05-13 21:02:52 -070077 private ViewGroup mIndicationArea;
78 private KeyguardIndicationTextView mTextView;
79 private KeyguardIndicationTextView mDisclosure;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060080 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020081 private final IBatteryStats mBatteryInfo;
Adrian Roosc1b50322017-02-27 21:07:58 +010082 private final SettableWakeLock mWakeLock;
Adrian Roos12c1ef52014-06-04 13:54:08 +020083
Adrian Roos7b043112015-07-10 13:00:33 -070084 private final int mSlowThreshold;
85 private final int mFastThreshold;
Lucas Dupin987f1932017-05-13 21:02:52 -070086 private LockIcon mLockIcon;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070087 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Adrian Roos7b043112015-07-10 13:00:33 -070088
Adrian Roos12c1ef52014-06-04 13:54:08 +020089 private String mRestingIndication;
Lucas Dupinef886542018-01-03 16:03:07 -080090 private CharSequence mTransientIndication;
Jason Chang2386a372018-04-24 16:05:30 +080091 private ColorStateList mTransientTextColorState;
92 private ColorStateList mInitialTextColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +020093 private boolean mVisible;
94
95 private boolean mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -040096 private boolean mPowerPluggedInWired;
Adrian Roos12c1ef52014-06-04 13:54:08 +020097 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -070098 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -080099 private int mChargingWattage;
Lucas Dupin4272f442018-01-13 22:00:35 -0800100 private int mBatteryLevel;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700101 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200102
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700103 private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800104
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100105 private final DevicePolicyManager mDevicePolicyManager;
Adrian Roos91ba3072017-02-14 16:50:46 +0100106 private boolean mDozing;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100107
Adrian Roosaf45b602017-03-14 13:10:25 -0700108 /**
109 * Creates a new KeyguardIndicationController and registers callbacks.
110 */
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100111 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
112 LockIcon lockIcon) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100113 this(context, indicationArea, lockIcon,
114 WakeLock.createPartial(context, "Doze:KeyguardIndication"));
Adrian Roosaf45b602017-03-14 13:10:25 -0700115
116 registerCallbacks(KeyguardUpdateMonitor.getInstance(context));
Adrian Roosc1b50322017-02-27 21:07:58 +0100117 }
118
Adrian Roosaf45b602017-03-14 13:10:25 -0700119 /**
120 * Creates a new KeyguardIndicationController for testing. Does *not* register callbacks.
121 */
Adrian Roosc1b50322017-02-27 21:07:58 +0100122 @VisibleForTesting
123 KeyguardIndicationController(Context context, ViewGroup indicationArea, LockIcon lockIcon,
124 WakeLock wakeLock) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200125 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100126 mIndicationArea = indicationArea;
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800127 mTextView = indicationArea.findViewById(R.id.keyguard_indication_text);
Jason Chang2386a372018-04-24 16:05:30 +0800128 mInitialTextColorState = mTextView != null ?
129 mTextView.getTextColors() : ColorStateList.valueOf(Color.WHITE);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800130 mDisclosure = indicationArea.findViewById(R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700131 mLockIcon = lockIcon;
Adrian Roosc1b50322017-02-27 21:07:58 +0100132 mWakeLock = new SettableWakeLock(wakeLock);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200133
Adrian Roos7b043112015-07-10 13:00:33 -0700134 Resources res = context.getResources();
135 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
136 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
137
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600138 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200139 mBatteryInfo = IBatteryStats.Stub.asInterface(
140 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600141
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100142 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
143 Context.DEVICE_POLICY_SERVICE);
144
Adrian Roosaf45b602017-03-14 13:10:25 -0700145 updateDisclosure();
146 }
147
148 private void registerCallbacks(KeyguardUpdateMonitor monitor) {
149 monitor.registerCallback(getKeyguardCallback());
150
151 mContext.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
Jason Monkcd26af72017-01-11 14:32:58 -0500152 new IntentFilter(Intent.ACTION_TIME_TICK), null,
Jason Monk9c7844c2017-01-18 15:21:53 -0500153 Dependency.get(Dependency.TIME_TICK_HANDLER));
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100154 }
155
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800156 /**
157 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
158 * {@link KeyguardIndicationController}.
159 *
160 * <p>Subclasses may override this method to extend or change the callback behavior by extending
161 * the {@link BaseKeyguardCallback}.
162 *
163 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
164 * same instance.
165 */
166 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700167 if (mUpdateMonitorCallback == null) {
168 mUpdateMonitorCallback = new BaseKeyguardCallback();
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800169 }
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700170 return mUpdateMonitorCallback;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800171 }
172
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100173 private void updateDisclosure() {
174 if (mDevicePolicyManager == null) {
175 return;
176 }
177
Adrian Roos91ba3072017-02-14 16:50:46 +0100178 if (!mDozing && mDevicePolicyManager.isDeviceManaged()) {
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100179 final CharSequence organizationName =
180 mDevicePolicyManager.getDeviceOwnerOrganizationName();
181 if (organizationName != null) {
182 mDisclosure.switchIndication(mContext.getResources().getString(
183 R.string.do_disclosure_with_name, organizationName));
184 } else {
185 mDisclosure.switchIndication(R.string.do_disclosure_generic);
186 }
187 mDisclosure.setVisibility(View.VISIBLE);
188 } else {
189 mDisclosure.setVisibility(View.GONE);
190 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200191 }
192
193 public void setVisible(boolean visible) {
194 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100195 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200196 if (visible) {
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700197 // If this is called after an error message was already shown, we should not clear it.
198 // Otherwise the error message won't be shown
199 if (!mHandler.hasMessages(MSG_HIDE_TRANSIENT)) {
200 hideTransientIndication();
201 }
Beverly8c785892018-01-31 17:25:52 -0500202 updateIndication(false);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700203 } else if (!visible) {
204 // If we unlock and return to keyguard quickly, previous error should not be shown
205 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200206 }
207 }
208
209 /**
210 * Sets the indication that is shown if nothing else is showing.
211 */
212 public void setRestingIndication(String restingIndication) {
213 mRestingIndication = restingIndication;
Beverly8c785892018-01-31 17:25:52 -0500214 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200215 }
216
217 /**
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800218 * Sets the active controller managing changes and callbacks to user information.
219 */
220 public void setUserInfoController(UserInfoController userInfoController) {
221 }
222
223 /**
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700224 * Returns the indication text indicating that trust has been granted.
225 *
226 * @return {@code null} or an empty string if a trust indication text should not be shown.
227 */
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700228 protected String getTrustGrantedIndication() {
229 return null;
230 }
231
232 /**
233 * Returns the indication text indicating that trust is currently being managed.
234 *
235 * @return {@code null} or an empty string if a trust managed text should not be shown.
236 */
237 protected String getTrustManagedIndication() {
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700238 return null;
239 }
240
241 /**
Adrian Roos12c1ef52014-06-04 13:54:08 +0200242 * Hides transient indication in {@param delayMs}.
243 */
244 public void hideTransientIndicationDelayed(long delayMs) {
245 mHandler.sendMessageDelayed(
246 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
247 }
248
249 /**
250 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
251 */
252 public void showTransientIndication(int transientIndication) {
253 showTransientIndication(mContext.getResources().getString(transientIndication));
254 }
255
256 /**
257 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
258 */
Lucas Dupinef886542018-01-03 16:03:07 -0800259 public void showTransientIndication(CharSequence transientIndication) {
Jason Chang2386a372018-04-24 16:05:30 +0800260 showTransientIndication(transientIndication, mInitialTextColorState);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700261 }
262
263 /**
264 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
265 */
Jason Chang2386a372018-04-24 16:05:30 +0800266 public void showTransientIndication(CharSequence transientIndication,
267 ColorStateList textColorState) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200268 mTransientIndication = transientIndication;
Jason Chang2386a372018-04-24 16:05:30 +0800269 mTransientTextColorState = textColorState;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200270 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Adrian Roosc1b50322017-02-27 21:07:58 +0100271 if (mDozing && !TextUtils.isEmpty(mTransientIndication)) {
272 // Make sure this doesn't get stuck and burns in. Acquire wakelock until its cleared.
273 mWakeLock.setAcquired(true);
274 hideTransientIndicationDelayed(BaseKeyguardCallback.HIDE_DELAY_MS);
275 }
Beverly8c785892018-01-31 17:25:52 -0500276
277 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200278 }
279
280 /**
281 * Hides transient indication.
282 */
283 public void hideTransientIndication() {
284 if (mTransientIndication != null) {
285 mTransientIndication = null;
286 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
Beverly8c785892018-01-31 17:25:52 -0500287 updateIndication(false);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200288 }
289 }
290
Beverly8c785892018-01-31 17:25:52 -0500291 protected final void updateIndication(boolean animate) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100292 if (TextUtils.isEmpty(mTransientIndication)) {
293 mWakeLock.setAcquired(false);
294 }
295
Adrian Roos12c1ef52014-06-04 13:54:08 +0200296 if (mVisible) {
Lucas Dupin53d50622017-05-13 15:54:14 -0700297 // Walk down a precedence-ordered list of what indication
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600298 // should be shown based on user or device state
Adrian Roos91ba3072017-02-14 16:50:46 +0100299 if (mDozing) {
Lucas Dupin4272f442018-01-13 22:00:35 -0800300 mTextView.setTextColor(Color.WHITE);
Adrian Roos91ba3072017-02-14 16:50:46 +0100301 if (!TextUtils.isEmpty(mTransientIndication)) {
Adrian Roos12e112d2017-07-25 16:46:23 +0200302 // When dozing we ignore any text color and use white instead, because
303 // colors can be hard to read in low brightness.
Adrian Roos91ba3072017-02-14 16:50:46 +0100304 mTextView.switchIndication(mTransientIndication);
Lucas Dupin4272f442018-01-13 22:00:35 -0800305 } else if (mPowerPluggedIn) {
306 String indication = computePowerIndication();
Beverly8c785892018-01-31 17:25:52 -0500307 if (animate) {
Beverly85499d92018-02-14 15:55:16 -0500308 animateText(mTextView, indication);
Beverly8c785892018-01-31 17:25:52 -0500309 } else {
310 mTextView.switchIndication(indication);
311 }
Adrian Roos91ba3072017-02-14 16:50:46 +0100312 } else {
Lucas Dupin4272f442018-01-13 22:00:35 -0800313 String percentage = NumberFormat.getPercentInstance()
314 .format(mBatteryLevel / 100f);
315 mTextView.switchIndication(percentage);
Adrian Roos91ba3072017-02-14 16:50:46 +0100316 }
317 return;
318 }
319
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700320 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Jorim Jaggifabc7432017-05-15 02:40:05 +0200321 int userId = KeyguardUpdateMonitor.getCurrentUser();
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700322 String trustGrantedIndication = getTrustGrantedIndication();
323 String trustManagedIndication = getTrustManagedIndication();
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700324 if (!mUserManager.isUserUnlocked(userId)) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600325 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
Jason Chang2386a372018-04-24 16:05:30 +0800326 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600327 } else if (!TextUtils.isEmpty(mTransientIndication)) {
328 mTextView.switchIndication(mTransientIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800329 mTextView.setTextColor(mTransientTextColorState);
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700330 } else if (!TextUtils.isEmpty(trustGrantedIndication)
Zachary Iqbal8f4c2422017-04-20 17:56:42 -0700331 && updateMonitor.getUserHasTrust(userId)) {
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700332 mTextView.switchIndication(trustGrantedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800333 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600334 } else if (mPowerPluggedIn) {
335 String indication = computePowerIndication();
336 if (DEBUG_CHARGING_SPEED) {
337 indication += ", " + (mChargingWattage / 1000) + " mW";
338 }
Jason Chang2386a372018-04-24 16:05:30 +0800339 mTextView.setTextColor(mInitialTextColorState);
Beverly85499d92018-02-14 15:55:16 -0500340 if (animate) {
341 animateText(mTextView, indication);
342 } else {
343 mTextView.switchIndication(indication);
344 }
Zachary Iqbaldc05aa02017-05-17 18:52:49 -0700345 } else if (!TextUtils.isEmpty(trustManagedIndication)
346 && updateMonitor.getUserTrustIsManaged(userId)
347 && !updateMonitor.getUserHasTrust(userId)) {
348 mTextView.switchIndication(trustManagedIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800349 mTextView.setTextColor(mInitialTextColorState);
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600350 } else {
351 mTextView.switchIndication(mRestingIndication);
Jason Chang2386a372018-04-24 16:05:30 +0800352 mTextView.setTextColor(mInitialTextColorState);
Adrian Roos7b043112015-07-10 13:00:33 -0700353 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200354 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200355 }
356
Beverly85499d92018-02-14 15:55:16 -0500357 // animates textView - textView moves up and bounces down
358 private void animateText(KeyguardIndicationTextView textView, String indication) {
359 int yTranslation = mContext.getResources().getInteger(
360 R.integer.wired_charging_keyguard_text_animation_distance);
361 int animateUpDuration = mContext.getResources().getInteger(
362 R.integer.wired_charging_keyguard_text_animation_duration_up);
363 int animateDownDuration = mContext.getResources().getInteger(
364 R.integer.wired_charging_keyguard_text_animation_duration_down);
365 textView.animate()
366 .translationYBy(yTranslation)
367 .setInterpolator(Interpolators.LINEAR)
368 .setDuration(animateUpDuration)
369 .setListener(new AnimatorListenerAdapter() {
370 @Override
371 public void onAnimationStart(Animator animation) {
372 textView.switchIndication(indication);
373 }
374 @Override
375 public void onAnimationEnd(Animator animation) {
376 textView.animate()
377 .setDuration(animateDownDuration)
378 .setInterpolator(Interpolators.BOUNCE)
379 .translationYBy(-1 * yTranslation)
380 .setListener(null);
381 }
382 });
383 }
384
Adrian Roos12c1ef52014-06-04 13:54:08 +0200385 private String computePowerIndication() {
386 if (mPowerCharged) {
387 return mContext.getResources().getString(R.string.keyguard_charged);
388 }
389
390 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700391 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200392 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700393 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
394
Adrian Roos12c1ef52014-06-04 13:54:08 +0200395 } catch (RemoteException e) {
396 Log.e(TAG, "Error calling IBatteryStats: ", e);
397 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700398 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200399
Adrian Roos7b043112015-07-10 13:00:33 -0700400 int chargingId;
401 switch (mChargingSpeed) {
402 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
Adrian Roos7e39e592015-09-23 17:03:47 -0700403 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700404 ? R.string.keyguard_indication_charging_time_fast
Adrian Roos7e39e592015-09-23 17:03:47 -0700405 : R.string.keyguard_plugged_in_charging_fast;
Adrian Roos7b043112015-07-10 13:00:33 -0700406 break;
407 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
Adrian Roos7e39e592015-09-23 17:03:47 -0700408 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700409 ? R.string.keyguard_indication_charging_time_slowly
Adrian Roos7e39e592015-09-23 17:03:47 -0700410 : R.string.keyguard_plugged_in_charging_slowly;
Adrian Roos7b043112015-07-10 13:00:33 -0700411 break;
412 default:
Adrian Roos7e39e592015-09-23 17:03:47 -0700413 chargingId = hasChargingTime
414 ? R.string.keyguard_indication_charging_time
415 : R.string.keyguard_plugged_in;
Adrian Roos7b043112015-07-10 13:00:33 -0700416 break;
417 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700418
Lucas Dupin8d595d22018-03-08 10:34:58 -0800419 String percentage = NumberFormat.getPercentInstance()
420 .format(mBatteryLevel / 100f);
Adrian Roos7e39e592015-09-23 17:03:47 -0700421 if (hasChargingTime) {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800422 // We now have battery percentage in these strings and it's expected that all
423 // locales will also have it in the future. For now, we still have to support the old
424 // format until all languages get the new translations.
Adrian Roos7e39e592015-09-23 17:03:47 -0700425 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
426 mContext, chargingTimeRemaining);
Lucas Dupin8d595d22018-03-08 10:34:58 -0800427 try {
428 return mContext.getResources().getString(chargingId, chargingTimeFormatted,
429 percentage);
430 } catch (IllegalFormatConversionException e) {
431 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
432 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700433 } else {
Lucas Dupin8d595d22018-03-08 10:34:58 -0800434 // Same as above
435 try {
436 return mContext.getResources().getString(chargingId, percentage);
437 } catch (IllegalFormatConversionException e) {
438 return mContext.getResources().getString(chargingId);
439 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700440 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200441 }
442
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800443 public void setStatusBarKeyguardViewManager(
444 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
445 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
446 }
447
Adrian Roosaf45b602017-03-14 13:10:25 -0700448 private final BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800449 @Override
450 public void onReceive(Context context, Intent intent) {
451 mHandler.post(() -> {
452 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500453 updateIndication(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800454 }
455 });
456 }
457 };
458
459 private final Handler mHandler = new Handler() {
460 @Override
461 public void handleMessage(Message msg) {
Adrian Roosc1b50322017-02-27 21:07:58 +0100462 if (msg.what == MSG_HIDE_TRANSIENT) {
463 hideTransientIndication();
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800464 } else if (msg.what == MSG_CLEAR_FP_MSG) {
465 mLockIcon.setTransientFpError(false);
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800466 }
467 }
468 };
469
Adrian Roos91ba3072017-02-14 16:50:46 +0100470 public void setDozing(boolean dozing) {
Jorim Jaggifabc7432017-05-15 02:40:05 +0200471 if (mDozing == dozing) {
472 return;
473 }
Adrian Roos91ba3072017-02-14 16:50:46 +0100474 mDozing = dozing;
Beverly8c785892018-01-31 17:25:52 -0500475 updateIndication(false);
Adrian Roos91ba3072017-02-14 16:50:46 +0100476 updateDisclosure();
477 }
478
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800479 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
480 pw.println("KeyguardIndicationController:");
Jason Chang2386a372018-04-24 16:05:30 +0800481 pw.println(" mTransientTextColorState: " + mTransientTextColorState);
482 pw.println(" mInitialTextColorState: " + mInitialTextColorState);
Beverly2034c832018-03-19 11:18:51 -0400483 pw.println(" mPowerPluggedInWired: " + mPowerPluggedInWired);
Lucas Dupin3fcdd472018-01-19 19:06:45 -0800484 pw.println(" mPowerPluggedIn: " + mPowerPluggedIn);
485 pw.println(" mPowerCharged: " + mPowerCharged);
486 pw.println(" mChargingSpeed: " + mChargingSpeed);
487 pw.println(" mChargingWattage: " + mChargingWattage);
488 pw.println(" mMessageToShowOnScreenOn: " + mMessageToShowOnScreenOn);
489 pw.println(" mDozing: " + mDozing);
490 pw.println(" mBatteryLevel: " + mBatteryLevel);
491 pw.println(" mTextView.getText(): " + (mTextView == null ? null : mTextView.getText()));
492 pw.println(" computePowerIndication(): " + computePowerIndication());
493 }
494
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800495 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
Adrian Roos56021892017-02-27 20:25:09 +0100496 public static final int HIDE_DELAY_MS = 5000;
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800497 private int mLastSuccessiveErrorMessage = -1;
Selim Cinek3e451942016-07-14 18:07:53 -0700498
Adrian Roos12c1ef52014-06-04 13:54:08 +0200499 @Override
500 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100501 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200502 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roos56021892017-02-27 20:25:09 +0100503 boolean wasPluggedIn = mPowerPluggedIn;
Beverly2034c832018-03-19 11:18:51 -0400504 mPowerPluggedInWired = status.isPluggedInWired() && isChargingOrFull;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100505 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200506 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800507 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700508 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Lucas Dupin4272f442018-01-13 22:00:35 -0800509 mBatteryLevel = status.level;
Beverly2034c832018-03-19 11:18:51 -0400510 updateIndication(!wasPluggedIn && mPowerPluggedInWired);
Adrian Roosc1b50322017-02-27 21:07:58 +0100511 if (mDozing) {
512 if (!wasPluggedIn && mPowerPluggedIn) {
513 showTransientIndication(computePowerIndication());
514 hideTransientIndicationDelayed(HIDE_DELAY_MS);
515 } else if (wasPluggedIn && !mPowerPluggedIn) {
516 hideTransientIndication();
517 }
Adrian Roos56021892017-02-27 20:25:09 +0100518 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200519 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700520
521 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100522 public void onKeyguardVisibilityChanged(boolean showing) {
523 if (showing) {
524 updateDisclosure();
525 }
526 }
527
528 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700529 public void onFingerprintHelp(int msgId, String helpString) {
530 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
531 if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
532 return;
533 }
Jason Chang2386a372018-04-24 16:05:30 +0800534 ColorStateList errorColorState = Utils.getColorError(mContext);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700535 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Jason Chang2386a372018-04-24 16:05:30 +0800536 mStatusBarKeyguardViewManager.showBouncerMessage(helpString,
537 errorColorState.getDefaultColor());
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700538 } else if (updateMonitor.isScreenOn()) {
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700539 mLockIcon.setTransientFpError(true);
Jason Chang2386a372018-04-24 16:05:30 +0800540 showTransientIndication(helpString, errorColorState);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700541 hideTransientIndicationDelayed(TRANSIENT_FP_ERROR_TIMEOUT);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700542 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
543 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
544 TRANSIENT_FP_ERROR_TIMEOUT);
545 }
Selim Cinek3e451942016-07-14 18:07:53 -0700546 // Help messages indicate that there was actually a try since the last error, so those
547 // are not two successive error messages anymore.
548 mLastSuccessiveErrorMessage = -1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700549 }
550
551 @Override
552 public void onFingerprintError(int msgId, String errString) {
553 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700554 if ((!updateMonitor.isUnlockingWithFingerprintAllowed()
555 && msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700556 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
557 return;
558 }
Jason Chang2386a372018-04-24 16:05:30 +0800559 ColorStateList errorColorState = Utils.getColorError(mContext);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700560 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700561 // When swiping up right after receiving a fingerprint error, the bouncer calls
562 // authenticate leading to the same message being shown again on the bouncer.
563 // We want to avoid this, as it may confuse the user when the message is too
564 // generic.
565 if (mLastSuccessiveErrorMessage != msgId) {
Jason Chang2386a372018-04-24 16:05:30 +0800566 mStatusBarKeyguardViewManager.showBouncerMessage(errString,
567 errorColorState.getDefaultColor());
Selim Cinek3e451942016-07-14 18:07:53 -0700568 }
Kevin Chyn4c4001c2017-08-25 14:23:36 -0700569 } else if (updateMonitor.isScreenOn()) {
Jason Chang2386a372018-04-24 16:05:30 +0800570 showTransientIndication(errString, errorColorState);
Selim Cinek3e451942016-07-14 18:07:53 -0700571 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100572 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinek3e451942016-07-14 18:07:53 -0700573 } else {
574 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700575 }
Selim Cinek3e451942016-07-14 18:07:53 -0700576 mLastSuccessiveErrorMessage = msgId;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700577 }
578
579 @Override
Lucas Dupinef886542018-01-03 16:03:07 -0800580 public void onTrustAgentErrorMessage(CharSequence message) {
Jason Chang2386a372018-04-24 16:05:30 +0800581 showTransientIndication(message, Utils.getColorError(mContext));
Lucas Dupinef886542018-01-03 16:03:07 -0800582 }
583
584 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700585 public void onScreenTurnedOn() {
586 if (mMessageToShowOnScreenOn != null) {
Jason Chang2386a372018-04-24 16:05:30 +0800587 showTransientIndication(mMessageToShowOnScreenOn, Utils.getColorError(mContext));
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700588 // We want to keep this message around in case the screen was off
Adrian Roos56021892017-02-27 20:25:09 +0100589 hideTransientIndicationDelayed(HIDE_DELAY_MS);
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700590 mMessageToShowOnScreenOn = null;
591 }
592 }
593
594 @Override
595 public void onFingerprintRunningStateChanged(boolean running) {
596 if (running) {
597 mMessageToShowOnScreenOn = null;
598 }
599 }
Selim Cinek3e451942016-07-14 18:07:53 -0700600
601 @Override
602 public void onFingerprintAuthenticated(int userId) {
603 super.onFingerprintAuthenticated(userId);
604 mLastSuccessiveErrorMessage = -1;
605 }
606
607 @Override
608 public void onFingerprintAuthFailed() {
609 super.onFingerprintAuthFailed();
610 mLastSuccessiveErrorMessage = -1;
611 }
Jorim Jaggidadafd42016-09-30 07:20:25 -0700612
613 @Override
614 public void onUserUnlocked() {
615 if (mVisible) {
Beverly8c785892018-01-31 17:25:52 -0500616 updateIndication(false);
Jorim Jaggidadafd42016-09-30 07:20:25 -0700617 }
618 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200619 };
Adrian Roos12c1ef52014-06-04 13:54:08 +0200620}