blob: d599ec1a20625796501ee5e8f67ffe1e68004638 [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
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060019import android.app.ActivityManager;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010020import android.app.admin.DevicePolicyManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
Adrian Roos7b043112015-07-10 13:00:33 -070025import android.content.res.Resources;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070026import android.graphics.Color;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070027import android.hardware.fingerprint.FingerprintManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020028import android.os.BatteryManager;
29import android.os.BatteryStats;
30import android.os.Handler;
31import android.os.Message;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.UserHandle;
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
Selim Cinekcfafe4e2015-08-11 14:58:44 -070042import com.android.internal.app.IBatteryStats;
43import com.android.keyguard.KeyguardUpdateMonitor;
44import com.android.keyguard.KeyguardUpdateMonitorCallback;
Jason Monk9c7844c2017-01-18 15:21:53 -050045import com.android.systemui.Dependency;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070046import com.android.systemui.R;
47import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
48import com.android.systemui.statusbar.phone.LockIcon;
49import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
Zachary Iqbalf50284c2017-01-22 18:54:46 -080050import com.android.systemui.statusbar.policy.UserInfoController;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070051
Adrian Roos12c1ef52014-06-04 13:54:08 +020052/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070053 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020054 */
55public class KeyguardIndicationController {
56
Adrian Roos0c859ae2015-11-23 16:47:50 -080057 private static final String TAG = "KeyguardIndication";
58 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020059
60 private static final int MSG_HIDE_TRANSIENT = 1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070061 private static final int MSG_CLEAR_FP_MSG = 2;
62 private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020063
64 private final Context mContext;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010065 private final ViewGroup mIndicationArea;
Adrian Roos12c1ef52014-06-04 13:54:08 +020066 private final KeyguardIndicationTextView mTextView;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010067 private final KeyguardIndicationTextView mDisclosure;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060068 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020069 private final IBatteryStats mBatteryInfo;
70
Adrian Roos7b043112015-07-10 13:00:33 -070071 private final int mSlowThreshold;
72 private final int mFastThreshold;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070073 private final LockIcon mLockIcon;
74 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Adrian Roos7b043112015-07-10 13:00:33 -070075
Adrian Roos12c1ef52014-06-04 13:54:08 +020076 private String mRestingIndication;
77 private String mTransientIndication;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070078 private int mTransientTextColor;
Adrian Roos12c1ef52014-06-04 13:54:08 +020079 private boolean mVisible;
80
81 private boolean mPowerPluggedIn;
82 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -070083 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -080084 private int mChargingWattage;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070085 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +020086
Zachary Iqbalf50284c2017-01-22 18:54:46 -080087 private KeyguardUpdateMonitorCallback mUpdateMonitor;
88
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010089 private final DevicePolicyManager mDevicePolicyManager;
90
91 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
92 LockIcon lockIcon) {
Adrian Roos12c1ef52014-06-04 13:54:08 +020093 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010094 mIndicationArea = indicationArea;
95 mTextView = (KeyguardIndicationTextView) indicationArea.findViewById(
96 R.id.keyguard_indication_text);
97 mDisclosure = (KeyguardIndicationTextView) indicationArea.findViewById(
98 R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -070099 mLockIcon = lockIcon;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200100
Adrian Roos7b043112015-07-10 13:00:33 -0700101 Resources res = context.getResources();
102 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
103 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
104
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600105 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200106 mBatteryInfo = IBatteryStats.Stub.asInterface(
107 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600108
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100109 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
110 Context.DEVICE_POLICY_SERVICE);
111
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800112 KeyguardUpdateMonitor.getInstance(context).registerCallback(getKeyguardCallback());
Jeff Sharkey99e1bca2016-08-23 16:32:03 -0600113 context.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
Jason Monkcd26af72017-01-11 14:32:58 -0500114 new IntentFilter(Intent.ACTION_TIME_TICK), null,
Jason Monk9c7844c2017-01-18 15:21:53 -0500115 Dependency.get(Dependency.TIME_TICK_HANDLER));
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100116
117 updateDisclosure();
118 }
119
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800120 /**
121 * Gets the {@link KeyguardUpdateMonitorCallback} instance associated with this
122 * {@link KeyguardIndicationController}.
123 *
124 * <p>Subclasses may override this method to extend or change the callback behavior by extending
125 * the {@link BaseKeyguardCallback}.
126 *
127 * @return A KeyguardUpdateMonitorCallback. Multiple calls to this method <b>must</b> return the
128 * same instance.
129 */
130 protected KeyguardUpdateMonitorCallback getKeyguardCallback() {
131 if (mUpdateMonitor == null) {
132 mUpdateMonitor = new BaseKeyguardCallback();
133 }
134 return mUpdateMonitor;
135 }
136
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100137 private void updateDisclosure() {
138 if (mDevicePolicyManager == null) {
139 return;
140 }
141
142 if (mDevicePolicyManager.isDeviceManaged()) {
143 final CharSequence organizationName =
144 mDevicePolicyManager.getDeviceOwnerOrganizationName();
145 if (organizationName != null) {
146 mDisclosure.switchIndication(mContext.getResources().getString(
147 R.string.do_disclosure_with_name, organizationName));
148 } else {
149 mDisclosure.switchIndication(R.string.do_disclosure_generic);
150 }
151 mDisclosure.setVisibility(View.VISIBLE);
152 } else {
153 mDisclosure.setVisibility(View.GONE);
154 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200155 }
156
157 public void setVisible(boolean visible) {
158 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100159 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200160 if (visible) {
161 hideTransientIndication();
162 updateIndication();
163 }
164 }
165
166 /**
167 * Sets the indication that is shown if nothing else is showing.
168 */
169 public void setRestingIndication(String restingIndication) {
170 mRestingIndication = restingIndication;
171 updateIndication();
172 }
173
174 /**
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800175 * Sets the active controller managing changes and callbacks to user information.
176 */
177 public void setUserInfoController(UserInfoController userInfoController) {
178 }
179
180 /**
Adrian Roos12c1ef52014-06-04 13:54:08 +0200181 * Hides transient indication in {@param delayMs}.
182 */
183 public void hideTransientIndicationDelayed(long delayMs) {
184 mHandler.sendMessageDelayed(
185 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
186 }
187
188 /**
189 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
190 */
191 public void showTransientIndication(int transientIndication) {
192 showTransientIndication(mContext.getResources().getString(transientIndication));
193 }
194
195 /**
196 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
197 */
198 public void showTransientIndication(String transientIndication) {
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700199 showTransientIndication(transientIndication, Color.WHITE);
200 }
201
202 /**
203 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
204 */
205 public void showTransientIndication(String transientIndication, int textColor) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200206 mTransientIndication = transientIndication;
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700207 mTransientTextColor = textColor;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200208 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
209 updateIndication();
210 }
211
212 /**
213 * Hides transient indication.
214 */
215 public void hideTransientIndication() {
216 if (mTransientIndication != null) {
217 mTransientIndication = null;
218 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
219 updateIndication();
220 }
221 }
222
223 private void updateIndication() {
224 if (mVisible) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600225 // Walk down a precedence-ordered list of what should indication
226 // should be shown based on user or device state
227 if (!mUserManager.isUserUnlocked(ActivityManager.getCurrentUser())) {
228 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
229 mTextView.setTextColor(Color.WHITE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200230
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600231 } else if (!TextUtils.isEmpty(mTransientIndication)) {
232 mTextView.switchIndication(mTransientIndication);
233 mTextView.setTextColor(mTransientTextColor);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700234
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600235 } else if (mPowerPluggedIn) {
236 String indication = computePowerIndication();
237 if (DEBUG_CHARGING_SPEED) {
238 indication += ", " + (mChargingWattage / 1000) + " mW";
239 }
240 mTextView.switchIndication(indication);
241 mTextView.setTextColor(Color.WHITE);
242
243 } else {
244 mTextView.switchIndication(mRestingIndication);
245 mTextView.setTextColor(Color.WHITE);
Adrian Roos7b043112015-07-10 13:00:33 -0700246 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200247 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200248 }
249
250 private String computePowerIndication() {
251 if (mPowerCharged) {
252 return mContext.getResources().getString(R.string.keyguard_charged);
253 }
254
255 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700256 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200257 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700258 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
259
Adrian Roos12c1ef52014-06-04 13:54:08 +0200260 } catch (RemoteException e) {
261 Log.e(TAG, "Error calling IBatteryStats: ", e);
262 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700263 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200264
Adrian Roos7b043112015-07-10 13:00:33 -0700265 int chargingId;
266 switch (mChargingSpeed) {
267 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
Adrian Roos7e39e592015-09-23 17:03:47 -0700268 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700269 ? R.string.keyguard_indication_charging_time_fast
Adrian Roos7e39e592015-09-23 17:03:47 -0700270 : R.string.keyguard_plugged_in_charging_fast;
Adrian Roos7b043112015-07-10 13:00:33 -0700271 break;
272 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
Adrian Roos7e39e592015-09-23 17:03:47 -0700273 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700274 ? R.string.keyguard_indication_charging_time_slowly
Adrian Roos7e39e592015-09-23 17:03:47 -0700275 : R.string.keyguard_plugged_in_charging_slowly;
Adrian Roos7b043112015-07-10 13:00:33 -0700276 break;
277 default:
Adrian Roos7e39e592015-09-23 17:03:47 -0700278 chargingId = hasChargingTime
279 ? R.string.keyguard_indication_charging_time
280 : R.string.keyguard_plugged_in;
Adrian Roos7b043112015-07-10 13:00:33 -0700281 break;
282 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700283
284 if (hasChargingTime) {
285 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
286 mContext, chargingTimeRemaining);
287 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
288 } else {
289 return mContext.getResources().getString(chargingId);
290 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200291 }
292
Zachary Iqbalf50284c2017-01-22 18:54:46 -0800293 public void setStatusBarKeyguardViewManager(
294 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
295 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
296 }
297
298 BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
299 @Override
300 public void onReceive(Context context, Intent intent) {
301 mHandler.post(() -> {
302 if (mVisible) {
303 updateIndication();
304 }
305 });
306 }
307 };
308
309 private final Handler mHandler = new Handler() {
310 @Override
311 public void handleMessage(Message msg) {
312 if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
313 mTransientIndication = null;
314 updateIndication();
315 } else if (msg.what == MSG_CLEAR_FP_MSG) {
316 mLockIcon.setTransientFpError(false);
317 hideTransientIndication();
318 }
319 }
320 };
321
322 protected class BaseKeyguardCallback extends KeyguardUpdateMonitorCallback {
323 private int mLastSuccessiveErrorMessage = -1;
Selim Cinek3e451942016-07-14 18:07:53 -0700324
Adrian Roos12c1ef52014-06-04 13:54:08 +0200325 @Override
326 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100327 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200328 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100329 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200330 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800331 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700332 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200333 updateIndication();
334 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700335
336 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100337 public void onKeyguardVisibilityChanged(boolean showing) {
338 if (showing) {
339 updateDisclosure();
340 }
341 }
342
343 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700344 public void onFingerprintHelp(int msgId, String helpString) {
345 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
346 if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
347 return;
348 }
349 int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
350 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
351 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
352 } else if (updateMonitor.isDeviceInteractive()) {
353 mLockIcon.setTransientFpError(true);
354 showTransientIndication(helpString, errorColor);
355 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
356 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
357 TRANSIENT_FP_ERROR_TIMEOUT);
358 }
Selim Cinek3e451942016-07-14 18:07:53 -0700359 // Help messages indicate that there was actually a try since the last error, so those
360 // are not two successive error messages anymore.
361 mLastSuccessiveErrorMessage = -1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700362 }
363
364 @Override
365 public void onFingerprintError(int msgId, String errString) {
366 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
367 if (!updateMonitor.isUnlockingWithFingerprintAllowed()
368 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
369 return;
370 }
371 int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
372 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700373 // When swiping up right after receiving a fingerprint error, the bouncer calls
374 // authenticate leading to the same message being shown again on the bouncer.
375 // We want to avoid this, as it may confuse the user when the message is too
376 // generic.
377 if (mLastSuccessiveErrorMessage != msgId) {
378 mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
379 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700380 } else if (updateMonitor.isDeviceInteractive()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700381 showTransientIndication(errString, errorColor);
382 // We want to keep this message around in case the screen was off
383 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
384 hideTransientIndicationDelayed(5000);
385 } else {
386 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700387 }
Selim Cinek3e451942016-07-14 18:07:53 -0700388 mLastSuccessiveErrorMessage = msgId;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700389 }
390
391 @Override
392 public void onScreenTurnedOn() {
393 if (mMessageToShowOnScreenOn != null) {
394 int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
395 null);
396 showTransientIndication(mMessageToShowOnScreenOn, errorColor);
397 // We want to keep this message around in case the screen was off
398 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
399 hideTransientIndicationDelayed(5000);
400 mMessageToShowOnScreenOn = null;
401 }
402 }
403
404 @Override
405 public void onFingerprintRunningStateChanged(boolean running) {
406 if (running) {
407 mMessageToShowOnScreenOn = null;
408 }
409 }
Selim Cinek3e451942016-07-14 18:07:53 -0700410
411 @Override
412 public void onFingerprintAuthenticated(int userId) {
413 super.onFingerprintAuthenticated(userId);
414 mLastSuccessiveErrorMessage = -1;
415 }
416
417 @Override
418 public void onFingerprintAuthFailed() {
419 super.onFingerprintAuthFailed();
420 mLastSuccessiveErrorMessage = -1;
421 }
Jorim Jaggidadafd42016-09-30 07:20:25 -0700422
423 @Override
424 public void onUserUnlocked() {
425 if (mVisible) {
426 updateIndication();
427 }
428 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200429 };
Adrian Roos12c1ef52014-06-04 13:54:08 +0200430}