Home | History | Annotate | Download | only in statusbar
      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.app.ActivityManager;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.res.Resources;
     25 import android.graphics.Color;
     26 import android.hardware.fingerprint.FingerprintManager;
     27 import android.os.BatteryManager;
     28 import android.os.BatteryStats;
     29 import android.os.Handler;
     30 import android.os.Message;
     31 import android.os.RemoteException;
     32 import android.os.ServiceManager;
     33 import android.os.UserHandle;
     34 import android.os.UserManager;
     35 import android.text.TextUtils;
     36 import android.text.format.Formatter;
     37 import android.util.Log;
     38 import android.view.View;
     39 
     40 import com.android.internal.app.IBatteryStats;
     41 import com.android.keyguard.KeyguardUpdateMonitor;
     42 import com.android.keyguard.KeyguardUpdateMonitorCallback;
     43 import com.android.systemui.R;
     44 import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
     45 import com.android.systemui.statusbar.phone.LockIcon;
     46 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
     47 
     48 /**
     49  * Controls the indications and error messages shown on the Keyguard
     50  */
     51 public class KeyguardIndicationController {
     52 
     53     private static final String TAG = "KeyguardIndication";
     54     private static final boolean DEBUG_CHARGING_SPEED = false;
     55 
     56     private static final int MSG_HIDE_TRANSIENT = 1;
     57     private static final int MSG_CLEAR_FP_MSG = 2;
     58     private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
     59 
     60     private final Context mContext;
     61     private final KeyguardIndicationTextView mTextView;
     62     private final UserManager mUserManager;
     63     private final IBatteryStats mBatteryInfo;
     64 
     65     private final int mSlowThreshold;
     66     private final int mFastThreshold;
     67     private final LockIcon mLockIcon;
     68     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     69 
     70     private String mRestingIndication;
     71     private String mTransientIndication;
     72     private int mTransientTextColor;
     73     private boolean mVisible;
     74 
     75     private boolean mPowerPluggedIn;
     76     private boolean mPowerCharged;
     77     private int mChargingSpeed;
     78     private int mChargingWattage;
     79     private String mMessageToShowOnScreenOn;
     80 
     81     public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
     82                                         LockIcon lockIcon) {
     83         mContext = context;
     84         mTextView = textView;
     85         mLockIcon = lockIcon;
     86 
     87         Resources res = context.getResources();
     88         mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
     89         mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
     90 
     91         mUserManager = context.getSystemService(UserManager.class);
     92         mBatteryInfo = IBatteryStats.Stub.asInterface(
     93                 ServiceManager.getService(BatteryStats.SERVICE_NAME));
     94 
     95         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitor);
     96         context.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM,
     97                 new IntentFilter(Intent.ACTION_TIME_TICK), null, null);
     98     }
     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) {
    136         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) {
    143         mTransientIndication = transientIndication;
    144         mTransientTextColor = textColor;
    145         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) {
    162             // 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);
    167 
    168             } else if (!TextUtils.isEmpty(mTransientIndication)) {
    169                 mTextView.switchIndication(mTransientIndication);
    170                 mTextView.setTextColor(mTransientTextColor);
    171 
    172             } 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);
    183             }
    184         }
    185     }
    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.
    193         long chargingTimeRemaining = 0;
    194         try {
    195             chargingTimeRemaining = mBatteryInfo.computeChargeTimeRemaining();
    196 
    197         } catch (RemoteException e) {
    198             Log.e(TAG, "Error calling IBatteryStats: ", e);
    199         }
    200         final boolean hasChargingTime = chargingTimeRemaining > 0;
    201 
    202         int chargingId;
    203         switch (mChargingSpeed) {
    204             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
    205                 chargingId = hasChargingTime
    206                         ? R.string.keyguard_indication_charging_time_fast
    207                         : R.string.keyguard_plugged_in_charging_fast;
    208                 break;
    209             case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
    210                 chargingId = hasChargingTime
    211                         ? R.string.keyguard_indication_charging_time_slowly
    212                         : R.string.keyguard_plugged_in_charging_slowly;
    213                 break;
    214             default:
    215                 chargingId = hasChargingTime
    216                         ? R.string.keyguard_indication_charging_time
    217                         : R.string.keyguard_plugged_in;
    218                 break;
    219         }
    220 
    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         }
    228     }
    229 
    230     KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
    231         public int mLastSuccessiveErrorMessage = -1;
    232 
    233         @Override
    234         public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
    235             boolean isChargingOrFull = status.status == BatteryManager.BATTERY_STATUS_CHARGING
    236                     || status.status == BatteryManager.BATTERY_STATUS_FULL;
    237             mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
    238             mPowerCharged = status.isCharged();
    239             mChargingWattage = status.maxChargingWattage;
    240             mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
    241             updateIndication();
    242         }
    243 
    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             }
    260             // 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;
    263         }
    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()) {
    274                 // 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                 }
    281             } else if (updateMonitor.isDeviceInteractive()) {
    282                 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;
    288             }
    289             mLastSuccessiveErrorMessage = msgId;
    290         }
    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         }
    311 
    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         }
    323     };
    324 
    325     BroadcastReceiver mReceiver = new BroadcastReceiver() {
    326         @Override
    327         public void onReceive(Context context, Intent intent) {
    328             if (mVisible) {
    329                 updateIndication();
    330             }
    331         }
    332     };
    333 
    334     private final Handler mHandler = new Handler() {
    335         @Override
    336         public void handleMessage(Message msg) {
    337             if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
    338                 mTransientIndication = null;
    339                 updateIndication();
    340             } else if (msg.what == MSG_CLEAR_FP_MSG) {
    341                 mLockIcon.setTransientFpError(false);
    342                 hideTransientIndication();
    343             }
    344         }
    345     };
    346 
    347     public void setStatusBarKeyguardViewManager(
    348             StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
    349         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
    350     }
    351 }
    352