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 static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;
     20 
     21 import android.content.Context;
     22 import android.content.res.ColorStateList;
     23 import android.content.res.TypedArray;
     24 import android.graphics.Color;
     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.TypedValue;
     31 import android.view.View;
     32 import android.widget.TextView;
     33 
     34 import com.android.systemui.statusbar.policy.ConfigurationController;
     35 
     36 import java.lang.ref.WeakReference;
     37 
     38 import javax.inject.Inject;
     39 import javax.inject.Named;
     40 
     41 /***
     42  * Manages a number of views inside of the given layout. See below for a list of widgets.
     43  */
     44 public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay,
     45         ConfigurationController.ConfigurationListener {
     46     /** Handler token posted with accessibility announcement runnables. */
     47     private static final Object ANNOUNCE_TOKEN = new Object();
     48 
     49     /**
     50      * Delay before speaking an accessibility announcement. Used to prevent
     51      * lift-to-type from interrupting itself.
     52      */
     53     private static final long ANNOUNCEMENT_DELAY = 250;
     54     private static final int DEFAULT_COLOR = -1;
     55 
     56     private final Handler mHandler;
     57     private final ConfigurationController mConfigurationController;
     58 
     59     private ColorStateList mDefaultColorState;
     60     private CharSequence mMessage;
     61     private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
     62     private boolean mBouncerVisible;
     63 
     64     private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
     65         public void onFinishedGoingToSleep(int why) {
     66             setSelected(false);
     67         }
     68 
     69         public void onStartedWakingUp() {
     70             setSelected(true);
     71         }
     72 
     73         @Override
     74         public void onKeyguardBouncerChanged(boolean bouncer) {
     75             mBouncerVisible = bouncer;
     76             update();
     77         }
     78     };
     79 
     80     public KeyguardMessageArea(Context context) {
     81         super(context, null);
     82         throw new IllegalStateException("This constructor should never be invoked");
     83     }
     84 
     85     @Inject
     86     public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
     87             ConfigurationController configurationController) {
     88         this(context, attrs, KeyguardUpdateMonitor.getInstance(context), configurationController);
     89     }
     90 
     91     public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor,
     92             ConfigurationController configurationController) {
     93         super(context, attrs);
     94         setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
     95 
     96         monitor.registerCallback(mInfoCallback);
     97         mHandler = new Handler(Looper.myLooper());
     98         mConfigurationController = configurationController;
     99         onThemeChanged();
    100     }
    101 
    102     @Override
    103     protected void onAttachedToWindow() {
    104         super.onAttachedToWindow();
    105         mConfigurationController.addCallback(this);
    106         onThemeChanged();
    107     }
    108 
    109     @Override
    110     protected void onDetachedFromWindow() {
    111         super.onDetachedFromWindow();
    112         mConfigurationController.removeCallback(this);
    113     }
    114 
    115     @Override
    116     public void setNextMessageColor(ColorStateList colorState) {
    117         mNextMessageColorState = colorState;
    118     }
    119 
    120     @Override
    121     public void onThemeChanged() {
    122         TypedArray array = mContext.obtainStyledAttributes(new int[] {
    123                 R.attr.wallpaperTextColor
    124         });
    125         ColorStateList newTextColors = ColorStateList.valueOf(array.getColor(0, Color.RED));
    126         array.recycle();
    127         mDefaultColorState = newTextColors;
    128         update();
    129     }
    130 
    131     @Override
    132     public void onDensityOrFontScaleChanged() {
    133         TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] {
    134                 android.R.attr.textSize
    135         });
    136         setTextSize(TypedValue.COMPLEX_UNIT_PX, array.getDimensionPixelSize(0, 0));
    137         array.recycle();
    138     }
    139 
    140     @Override
    141     public void setMessage(CharSequence msg) {
    142         if (!TextUtils.isEmpty(msg)) {
    143             securityMessageChanged(msg);
    144         } else {
    145             clearMessage();
    146         }
    147     }
    148 
    149     @Override
    150     public void setMessage(int resId) {
    151         CharSequence message = null;
    152         if (resId != 0) {
    153             message = getContext().getResources().getText(resId);
    154         }
    155         setMessage(message);
    156     }
    157 
    158     @Override
    159     public void formatMessage(int resId, Object... formatArgs) {
    160         CharSequence message = null;
    161         if (resId != 0) {
    162             message = getContext().getString(resId, formatArgs);
    163         }
    164         setMessage(message);
    165     }
    166 
    167     public static KeyguardMessageArea findSecurityMessageDisplay(View v) {
    168         KeyguardMessageArea messageArea = v.findViewById(R.id.keyguard_message_area);
    169         if (messageArea == null) {
    170             messageArea = v.getRootView().findViewById(R.id.keyguard_message_area);
    171         }
    172         if (messageArea == null) {
    173             throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
    174         }
    175         return messageArea;
    176     }
    177 
    178     @Override
    179     protected void onFinishInflate() {
    180         boolean shouldMarquee = KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
    181         setSelected(shouldMarquee); // This is required to ensure marquee works
    182     }
    183 
    184     private void securityMessageChanged(CharSequence message) {
    185         mMessage = message;
    186         update();
    187         mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
    188         mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
    189                 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
    190     }
    191 
    192     private void clearMessage() {
    193         mMessage = null;
    194         update();
    195     }
    196 
    197     private void update() {
    198         CharSequence status = mMessage;
    199         setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE);
    200         setText(status);
    201         ColorStateList colorState = mDefaultColorState;
    202         if (mNextMessageColorState.getDefaultColor() != DEFAULT_COLOR) {
    203             colorState = mNextMessageColorState;
    204             mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR);
    205         }
    206         setTextColor(colorState);
    207     }
    208 
    209 
    210     /**
    211      * Runnable used to delay accessibility announcements.
    212      */
    213     private static class AnnounceRunnable implements Runnable {
    214         private final WeakReference<View> mHost;
    215         private final CharSequence mTextToAnnounce;
    216 
    217         AnnounceRunnable(View host, CharSequence textToAnnounce) {
    218             mHost = new WeakReference<View>(host);
    219             mTextToAnnounce = textToAnnounce;
    220         }
    221 
    222         @Override
    223         public void run() {
    224             final View host = mHost.get();
    225             if (host != null) {
    226                 host.announceForAccessibility(mTextToAnnounce);
    227             }
    228         }
    229     }
    230 }
    231