• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.statusbar;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.res.Resources;
24 import android.graphics.Color;
25 import android.hardware.fingerprint.FingerprintManager;
26 import android.os.BatteryManager;
27 import android.os.BatteryStats;
28 import android.os.Handler;
29 import android.os.Message;
30 import android.os.RemoteException;
31 import android.os.ServiceManager;
32 import android.os.UserHandle;
33 import android.text.TextUtils;
34 import android.text.format.Formatter;
35 import android.util.Log;
36 import android.view.View;
37 
38 import com.android.internal.app.IBatteryStats;
39 import com.android.keyguard.KeyguardUpdateMonitor;
40 import com.android.keyguard.KeyguardUpdateMonitorCallback;
41 import com.android.systemui.R;
42 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
43 import com.android.systemui.statusbar.phone.LockIcon;
44 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
45 
46 /**
47  * Controls the indications and error messages shown on the Keyguard
48  */
49 public class KeyguardIndicationController {
50 
51     private static final String TAG = "KeyguardIndication";
52     private static final boolean DEBUG_CHARGING_SPEED = false;
53 
54     private static final int MSG_HIDE_TRANSIENT = 1;
55     private static final int MSG_CLEAR_FP_MSG = 2;
56     private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
57 
58     private final Context mContext;
59     private final KeyguardIndicationTextView mTextView;
60     private final IBatteryStats mBatteryInfo;
61 
62     private final int mSlowThreshold;
63     private final int mFastThreshold;
64     private final LockIcon mLockIcon;
65     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
66 
67     private String mRestingIndication;
68     private String mTransientIndication;
69     private int mTransientTextColor;
70     private boolean mVisible;
71 
72     private boolean mPowerPluggedIn;
73     private boolean mPowerCharged;
74     private int mChargingSpeed;
75     private int mChargingWattage;
76     private String mMessageToShowOnScreenOn;
77 
KeyguardIndicationController(Context context, KeyguardIndicationTextView textView, LockIcon lockIcon)78     public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
79                                         LockIcon lockIcon) {
80         mContext = context;
81         mTextView = textView;
82         mLockIcon = lockIcon;
83 
84         Resources res = context.getResources();
85         mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
86         mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
87 
88 
89         mBatteryInfo = IBatteryStats.Stub.asInterface(
90                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
91         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
92         context.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM,
93                 new IntentFilter(Intent.ACTION_TIME_TICK), null, null);
94     }
95 
setVisible(boolean visible)96     public void setVisible(boolean visible) {
97         mVisible = visible;
98         mTextView.setVisibility(visible ? View.VISIBLE : View.GONE);
99         if (visible) {
100             hideTransientIndication();
101             updateIndication();
102         }
103     }
104 
105     /**
106      * Sets the indication that is shown if nothing else is showing.
107      */
setRestingIndication(String restingIndication)108     public void setRestingIndication(String restingIndication) {
109         mRestingIndication = restingIndication;
110         updateIndication();
111     }
112 
113     /**
114      * Hides transient indication in {@param delayMs}.
115      */
hideTransientIndicationDelayed(long delayMs)116     public void hideTransientIndicationDelayed(long delayMs) {
117         mHandler.sendMessageDelayed(
118                 mHandler.obtainMessage(MSG_HIDE_TRANSIENT), delayMs);
119     }
120 
121     /**
122      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
123      */
showTransientIndication(int transientIndication)124     public void showTransientIndication(int transientIndication) {
125         showTransientIndication(mContext.getResources().getString(transientIndication));
126     }
127 
128     /**
129      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
130      */
showTransientIndication(String transientIndication)131     public void showTransientIndication(String transientIndication) {
132         showTransientIndication(transientIndication, Color.WHITE);
133     }
134 
135     /**
136      * Shows {@param transientIndication} until it is hidden by {@link #hideTransientIndication}.
137      */
showTransientIndication(String transientIndication, int textColor)138     public void showTransientIndication(String transientIndication, int textColor) {
139         mTransientIndication = transientIndication;
140         mTransientTextColor = textColor;
141         mHandler.removeMessages(MSG_HIDE_TRANSIENT);
142         updateIndication();
143     }
144 
145     /**
146      * Hides transient indication.
147      */
hideTransientIndication()148     public void hideTransientIndication() {
149         if (mTransientIndication != null) {
150             mTransientIndication = null;
151             mHandler.removeMessages(MSG_HIDE_TRANSIENT);
152             updateIndication();
153         }
154     }
155 
updateIndication()156     private void updateIndication() {
157         if (mVisible) {
158             mTextView.switchIndication(computeIndication());
159             mTextView.setTextColor(computeColor());
160         }
161     }
162 
computeColor()163     private int computeColor() {
164         if (!TextUtils.isEmpty(mTransientIndication)) {
165             return mTransientTextColor;
166         }
167         return Color.WHITE;
168     }
169 
computeIndication()170     private String computeIndication() {
171         if (!TextUtils.isEmpty(mTransientIndication)) {
172             return mTransientIndication;
173         }
174         if (mPowerPluggedIn) {
175             String indication = computePowerIndication();
176             if (DEBUG_CHARGING_SPEED) {
177                 indication += ",  " + (mChargingWattage / 1000) + " mW";
178             }
179             return indication;
180         }
181         return mRestingIndication;
182     }
183 
computePowerIndication()184     private String computePowerIndication() {
185         if (mPowerCharged) {
186             return mContext.getResources().getString(R.string.keyguard_charged);
187         }
188 
189         // Try fetching charging time from battery stats.
190         long chargingTimeRemaining = 0;
191         try {
192             chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
193 
194         } catch (RemoteException e) {
195             Log.e(TAG, "Error calling IBatteryStats: ", e);
196         }
197         final boolean hasChargingTime = chargingTimeRemaining > 0;
198 
199         int chargingId;
200         switch (mChargingSpeed) {
201             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
202                 chargingId = hasChargingTime
203                         ? R.string.keyguard_indication_charging_time_fast
204                         : R.string.keyguard_plugged_in_charging_fast;
205                 break;
206             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
207                 chargingId = hasChargingTime
208                         ? R.string.keyguard_indication_charging_time_slowly
209                         : R.string.keyguard_plugged_in_charging_slowly;
210                 break;
211             default:
212                 chargingId = hasChargingTime
213                         ? R.string.keyguard_indication_charging_time
214                         : R.string.keyguard_plugged_in;
215                 break;
216         }
217 
218         if (hasChargingTime) {
219             String chargingTimeFormatted = Formatter.formatShortElapsedTimeRoundingUpToMinutes(
220                     mContext, chargingTimeRemaining);
221             return mContext.getResources().getString(chargingId, chargingTimeFormatted);
222         } else {
223             return mContext.getResources().getString(chargingId);
224         }
225     }
226 
227     KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
228         @Override
229         public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
230             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
231                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
232             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
233             mPowerCharged = status.isCharged();
234             mChargingWattage = status.maxChargingWattage;
235             mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
236             updateIndication();
237         }
238 
239         @Override
240         public void onFingerprintHelp(int msgId, String helpString) {
241             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
242             if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
243                 return;
244             }
245             int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
246             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
247                 mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
248             } else if (updateMonitor.isDeviceInteractive()) {
249                 mLockIcon.setTransientFpError(true);
250                 showTransientIndication(helpString, errorColor);
251                 mHandler.removeMessages(MSG_CLEAR_FP_MSG);
252                 mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
253                         TRANSIENT_FP_ERROR_TIMEOUT);
254             }
255         }
256 
257         @Override
258         public void onFingerprintError(int msgId, String errString) {
259             KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
260             if (!updateMonitor.isUnlockingWithFingerprintAllowed()
261                     || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
262                 return;
263             }
264             int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
265             if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
266                 mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
267             } else if (updateMonitor.isDeviceInteractive()) {
268                     showTransientIndication(errString, errorColor);
269                     // We want to keep this message around in case the screen was off
270                     mHandler.removeMessages(MSG_HIDE_TRANSIENT);
271                     hideTransientIndicationDelayed(5000);
272              } else {
273                     mMessageToShowOnScreenOn = errString;
274             }
275         }
276 
277         @Override
278         public void onScreenTurnedOn() {
279             if (mMessageToShowOnScreenOn != null) {
280                 int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
281                         null);
282                 showTransientIndication(mMessageToShowOnScreenOn, 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                 mMessageToShowOnScreenOn = null;
287             }
288         }
289 
290         @Override
291         public void onFingerprintRunningStateChanged(boolean running) {
292             if (running) {
293                 mMessageToShowOnScreenOn = null;
294             }
295         }
296     };
297 
298     BroadcastReceiver mReceiver = new BroadcastReceiver() {
299         @Override
300         public void onReceive(Context context, Intent intent) {
301             if (mVisible) {
302                 updateIndication();
303             }
304         }
305     };
306 
307     private final Handler mHandler = new Handler() {
308         @Override
309         public void handleMessage(Message msg) {
310             if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
311                 mTransientIndication = null;
312                 updateIndication();
313             } else if (msg.what == MSG_CLEAR_FP_MSG) {
314                 mLockIcon.setTransientFpError(false);
315                 hideTransientIndication();
316             }
317         }
318     };
319 
setStatusBarKeyguardViewManager( StatusBarKeyguardViewManager statusBarKeyguardViewManager)320     public void setStatusBarKeyguardViewManager(
321             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
322         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
323     }
324 }
325