blob: 0ef9715259c247c681c2d6d259f4db57a0ac5cbd [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;
Adrian Roos12c1ef52014-06-04 13:54:08 +020020import android.content.BroadcastReceiver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
Adrian Roos7b043112015-07-10 13:00:33 -070024import android.content.res.Resources;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070025import android.graphics.Color;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070026import android.hardware.fingerprint.FingerprintManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020027import android.os.BatteryManager;
28import android.os.BatteryStats;
29import android.os.Handler;
30import android.os.Message;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.os.UserHandle;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060034import android.os.UserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020035import android.text.TextUtils;
36import android.text.format.Formatter;
37import android.util.Log;
38import android.view.View;
39
Selim Cinekcfafe4e2015-08-11 14:58:44 -070040import com.android.internal.app.IBatteryStats;
41import com.android.keyguard.KeyguardUpdateMonitor;
42import com.android.keyguard.KeyguardUpdateMonitorCallback;
43import com.android.systemui.R;
44import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
45import com.android.systemui.statusbar.phone.LockIcon;
46import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
47
Adrian Roos12c1ef52014-06-04 13:54:08 +020048/**
Selim Cinekcfafe4e2015-08-11 14:58:44 -070049 * Controls the indications and error messages shown on the Keyguard
Adrian Roos12c1ef52014-06-04 13:54:08 +020050 */
51public class KeyguardIndicationController {
52
Adrian Roos0c859ae2015-11-23 16:47:50 -080053 private static final String TAG = "KeyguardIndication";
54 private static final boolean DEBUG_CHARGING_SPEED = false;
Adrian Roos12c1ef52014-06-04 13:54:08 +020055
56 private static final int MSG_HIDE_TRANSIENT = 1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070057 private static final int MSG_CLEAR_FP_MSG = 2;
58 private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
Adrian Roos12c1ef52014-06-04 13:54:08 +020059
60 private final Context mContext;
61 private final KeyguardIndicationTextView mTextView;
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060062 private final UserManager mUserManager;
Adrian Roos12c1ef52014-06-04 13:54:08 +020063 private final IBatteryStats mBatteryInfo;
64
Adrian Roos7b043112015-07-10 13:00:33 -070065 private final int mSlowThreshold;
66 private final int mFastThreshold;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070067 private final LockIcon mLockIcon;
68 private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
Adrian Roos7b043112015-07-10 13:00:33 -070069
Adrian Roos12c1ef52014-06-04 13:54:08 +020070 private String mRestingIndication;
71 private String mTransientIndication;
Jorim Jaggi27c9b742015-04-09 10:34:49 -070072 private int mTransientTextColor;
Adrian Roos12c1ef52014-06-04 13:54:08 +020073 private boolean mVisible;
74
75 private boolean mPowerPluggedIn;
76 private boolean mPowerCharged;
Adrian Roos7b043112015-07-10 13:00:33 -070077 private int mChargingSpeed;
Adrian Roos0c859ae2015-11-23 16:47:50 -080078 private int mChargingWattage;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070079 private String mMessageToShowOnScreenOn;
Adrian Roos12c1ef52014-06-04 13:54:08 +020080
Selim Cinekcfafe4e2015-08-11 14:58:44 -070081 public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
82 LockIcon lockIcon) {
Adrian Roos12c1ef52014-06-04 13:54:08 +020083 mContext = context;
84 mTextView = textView;
Selim Cinekcfafe4e2015-08-11 14:58:44 -070085 mLockIcon = lockIcon;
Adrian Roos12c1ef52014-06-04 13:54:08 +020086
Adrian Roos7b043112015-07-10 13:00:33 -070087 Resources res = context.getResources();
88 mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
89 mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
90
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060091 mUserManager = context.getSystemService(UserManager.class);
Adrian Roos12c1ef52014-06-04 13:54:08 +020092 mBatteryInfo = IBatteryStats.Stub.asInterface(
93 ServiceManager.getService(BatteryStats.SERVICE_NAME));
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -060094
Adrian Roos12c1ef52014-06-04 13:54:08 +020095 KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
Jeff Sharkey99e1bca2016-08-23 16:32:03 -060096 context.registerReceiverAsUser(mTickReceiver, UserHandle.SYSTEM,
Xiaohui Chen87d0e252015-07-30 15:38:16 -070097 new IntentFilter(Intent.ACTION_TIME_TICK), null, null);
Adrian Roos12c1ef52014-06-04 13:54:08 +020098 }
99
100 public void setVisible(boolean visible) {
101 mVisible = visible;
102 mTextView.setVisibility(visible ? View.VISIBLE : View.GONE);
103 if (visible) {
104 hideTransientIndication();
105 updateIndication();
106 }
107 }
108
109 /**
110 * Sets the indication that is shown if nothing else is showing.
111 */
112 public void setRestingIndication(String restingIndication) {
113 mRestingIndication = restingIndication;
114 updateIndication();
115 }
116
117 /**
118 * Hides transient indication in {@param delayMs}.
119 */
120 public void hideTransientIndicationDelayed(long delayMs) {
121 mHandler.sendMessageDelayed(
122 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
123 }
124
125 /**
126 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
127 */
128 public void showTransientIndication(int transientIndication) {
129 showTransientIndication(mContext.getResources().getString(transientIndication));
130 }
131
132 /**
133 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
134 */
135 public void showTransientIndication(String transientIndication) {
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700136 showTransientIndication(transientIndication, Color.WHITE);
137 }
138
139 /**
140 * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
141 */
142 public void showTransientIndication(String transientIndication, int textColor) {
Adrian Roos12c1ef52014-06-04 13:54:08 +0200143 mTransientIndication = transientIndication;
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700144 mTransientTextColor = textColor;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200145 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
146 updateIndication();
147 }
148
149 /**
150 * Hides transient indication.
151 */
152 public void hideTransientIndication() {
153 if (mTransientIndication != null) {
154 mTransientIndication = null;
155 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
156 updateIndication();
157 }
158 }
159
160 private void updateIndication() {
161 if (mVisible) {
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600162 // Walk down a precedence-ordered list of what should indication
163 // should be shown based on user or device state
164 if (!mUserManager.isUserUnlocked(ActivityManager.getCurrentUser())) {
165 mTextView.switchIndication(com.android.internal.R.string.lockscreen_storage_locked);
166 mTextView.setTextColor(Color.WHITE);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200167
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600168 } else if (!TextUtils.isEmpty(mTransientIndication)) {
169 mTextView.switchIndication(mTransientIndication);
170 mTextView.setTextColor(mTransientTextColor);
Jorim Jaggi27c9b742015-04-09 10:34:49 -0700171
Jeff Sharkeyb6edaa92016-07-27 15:51:31 -0600172 } else if (mPowerPluggedIn) {
173 String indication = computePowerIndication();
174 if (DEBUG_CHARGING_SPEED) {
175 indication += ", " + (mChargingWattage / 1000) + " mW";
176 }
177 mTextView.switchIndication(indication);
178 mTextView.setTextColor(Color.WHITE);
179
180 } else {
181 mTextView.switchIndication(mRestingIndication);
182 mTextView.setTextColor(Color.WHITE);
Adrian Roos7b043112015-07-10 13:00:33 -0700183 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200184 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200185 }
186
187 private String computePowerIndication() {
188 if (mPowerCharged) {
189 return mContext.getResources().getString(R.string.keyguard_charged);
190 }
191
192 // Try fetching charging time from battery stats.
Adrian Roos7e39e592015-09-23 17:03:47 -0700193 long chargingTimeRemaining = 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200194 try {
Adrian Roos7e39e592015-09-23 17:03:47 -0700195 chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
196
Adrian Roos12c1ef52014-06-04 13:54:08 +0200197 } catch (RemoteException e) {
198 Log.e(TAG, "Error calling IBatteryStats: ", e);
199 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700200 final boolean hasChargingTime = chargingTimeRemaining > 0;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200201
Adrian Roos7b043112015-07-10 13:00:33 -0700202 int chargingId;
203 switch (mChargingSpeed) {
204 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
Adrian Roos7e39e592015-09-23 17:03:47 -0700205 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700206 ? R.string.keyguard_indication_charging_time_fast
Adrian Roos7e39e592015-09-23 17:03:47 -0700207 : R.string.keyguard_plugged_in_charging_fast;
Adrian Roos7b043112015-07-10 13:00:33 -0700208 break;
209 case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
Adrian Roos7e39e592015-09-23 17:03:47 -0700210 chargingId = hasChargingTime
Adrian Roosf142cac2015-09-25 15:15:17 -0700211 ? R.string.keyguard_indication_charging_time_slowly
Adrian Roos7e39e592015-09-23 17:03:47 -0700212 : R.string.keyguard_plugged_in_charging_slowly;
Adrian Roos7b043112015-07-10 13:00:33 -0700213 break;
214 default:
Adrian Roos7e39e592015-09-23 17:03:47 -0700215 chargingId = hasChargingTime
216 ? R.string.keyguard_indication_charging_time
217 : R.string.keyguard_plugged_in;
Adrian Roos7b043112015-07-10 13:00:33 -0700218 break;
219 }
Adrian Roos7e39e592015-09-23 17:03:47 -0700220
221 if (hasChargingTime) {
222 String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
223 mContext, chargingTimeRemaining);
224 return mContext.getResources().getString(chargingId, chargingTimeFormatted);
225 } else {
226 return mContext.getResources().getString(chargingId);
227 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200228 }
229
230 KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
Selim Cinek3e451942016-07-14 18:07:53 -0700231 public int mLastSuccessiveErrorMessage = -1;
232
Adrian Roos12c1ef52014-06-04 13:54:08 +0200233 @Override
234 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100235 boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
Adrian Roos12c1ef52014-06-04 13:54:08 +0200236 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Adrian Roosad3bc7f2014-10-30 18:29:38 +0100237 mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
Adrian Roos12c1ef52014-06-04 13:54:08 +0200238 mPowerCharged = status.isCharged();
Adrian Roos0c859ae2015-11-23 16:47:50 -0800239 mChargingWattage = status.maxChargingWattage;
Adrian Roos7b043112015-07-10 13:00:33 -0700240 mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
Adrian Roos12c1ef52014-06-04 13:54:08 +0200241 updateIndication();
242 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700243
244 @Override
245 public void onFingerprintHelp(int msgId, String helpString) {
246 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
247 if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
248 return;
249 }
250 int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
251 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
252 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
253 } else if (updateMonitor.isDeviceInteractive()) {
254 mLockIcon.setTransientFpError(true);
255 showTransientIndication(helpString, errorColor);
256 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
257 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
258 TRANSIENT_FP_ERROR_TIMEOUT);
259 }
Selim Cinek3e451942016-07-14 18:07:53 -0700260 // Help messages indicate that there was actually a try since the last error, so those
261 // are not two successive error messages anymore.
262 mLastSuccessiveErrorMessage = -1;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700263 }
264
265 @Override
266 public void onFingerprintError(int msgId, String errString) {
267 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
268 if (!updateMonitor.isUnlockingWithFingerprintAllowed()
269 || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
270 return;
271 }
272 int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
273 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700274 // When swiping up right after receiving a fingerprint error, the bouncer calls
275 // authenticate leading to the same message being shown again on the bouncer.
276 // We want to avoid this, as it may confuse the user when the message is too
277 // generic.
278 if (mLastSuccessiveErrorMessage != msgId) {
279 mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
280 }
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700281 } else if (updateMonitor.isDeviceInteractive()) {
Selim Cinek3e451942016-07-14 18:07:53 -0700282 showTransientIndication(errString, errorColor);
283 // We want to keep this message around in case the screen was off
284 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
285 hideTransientIndicationDelayed(5000);
286 } else {
287 mMessageToShowOnScreenOn = errString;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700288 }
Selim Cinek3e451942016-07-14 18:07:53 -0700289 mLastSuccessiveErrorMessage = msgId;
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700290 }
291
292 @Override
293 public void onScreenTurnedOn() {
294 if (mMessageToShowOnScreenOn != null) {
295 int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
296 null);
297 showTransientIndication(mMessageToShowOnScreenOn, errorColor);
298 // We want to keep this message around in case the screen was off
299 mHandler.removeMessages(MSG_HIDE_TRANSIENT);
300 hideTransientIndicationDelayed(5000);
301 mMessageToShowOnScreenOn = null;
302 }
303 }
304
305 @Override
306 public void onFingerprintRunningStateChanged(boolean running) {
307 if (running) {
308 mMessageToShowOnScreenOn = null;
309 }
310 }
Selim Cinek3e451942016-07-14 18:07:53 -0700311
312 @Override
313 public void onFingerprintAuthenticated(int userId) {
314 super.onFingerprintAuthenticated(userId);
315 mLastSuccessiveErrorMessage = -1;
316 }
317
318 @Override
319 public void onFingerprintAuthFailed() {
320 super.onFingerprintAuthFailed();
321 mLastSuccessiveErrorMessage = -1;
322 }
Jorim Jaggidadafd42016-09-30 07:20:25 -0700323
324 @Override
325 public void onUserUnlocked() {
326 if (mVisible) {
327 updateIndication();
328 }
329 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200330 };
331
Jeff Sharkey99e1bca2016-08-23 16:32:03 -0600332 BroadcastReceiver mTickReceiver = new BroadcastReceiver() {
333 @Override
334 public void onReceive(Context context, Intent intent) {
335 if (mVisible) {
336 updateIndication();
337 }
338 }
339 };
340
Adrian Roos12c1ef52014-06-04 13:54:08 +0200341
342 private final Handler mHandler = new Handler() {
343 @Override
344 public void handleMessage(Message msg) {
345 if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
346 mTransientIndication = null;
347 updateIndication();
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700348 } else if (msg.what == MSG_CLEAR_FP_MSG) {
349 mLockIcon.setTransientFpError(false);
350 hideTransientIndication();
Adrian Roos12c1ef52014-06-04 13:54:08 +0200351 }
352 }
353 };
Selim Cinekcfafe4e2015-08-11 14:58:44 -0700354
355 public void setStatusBarKeyguardViewManager(
356 StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
357 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
358 }
Adrian Roos12c1ef52014-06-04 13:54:08 +0200359}