blob: ede23efcf03fe5d0e80e4aeb39a9d1a338f55c28 [file] [log] [blame]
Jim Miller0b728242012-10-28 19:42:30 -07001/*
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
Jim Miller5ecd8112013-01-09 18:50:26 -080017package com.android.keyguard;
Jim Miller0b728242012-10-28 19:42:30 -070018
Chris Wrenc3451462012-10-30 11:22:58 -040019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ObjectAnimator;
Jim Miller0b728242012-10-28 19:42:30 -070022import android.content.ContentResolver;
23import android.content.Context;
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -050024import android.os.BatteryManager;
Jim Miller0b728242012-10-28 19:42:30 -070025import android.os.Handler;
26import android.os.Looper;
Alan Viveretteb428b0f2013-02-07 12:19:15 -080027import android.os.SystemClock;
Jim Miller0b728242012-10-28 19:42:30 -070028import android.text.TextUtils;
29import android.util.AttributeSet;
Elliott Hughescacbe1b2014-04-23 17:58:57 -070030import android.util.MutableInt;
Jim Miller0b728242012-10-28 19:42:30 -070031import android.view.View;
32import android.widget.TextView;
33
Alan Viveretteb428b0f2013-02-07 12:19:15 -080034import java.lang.ref.WeakReference;
35
Jim Miller187ec582013-04-15 18:27:54 -070036import com.android.internal.widget.LockPatternUtils;
Jim Miller0b728242012-10-28 19:42:30 -070037
38/***
39 * Manages a number of views inside of the given layout. See below for a list of widgets.
40 */
41class KeyguardMessageArea extends TextView {
Alan Viveretteb428b0f2013-02-07 12:19:15 -080042 /** Handler token posted with accessibility announcement runnables. */
43 private static final Object ANNOUNCE_TOKEN = new Object();
44
45 /**
46 * Delay before speaking an accessibility announcement. Used to prevent
47 * lift-to-type from interrupting itself.
48 */
49 private static final long ANNOUNCEMENT_DELAY = 250;
50
Jim Miller0b728242012-10-28 19:42:30 -070051 static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
52 static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
53
54 static final int SECURITY_MESSAGE_DURATION = 5000;
Chris Wrenc3451462012-10-30 11:22:58 -040055 protected static final int FADE_DURATION = 750;
Jim Miller0b728242012-10-28 19:42:30 -070056
Jim Miller187ec582013-04-15 18:27:54 -070057 private static final String TAG = "KeyguardMessageArea";
58
Jim Miller0b728242012-10-28 19:42:30 -070059 // are we showing battery information?
60 boolean mShowingBatteryInfo = false;
61
Chris Wrenc3451462012-10-30 11:22:58 -040062 // is the bouncer up?
63 boolean mShowingBouncer = false;
64
Jim Miller0b728242012-10-28 19:42:30 -070065 // last known plugged in state
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -050066 boolean mCharging = false;
Jim Miller0b728242012-10-28 19:42:30 -070067
68 // last known battery level
69 int mBatteryLevel = 100;
70
71 KeyguardUpdateMonitor mUpdateMonitor;
72
73 // Timeout before we reset the message to show charging/owner info
74 long mTimeout = SECURITY_MESSAGE_DURATION;
75
76 // Shadowed text values
77 protected boolean mBatteryCharged;
78 protected boolean mBatteryIsLow;
79
80 private Handler mHandler;
81
82 CharSequence mMessage;
83 boolean mShowingMessage;
Jim Miller187ec582013-04-15 18:27:54 -070084 private CharSequence mSeparator;
85 private LockPatternUtils mLockPatternUtils;
86
Jim Miller0b728242012-10-28 19:42:30 -070087 Runnable mClearMessageRunnable = new Runnable() {
88 @Override
89 public void run() {
90 mMessage = null;
91 mShowingMessage = false;
Chris Wrenc3451462012-10-30 11:22:58 -040092 if (mShowingBouncer) {
93 hideMessage(FADE_DURATION, true);
94 } else {
95 update();
96 }
Jim Miller0b728242012-10-28 19:42:30 -070097 }
98 };
99
100 public static class Helper implements SecurityMessageDisplay {
101 KeyguardMessageArea mMessageArea;
102 Helper(View v) {
103 mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
104 if (mMessageArea == null) {
105 throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
106 }
107 }
108
109 public void setMessage(CharSequence msg, boolean important) {
110 if (!TextUtils.isEmpty(msg) && important) {
111 mMessageArea.mMessage = msg;
112 mMessageArea.securityMessageChanged();
113 }
114 }
115
116 public void setMessage(int resId, boolean important) {
117 if (resId != 0 && important) {
118 mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
119 mMessageArea.securityMessageChanged();
120 }
121 }
122
123 public void setMessage(int resId, boolean important, Object... formatArgs) {
124 if (resId != 0 && important) {
125 mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
126 mMessageArea.securityMessageChanged();
127 }
128 }
129
130 @Override
Chris Wrenc3451462012-10-30 11:22:58 -0400131 public void showBouncer(int duration) {
132 mMessageArea.hideMessage(duration, false);
133 mMessageArea.mShowingBouncer = true;
134 }
135
136 @Override
137 public void hideBouncer(int duration) {
138 mMessageArea.showMessage(duration);
139 mMessageArea.mShowingBouncer = false;
140 }
141
142 @Override
Jim Miller0b728242012-10-28 19:42:30 -0700143 public void setTimeout(int timeoutMs) {
144 mMessageArea.mTimeout = timeoutMs;
145 }
146 }
147
148 private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
149 @Override
150 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
151 mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500152 mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
153 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Jim Miller0b728242012-10-28 19:42:30 -0700154 mBatteryLevel = status.level;
155 mBatteryCharged = status.isCharged();
156 mBatteryIsLow = status.isBatteryLow();
157 update();
158 }
Jim Miller20daffd2013-10-07 14:59:53 -0700159 public void onScreenTurnedOff(int why) {
160 setSelected(false);
161 };
162 public void onScreenTurnedOn() {
163 setSelected(true);
164 };
Jim Miller0b728242012-10-28 19:42:30 -0700165 };
166
167 public KeyguardMessageArea(Context context) {
168 this(context, null);
169 }
170
171 public KeyguardMessageArea(Context context, AttributeSet attrs) {
172 super(context, attrs);
Chris Craik362adb82013-11-05 17:41:21 -0800173 setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
Jim Miller0b728242012-10-28 19:42:30 -0700174
Jim Miller187ec582013-04-15 18:27:54 -0700175 mLockPatternUtils = new LockPatternUtils(context);
176
Jim Miller0b728242012-10-28 19:42:30 -0700177 // Registering this callback immediately updates the battery state, among other things.
178 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
179 mUpdateMonitor.registerCallback(mInfoCallback);
180 mHandler = new Handler(Looper.myLooper());
181
Jim Millere9be1402012-11-01 16:14:20 -0700182 mSeparator = getResources().getString(R.string.kg_text_message_separator);
183
Jim Miller0b728242012-10-28 19:42:30 -0700184 update();
185 }
186
Jim Miller20daffd2013-10-07 14:59:53 -0700187 @Override
188 protected void onFinishInflate() {
189 final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
190 setSelected(screenOn); // This is required to ensure marquee works
191 }
192
Jim Miller0b728242012-10-28 19:42:30 -0700193 public void securityMessageChanged() {
Chris Wrenc3451462012-10-30 11:22:58 -0400194 setAlpha(1f);
Jim Miller0b728242012-10-28 19:42:30 -0700195 mShowingMessage = true;
196 update();
197 mHandler.removeCallbacks(mClearMessageRunnable);
198 if (mTimeout > 0) {
199 mHandler.postDelayed(mClearMessageRunnable, mTimeout);
200 }
Alan Viveretteb428b0f2013-02-07 12:19:15 -0800201 mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
202 mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
203 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
Jim Miller0b728242012-10-28 19:42:30 -0700204 }
205
206 /**
207 * Update the status lines based on these rules:
208 * AlarmStatus: Alarm state always gets it's own line.
209 * Status1 is shared between help, battery status and generic unlock instructions,
210 * prioritized in that order.
211 * @param showStatusLines status lines are shown if true
212 */
213 void update() {
214 MutableInt icon = new MutableInt(0);
215 CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
216 setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
217 setText(status);
218 }
219
Jim Millere9be1402012-11-01 16:14:20 -0700220 private CharSequence concat(CharSequence... args) {
Jim Miller0b728242012-10-28 19:42:30 -0700221 StringBuilder b = new StringBuilder();
Jim Millere9be1402012-11-01 16:14:20 -0700222 if (!TextUtils.isEmpty(args[0])) {
223 b.append(args[0]);
224 }
225 for (int i = 1; i < args.length; i++) {
226 CharSequence text = args[i];
227 if (!TextUtils.isEmpty(text)) {
228 if (b.length() > 0) {
229 b.append(mSeparator);
230 }
231 b.append(text);
Jim Miller0b728242012-10-28 19:42:30 -0700232 }
233 }
234 return b.toString();
235 }
236
Jim Miller0b728242012-10-28 19:42:30 -0700237 CharSequence getCurrentMessage() {
238 return mShowingMessage ? mMessage : null;
239 }
240
241 String getOwnerInfo() {
242 ContentResolver res = getContext().getContentResolver();
Jim Miller187ec582013-04-15 18:27:54 -0700243 String info = null;
244 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
245 if (ownerInfoEnabled && !mShowingMessage) {
246 info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
247 }
248 return info;
Jim Miller0b728242012-10-28 19:42:30 -0700249 }
250
251 private CharSequence getChargeInfo(MutableInt icon) {
252 CharSequence string = null;
253 if (mShowingBatteryInfo && !mShowingMessage) {
254 // Battery status
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500255 if (mCharging) {
Jim Miller0b728242012-10-28 19:42:30 -0700256 // Charging, charged or waiting to charge.
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500257 string = getContext().getString(mBatteryCharged
Jim Miller5ecd8112013-01-09 18:50:26 -0800258 ? R.string.keyguard_charged
259 : R.string.keyguard_plugged_in, mBatteryLevel);
Jim Miller0b728242012-10-28 19:42:30 -0700260 icon.value = CHARGING_ICON;
261 } else if (mBatteryIsLow) {
262 // Battery is low
Jim Miller5ecd8112013-01-09 18:50:26 -0800263 string = getContext().getString(R.string.keyguard_low_battery);
Jim Miller0b728242012-10-28 19:42:30 -0700264 icon.value = BATTERY_LOW_ICON;
265 }
266 }
267 return string;
268 }
269
Chris Wrenc3451462012-10-30 11:22:58 -0400270 private void hideMessage(int duration, boolean thenUpdate) {
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500271 if (duration > 0) {
272 Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
273 anim.setDuration(duration);
274 if (thenUpdate) {
275 anim.addListener(new AnimatorListenerAdapter() {
276 @Override
277 public void onAnimationEnd(Animator animation) {
278 update();
279 }
Chris Wrenc3451462012-10-30 11:22:58 -0400280 });
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500281 }
282 anim.start();
283 } else {
284 setAlpha(0f);
285 if (thenUpdate) {
286 update();
287 }
Chris Wrenc3451462012-10-30 11:22:58 -0400288 }
Chris Wrenc3451462012-10-30 11:22:58 -0400289 }
290
291 private void showMessage(int duration) {
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500292 if (duration > 0) {
293 Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
294 anim.setDuration(duration);
295 anim.start();
296 } else {
297 setAlpha(1f);
298 }
Chris Wrenc3451462012-10-30 11:22:58 -0400299 }
Alan Viveretteb428b0f2013-02-07 12:19:15 -0800300
301 /**
302 * Runnable used to delay accessibility announcements.
303 */
304 private static class AnnounceRunnable implements Runnable {
305 private final WeakReference<View> mHost;
306 private final CharSequence mTextToAnnounce;
307
308 public AnnounceRunnable(View host, CharSequence textToAnnounce) {
309 mHost = new WeakReference<View>(host);
310 mTextToAnnounce = textToAnnounce;
311 }
312
313 @Override
314 public void run() {
315 final View host = mHost.get();
316 if (host != null) {
317 host.announceForAccessibility(mTextToAnnounce);
318 }
319 }
320 }
Jim Miller0b728242012-10-28 19:42:30 -0700321}