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