Home | History | Annotate | Download | only in keyguard
      1 /*
      2  * Copyright (C) 2011 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.keyguard;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ObjectAnimator;
     22 import android.content.ContentResolver;
     23 import android.content.Context;
     24 import android.os.BatteryManager;
     25 import android.os.Handler;
     26 import android.os.Looper;
     27 import android.os.SystemClock;
     28 import android.text.TextUtils;
     29 import android.util.AttributeSet;
     30 import android.util.MutableInt;
     31 import android.view.View;
     32 import android.widget.TextView;
     33 
     34 import java.lang.ref.WeakReference;
     35 
     36 import com.android.internal.widget.LockPatternUtils;
     37 
     38 /***
     39  * Manages a number of views inside of the given layout. See below for a list of widgets.
     40  */
     41 class KeyguardMessageArea extends TextView {
     42     /** Handler token posted with accessibility announcement runnables. */
     43     private static final Object ANNOUNCE_TOKEN = new Object();
     44 
     45     /**
     46      * Delay before speaking an accessibility announcement. Used to prevent
     47      * lift-to-type from interrupting itself.
     48      */
     49     private static final long ANNOUNCEMENT_DELAY = 250;
     50 
     51     static final int SECURITY_MESSAGE_DURATION = 5000;
     52     protected static final int FADE_DURATION = 750;
     53 
     54     private static final String TAG = "KeyguardMessageArea";
     55 
     56     // is the bouncer up?
     57     boolean mShowingBouncer = false;
     58 
     59     KeyguardUpdateMonitor mUpdateMonitor;
     60 
     61     // Timeout before we reset the message to show charging/owner info
     62     long mTimeout = SECURITY_MESSAGE_DURATION;
     63 
     64     private Handler mHandler;
     65 
     66     CharSequence mMessage;
     67     boolean mShowingMessage;
     68     private CharSequence mSeparator;
     69     private LockPatternUtils mLockPatternUtils;
     70 
     71     Runnable mClearMessageRunnable = new Runnable() {
     72         @Override
     73         public void run() {
     74             mMessage = null;
     75             mShowingMessage = false;
     76             if (mShowingBouncer) {
     77                 hideMessage(FADE_DURATION, true);
     78             } else {
     79                 update();
     80             }
     81         }
     82     };
     83 
     84     public static class Helper implements SecurityMessageDisplay {
     85         KeyguardMessageArea mMessageArea;
     86         Helper(View v) {
     87             mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
     88             if (mMessageArea == null) {
     89                 throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
     90             }
     91         }
     92 
     93         public void setMessage(CharSequence msg, boolean important) {
     94             if (!TextUtils.isEmpty(msg) && important) {
     95                 mMessageArea.mMessage = msg;
     96                 mMessageArea.securityMessageChanged();
     97             }
     98         }
     99 
    100         public void setMessage(int resId, boolean important) {
    101             if (resId != 0 && important) {
    102                 mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
    103                 mMessageArea.securityMessageChanged();
    104             }
    105         }
    106 
    107         public void setMessage(int resId, boolean important, Object... formatArgs) {
    108             if (resId != 0 && important) {
    109                 mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
    110                 mMessageArea.securityMessageChanged();
    111             }
    112         }
    113 
    114         @Override
    115         public void showBouncer(int duration) {
    116             mMessageArea.hideMessage(duration, false);
    117             mMessageArea.mShowingBouncer = true;
    118         }
    119 
    120         @Override
    121         public void hideBouncer(int duration) {
    122             mMessageArea.showMessage(duration);
    123             mMessageArea.mShowingBouncer = false;
    124         }
    125 
    126         @Override
    127         public void setTimeout(int timeoutMs) {
    128             mMessageArea.mTimeout = timeoutMs;
    129         }
    130     }
    131 
    132     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
    133         public void onScreenTurnedOff(int why) {
    134             setSelected(false);
    135         };
    136         public void onScreenTurnedOn() {
    137             setSelected(true);
    138         };
    139     };
    140 
    141     public KeyguardMessageArea(Context context) {
    142         this(context, null);
    143     }
    144 
    145     public KeyguardMessageArea(Context context, AttributeSet attrs) {
    146         super(context, attrs);
    147         setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
    148 
    149         mLockPatternUtils = new LockPatternUtils(context);
    150 
    151         // Registering this callback immediately updates the battery state, among other things.
    152         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
    153         mUpdateMonitor.registerCallback(mInfoCallback);
    154         mHandler = new Handler(Looper.myLooper());
    155 
    156         mSeparator = getResources().getString(
    157                 com.android.internal.R.string.kg_text_message_separator);
    158 
    159         update();
    160     }
    161 
    162     @Override
    163     protected void onFinishInflate() {
    164         final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
    165         setSelected(screenOn); // This is required to ensure marquee works
    166     }
    167 
    168     public void securityMessageChanged() {
    169         setAlpha(1f);
    170         mShowingMessage = true;
    171         update();
    172         mHandler.removeCallbacks(mClearMessageRunnable);
    173         if (mTimeout > 0) {
    174             mHandler.postDelayed(mClearMessageRunnable, mTimeout);
    175         }
    176         mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
    177         mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
    178                 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
    179     }
    180 
    181     /**
    182      * Update the status lines based on these rules:
    183      * AlarmStatus: Alarm state always gets it's own line.
    184      * Status1 is shared between help, battery status and generic unlock instructions,
    185      * prioritized in that order.
    186      * @param showStatusLines status lines are shown if true
    187      */
    188     void update() {
    189         MutableInt icon = new MutableInt(0);
    190         CharSequence status = getCurrentMessage();
    191         setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
    192         setText(status);
    193     }
    194 
    195 
    196     CharSequence getCurrentMessage() {
    197         return mShowingMessage ? mMessage : null;
    198     }
    199 
    200     private void hideMessage(int duration, boolean thenUpdate) {
    201         if (duration > 0) {
    202             Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
    203             anim.setDuration(duration);
    204             if (thenUpdate) {
    205                 anim.addListener(new AnimatorListenerAdapter() {
    206                         @Override
    207                             public void onAnimationEnd(Animator animation) {
    208                             update();
    209                         }
    210                 });
    211             }
    212             anim.start();
    213         } else {
    214             setAlpha(0f);
    215             if (thenUpdate) {
    216                 update();
    217             }
    218         }
    219     }
    220 
    221     private void showMessage(int duration) {
    222         if (duration > 0) {
    223             Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
    224             anim.setDuration(duration);
    225             anim.start();
    226         } else {
    227             setAlpha(1f);
    228         }
    229     }
    230 
    231     /**
    232      * Runnable used to delay accessibility announcements.
    233      */
    234     private static class AnnounceRunnable implements Runnable {
    235         private final WeakReference<View> mHost;
    236         private final CharSequence mTextToAnnounce;
    237 
    238         public AnnounceRunnable(View host, CharSequence textToAnnounce) {
    239             mHost = new WeakReference<View>(host);
    240             mTextToAnnounce = textToAnnounce;
    241         }
    242 
    243         @Override
    244         public void run() {
    245             final View host = mHost.get();
    246             if (host != null) {
    247                 host.announceForAccessibility(mTextToAnnounce);
    248             }
    249         }
    250     }
    251 }
    252