blob: 08fd93da7924859d973e1a34466b0c8ac8398359 [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;
50
Adrian Roos12c1ef52014-06-04 13:54:08 +020051/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070052 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020053 */
54public class KeyguardIndicationController {
55
Adrian Roos0c859ae2015-11-23 16:47:50 -080056 private static final String TAG = "KeyguardIndication";
57 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020058
59 private static final int MSG_HIDE_TRANSIENT = 1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070060 private static final int MSG_CLEAR_FP_MSG = 2;
61 private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020062
63 private final Context mContext;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010064 private final ViewGroup mIndicationArea;
Adrian Roos12c1ef52014-06-04 13:54:08 +020065 private final KeyguardIndicationTextView mTextView;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010066 private final KeyguardIndicationTextView mDisclosure;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060067 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020068 private final IBatteryStats mBatteryInfo;
69
Adrian Roos7b043112015-07-10 13:00:33 -070070 private final int mSlowThreshold;
71 private final int mFastThreshold;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070072 private final LockIcon mLockIcon;
73 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Adrian Roos7b043112015-07-10 13:00:33 -070074
Adrian Roos12c1ef52014-06-04 13:54:08 +020075 private String mRestingIndication;
76 private String mTransientIndication;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070077 private int mTransientTextColor;
Adrian Roos12c1ef52014-06-04 13:54:08 +020078 private boolean mVisible;
79
80 private boolean mPowerPluggedIn;
81 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -070082 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -080083 private int mChargingWattage;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070084 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +020085
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010086 private final DevicePolicyManager mDevicePolicyManager;
87
88 public KeyguardIndicationController(Context context, ViewGroup indicationArea,
89 LockIcon lockIcon) {
Adrian Roos12c1ef52014-06-04 13:54:08 +020090 mContext = context;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +010091 mIndicationArea = indicationArea;
92 mTextView = (KeyguardIndicationTextView) indicationArea.findViewById(
93 R.id.keyguard_indication_text);
94 mDisclosure = (KeyguardIndicationTextView) indicationArea.findViewById(
95 R.id.keyguard_indication_enterprise_disclosure);
Selim Cinekcfafe4e2015-08-11 14:58:44 -070096 mLockIcon = lockIcon;
Adrian Roos12c1ef52014-06-04 13:54:08 +020097
Adrian Roos7b043112015-07-10 13:00:33 -070098 Resources res = context.getResources();
99 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
100 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
101
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600102 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200103 mBatteryInfo = IBatteryStats.Stub.asInterface(
104 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600105
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100106 mDevicePolicyManager = (DevicePolicyManager) context.getSystemService(
107 Context.DEVICE_POLICY_SERVICE);
108
Adrian Roos12c1ef52014-06-04 13:54:08 +0200109 KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
Jeff Sharkey99e1bca2016-08-23 16:32:03 -0600110 context.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
Jason Monkcd26af72017-01-11 14:32:58 -0500111 new IntentFilter(Intent.ACTION_TIME_TICK), null,
Jason Monk9c7844c2017-01-18 15:21:53 -0500112 Dependency.get(Dependency.TIME_TICK_HANDLER));
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100113
114 updateDisclosure();
115 }
116
117 private void updateDisclosure() {
118 if (mDevicePolicyManager == null) {
119 return;
120 }
121
122 if (mDevicePolicyManager.isDeviceManaged()) {
123 final CharSequence organizationName =
124 mDevicePolicyManager.getDeviceOwnerOrganizationName();
125 if (organizationName != null) {
126 mDisclosure.switchIndication(mContext.getResources().getString(
127 R.string.do_disclosure_with_name, organizationName));
128 } else {
129 mDisclosure.switchIndication(R.string.do_disclosure_generic);
130 }
131 mDisclosure.setVisibility(View.VISIBLE);
132 } else {
133 mDisclosure.setVisibility(View.GONE);
134 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200135 }
136
137 public void setVisible(boolean visible) {
138 mVisible = visible;
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100139 mIndicationArea.setVisibility(visible ? View.VISIBLE : View.GONE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200140 if (visible) {
141 hideTransientIndication();
142 updateIndication();
143 }
144 }
145
146 /**
147 * Sets the indication that is shown if nothing else is showing.
148 */
149 public void setRestingIndication(String restingIndication) {
150 mRestingIndication = restingIndication;
151 updateIndication();
152 }
153
154 /**
155 * Hides transient indication in {@param delayMs}.
156 */
157 public void hideTransientIndicationDelayed(long delayMs) {
158 mHandler.sendMessageDelayed(
159 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
160 }
161
162 /**
163 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
164 */
165 public void showTransientIndication(int transientIndication) {
166 showTransientIndication(mContext.getResources().getString(transientIndication));
167 }
168
169 /**
170 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
171 */
172 public void showTransientIndication(String transientIndication) {
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700173 showTransientIndication(transientIndication, Color.WHITE);
174 }
175
176 /**
177 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
178 */
179 public void showTransientIndication(String transientIndication, int textColor) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200180 mTransientIndication = transientIndication;
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700181 mTransientTextColor = textColor;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200182 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
183 updateIndication();
184 }
185
186 /**
187 * Hides transient indication.
188 */
189 public void hideTransientIndication() {
190 if (mTransientIndication != null) {
191 mTransientIndication = null;
192 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
193 updateIndication();
194 }
195 }
196
197 private void updateIndication() {
198 if (mVisible) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600199 // Walk down a precedence-ordered list of what should indication
200 // should be shown based on user or device state
201 if (!mUserManager.isUserUnlocked(ActivityManager.getCurrentUser())) {
202 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
203 mTextView.setTextColor(Color.WHITE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200204
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600205 } else if (!TextUtils.isEmpty(mTransientIndication)) {
206 mTextView.switchIndication(mTransientIndication);
207 mTextView.setTextColor(mTransientTextColor);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700208
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600209 } else if (mPowerPluggedIn) {
210 String indication = computePowerIndication();
211 if (DEBUG_CHARGING_SPEED) {
212 indication += ", " + (mChargingWattage / 1000) + " mW";
213 }
214 mTextView.switchIndication(indication);
215 mTextView.setTextColor(Color.WHITE);
216
217 } else {
218 mTextView.switchIndication(mRestingIndication);
219 mTextView.setTextColor(Color.WHITE);
Adrian Roos7b043112015-07-10 13:00:33 -0700220 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200221 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200222 }
223
224 private String computePowerIndication() {
225 if (mPowerCharged) {
226 return mContext.getResources().getString(R.string.keyguard_charged);
227 }
228
229 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700230 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200231 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700232 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
233
Adrian Roos12c1ef52014-06-04 13:54:08 +0200234 } catch (RemoteException e) {
235 Log.e(TAG, "Error calling IBatteryStats: ", e);
236 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700237 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200238
Adrian Roos7b043112015-07-10 13:00:33 -0700239 int chargingId;
240 switch (mChargingSpeed) {
241 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
Adrian Roos7e39e592015-09-23 17:03:47 -0700242 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700243 ? R.string.keyguard_indication_charging_time_fast
Adrian Roos7e39e592015-09-23 17:03:47 -0700244 : R.string.keyguard_plugged_in_charging_fast;
Adrian Roos7b043112015-07-10 13:00:33 -0700245 break;
246 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
Adrian Roos7e39e592015-09-23 17:03:47 -0700247 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700248 ? R.string.keyguard_indication_charging_time_slowly
Adrian Roos7e39e592015-09-23 17:03:47 -0700249 : R.string.keyguard_plugged_in_charging_slowly;
Adrian Roos7b043112015-07-10 13:00:33 -0700250 break;
251 default:
Adrian Roos7e39e592015-09-23 17:03:47 -0700252 chargingId = hasChargingTime
253 ? R.string.keyguard_indication_charging_time
254 : R.string.keyguard_plugged_in;
Adrian Roos7b043112015-07-10 13:00:33 -0700255 break;
256 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700257
258 if (hasChargingTime) {
259 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
260 mContext, chargingTimeRemaining);
261 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
262 } else {
263 return mContext.getResources().getString(chargingId);
264 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200265 }
266
267 KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
Selim Cinek3e451942016-07-14 18:07:53 -0700268 public int mLastSuccessiveErrorMessage = -1;
269
Adrian Roos12c1ef52014-06-04 13:54:08 +0200270 @Override
271 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100272 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200273 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100274 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200275 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800276 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700277 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200278 updateIndication();
279 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700280
281 @Override
Bartosz Fabianowski5f045002016-12-01 10:36:18 +0100282 public void onKeyguardVisibilityChanged(boolean showing) {
283 if (showing) {
284 updateDisclosure();
285 }
286 }
287
288 @Override
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700289 public void onFingerprintHelp(int msgId, String helpString) {
290 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
291 if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
292 return;
293 }
294 int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
295 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
296 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
297 } else if (updateMonitor.isDeviceInteractive()) {
298 mLockIcon.setTransientFpError(true);
299 showTransientIndication(helpString, errorColor);
300 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
301 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
302 TRANSIENT_FP_ERROR_TIMEOUT);
303 }
Selim Cinek3e451942016-07-14 18:07:53 -0700304 // Help messages indicate that there was actually a try since the last error, so those
305 // are not two successive error messages anymore.
306 mLastSuccessiveErrorMessage = -1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700307 }
308
309 @Override
310 public void onFingerprintError(int msgId, String errString) {
311 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
312 if (!updateMonitor.isUnlockingWithFingerprintAllowed()
313 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
314 return;
315 }
316 int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
317 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700318 // When swiping up right after receiving a fingerprint error, the bouncer calls
319 // authenticate leading to the same message being shown again on the bouncer.
320 // We want to avoid this, as it may confuse the user when the message is too
321 // generic.
322 if (mLastSuccessiveErrorMessage != msgId) {
323 mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
324 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700325 } else if (updateMonitor.isDeviceInteractive()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700326 showTransientIndication(errString, errorColor);
327 // We want to keep this message around in case the screen was off
328 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
329 hideTransientIndicationDelayed(5000);
330 } else {
331 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700332 }
Selim Cinek3e451942016-07-14 18:07:53 -0700333 mLastSuccessiveErrorMessage = msgId;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700334 }
335
336 @Override
337 public void onScreenTurnedOn() {
338 if (mMessageToShowOnScreenOn != null) {
339 int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
340 null);
341 showTransientIndication(mMessageToShowOnScreenOn, errorColor);
342 // We want to keep this message around in case the screen was off
343 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
344 hideTransientIndicationDelayed(5000);
345 mMessageToShowOnScreenOn = null;
346 }
347 }
348
349 @Override
350 public void onFingerprintRunningStateChanged(boolean running) {
351 if (running) {
352 mMessageToShowOnScreenOn = null;
353 }
354 }
Selim Cinek3e451942016-07-14 18:07:53 -0700355
356 @Override
357 public void onFingerprintAuthenticated(int userId) {
358 super.onFingerprintAuthenticated(userId);
359 mLastSuccessiveErrorMessage = -1;
360 }
361
362 @Override
363 public void onFingerprintAuthFailed() {
364 super.onFingerprintAuthFailed();
365 mLastSuccessiveErrorMessage = -1;
366 }
Jorim Jaggidadafd42016-09-30 07:20:25 -0700367
368 @Override
369 public void onUserUnlocked() {
370 if (mVisible) {
371 updateIndication();
372 }
373 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200374 };
375
Jeff Sharkey99e1bca2016-08-23 16:32:03 -0600376 BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
377 @Override
378 public void onReceive(Context context, Intent intent) {
Jason Monkcd26af72017-01-11 14:32:58 -0500379 mHandler.post(() -> {
380 if (mVisible) {
381 updateIndication();
382 }
383 });
Jeff Sharkey99e1bca2016-08-23 16:32:03 -0600384 }
385 };
386
Adrian Roos12c1ef52014-06-04 13:54:08 +0200387
388 private final Handler mHandler = new Handler() {
389 @Override
390 public void handleMessage(Message msg) {
391 if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
392 mTransientIndication = null;
393 updateIndication();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700394 } else if (msg.what == MSG_CLEAR_FP_MSG) {
395 mLockIcon.setTransientFpError(false);
396 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200397 }
398 }
399 };
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700400
401 public void setStatusBarKeyguardViewManager(
402 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
403 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
404 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200405}