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