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