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