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.content.Context;
     20 import android.os.Handler;
     21 import android.os.Looper;
     22 import android.os.SystemClock;
     23 import android.text.TextUtils;
     24 import android.util.AttributeSet;
     25 import android.view.View;
     26 import android.widget.TextView;
     27 
     28 import java.lang.ref.WeakReference;
     29 
     30 /***
     31  * Manages a number of views inside of the given layout. See below for a list of widgets.
     32  */
     33 class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
     34     /** Handler token posted with accessibility announcement runnables. */
     35     private static final Object ANNOUNCE_TOKEN = new Object();
     36 
     37     /**
     38      * Delay before speaking an accessibility announcement. Used to prevent
     39      * lift-to-type from interrupting itself.
     40      */
     41     private static final long ANNOUNCEMENT_DELAY = 250;
     42 
     43     private static final int SECURITY_MESSAGE_DURATION = 5000;
     44 
     45     private final KeyguardUpdateMonitor mUpdateMonitor;
     46     private final Handler mHandler;
     47 
     48     // Timeout before we reset the message to show charging/owner info
     49     long mTimeout = SECURITY_MESSAGE_DURATION;
     50     CharSequence mMessage;
     51 
     52     private final Runnable mClearMessageRunnable = new Runnable() {
     53         @Override
     54         public void run() {
     55             mMessage = null;
     56             update();
     57         }
     58     };
     59 
     60     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
     61         public void onFinishedGoingToSleep(int why) {
     62             setSelected(false);
     63         };
     64         public void onStartedWakingUp() {
     65             setSelected(true);
     66         };
     67     };
     68 
     69     public KeyguardMessageArea(Context context) {
     70         this(context, null);
     71     }
     72 
     73     public KeyguardMessageArea(Context context, AttributeSet attrs) {
     74         super(context, attrs);
     75         setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
     76 
     77         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
     78         mUpdateMonitor.registerCallback(mInfoCallback);
     79         mHandler = new Handler(Looper.myLooper());
     80 
     81         update();
     82     }
     83 
     84     @Override
     85     public void setMessage(CharSequence msg, boolean important) {
     86         if (!TextUtils.isEmpty(msg) && important) {
     87             securityMessageChanged(msg);
     88         } else {
     89             clearMessage();
     90         }
     91     }
     92 
     93     @Override
     94     public void setMessage(int resId, boolean important) {
     95         if (resId != 0 && important) {
     96             CharSequence message = getContext().getResources().getText(resId);
     97             securityMessageChanged(message);
     98         } else {
     99             clearMessage();
    100         }
    101     }
    102 
    103     @Override
    104     public void setMessage(int resId, boolean important, Object... formatArgs) {
    105         if (resId != 0 && important) {
    106             String message = getContext().getString(resId, formatArgs);
    107             securityMessageChanged(message);
    108         } else {
    109             clearMessage();
    110         }
    111     }
    112 
    113     @Override
    114     public void setTimeout(int timeoutMs) {
    115         mTimeout = timeoutMs;
    116     }
    117 
    118     public static SecurityMessageDisplay findSecurityMessageDisplay(View v) {
    119         KeyguardMessageArea messageArea = (KeyguardMessageArea) v.findViewById(
    120                 R.id.keyguard_message_area);
    121         if (messageArea == null) {
    122             throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
    123         }
    124         return messageArea;
    125     }
    126 
    127     @Override
    128     protected void onFinishInflate() {
    129         boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
    130         setSelected(shouldMarquee); // This is required to ensure marquee works
    131     }
    132 
    133     private void securityMessageChanged(CharSequence message) {
    134         mMessage = message;
    135         update();
    136         mHandler.removeCallbacks(mClearMessageRunnable);
    137         if (mTimeout > 0) {
    138             mHandler.postDelayed(mClearMessageRunnable, mTimeout);
    139         }
    140         mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
    141         mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
    142                 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
    143     }
    144 
    145     private void clearMessage() {
    146         mHandler.removeCallbacks(mClearMessageRunnable);
    147         mHandler.post(mClearMessageRunnable);
    148     }
    149 
    150     private void update() {
    151         CharSequence status = mMessage;
    152         setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
    153         setText(status);
    154     }
    155 
    156 
    157     /**
    158      * Runnable used to delay accessibility announcements.
    159      */
    160     private static class AnnounceRunnable implements Runnable {
    161         private final WeakReference<View> mHost;
    162         private final CharSequence mTextToAnnounce;
    163 
    164         AnnounceRunnable(View host, CharSequence textToAnnounce) {
    165             mHost = new WeakReference<View>(host);
    166             mTextToAnnounce = textToAnnounce;
    167         }
    168 
    169         @Override
    170         public void run() {
    171             final View host = mHost.get();
    172             if (host != null) {
    173                 host.announceForAccessibility(mTextToAnnounce);
    174             }
    175         }
    176     }
    177 }
    178