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     private static final int DEFAULT_COLOR = -1;
     43 
     44     private final Handler mHandler;
     45     private final int mDefaultColor;
     46 
     47     private CharSequence mMessage;
     48     private int mNextMessageColor = DEFAULT_COLOR;
     49 
     50     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
     51         public void onFinishedGoingToSleep(int why) {
     52             setSelected(false);
     53         };
     54         public void onStartedWakingUp() {
     55             setSelected(true);
     56         };
     57     };
     58 
     59     public KeyguardMessageArea(Context context) {
     60         this(context, null);
     61     }
     62 
     63     public KeyguardMessageArea(Context context, AttributeSet attrs) {
     64         this(context, attrs, KeyguardUpdateMonitor.getInstance(context));
     65     }
     66 
     67     public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor) {
     68         super(context, attrs);
     69         setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
     70 
     71         monitor.registerCallback(mInfoCallback);
     72         mHandler = new Handler(Looper.myLooper());
     73 
     74         mDefaultColor = getCurrentTextColor();
     75         update();
     76     }
     77 
     78     @Override
     79     public void setNextMessageColor(int color) {
     80         mNextMessageColor = color;
     81     }
     82 
     83     @Override
     84     public void setMessage(CharSequence msg) {
     85         if (!TextUtils.isEmpty(msg)) {
     86             securityMessageChanged(msg);
     87         } else {
     88             clearMessage();
     89         }
     90     }
     91 
     92     @Override
     93     public void setMessage(int resId) {
     94         CharSequence message = null;
     95         if (resId != 0) {
     96             message = getContext().getResources().getText(resId);
     97         }
     98         setMessage(message);
     99     }
    100 
    101     @Override
    102     public void formatMessage(int resId, Object... formatArgs) {
    103         CharSequence message = null;
    104         if (resId != 0) {
    105             message = getContext().getString(resId, formatArgs);
    106         }
    107         setMessage(message);
    108     }
    109 
    110     public static SecurityMessageDisplay findSecurityMessageDisplay(View v) {
    111         KeyguardMessageArea messageArea = (KeyguardMessageArea) v.findViewById(
    112                 R.id.keyguard_message_area);
    113         if (messageArea == null) {
    114             throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
    115         }
    116         return messageArea;
    117     }
    118 
    119     @Override
    120     protected void onFinishInflate() {
    121         boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
    122         setSelected(shouldMarquee); // This is required to ensure marquee works
    123     }
    124 
    125     private void securityMessageChanged(CharSequence message) {
    126         mMessage = message;
    127         update();
    128         mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
    129         mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
    130                 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
    131     }
    132 
    133     private void clearMessage() {
    134         mMessage = null;
    135         update();
    136     }
    137 
    138     private void update() {
    139         CharSequence status = mMessage;
    140         setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
    141         setText(status);
    142         int color = mDefaultColor;
    143         if (mNextMessageColor != DEFAULT_COLOR) {
    144             color = mNextMessageColor;
    145             mNextMessageColor = DEFAULT_COLOR;
    146         }
    147         setTextColor(color);
    148     }
    149 
    150 
    151     /**
    152      * Runnable used to delay accessibility announcements.
    153      */
    154     private static class AnnounceRunnable implements Runnable {
    155         private final WeakReference<View> mHost;
    156         private final CharSequence mTextToAnnounce;
    157 
    158         AnnounceRunnable(View host, CharSequence textToAnnounce) {
    159             mHost = new WeakReference<View>(host);
    160             mTextToAnnounce = textToAnnounce;
    161         }
    162 
    163         @Override
    164         public void run() {
    165             final View host = mHost.get();
    166             if (host != null) {
    167                 host.announceForAccessibility(mTextToAnnounce);
    168             }
    169         }
    170     }
    171 }
    172