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