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