blob: 2e4dbdf7945baefd51c37b6f67c4ee59f09ceced [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;
30import android.view.View;
31import android.widget.TextView;
32
33import libcore.util.MutableInt;
34
Alan Viveretteb428b0f2013-02-07 12:19:15 -080035import java.lang.ref.WeakReference;
36
Jim Miller187ec582013-04-15 18:27:54 -070037import com.android.internal.widget.LockPatternUtils;
Jim Miller0b728242012-10-28 19:42:30 -070038
39/***
40 * Manages a number of views inside of the given layout. See below for a list of widgets.
41 */
42class KeyguardMessageArea extends TextView {
Alan Viveretteb428b0f2013-02-07 12:19:15 -080043 /** Handler token posted with accessibility announcement runnables. */
44 private static final Object ANNOUNCE_TOKEN = new Object();
45
46 /**
47 * Delay before speaking an accessibility announcement. Used to prevent
48 * lift-to-type from interrupting itself.
49 */
50 private static final long ANNOUNCEMENT_DELAY = 250;
51
Jim Miller0b728242012-10-28 19:42:30 -070052 static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
53 static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
54
55 static final int SECURITY_MESSAGE_DURATION = 5000;
Chris Wrenc3451462012-10-30 11:22:58 -040056 protected static final int FADE_DURATION = 750;
Jim Miller0b728242012-10-28 19:42:30 -070057
Jim Miller187ec582013-04-15 18:27:54 -070058 private static final String TAG = "KeyguardMessageArea";
59
Jim Miller0b728242012-10-28 19:42:30 -070060 // are we showing battery information?
61 boolean mShowingBatteryInfo = false;
62
Chris Wrenc3451462012-10-30 11:22:58 -040063 // is the bouncer up?
64 boolean mShowingBouncer = false;
65
Jim Miller0b728242012-10-28 19:42:30 -070066 // last known plugged in state
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -050067 boolean mCharging = false;
Jim Miller0b728242012-10-28 19:42:30 -070068
69 // last known battery level
70 int mBatteryLevel = 100;
71
72 KeyguardUpdateMonitor mUpdateMonitor;
73
74 // Timeout before we reset the message to show charging/owner info
75 long mTimeout = SECURITY_MESSAGE_DURATION;
76
77 // Shadowed text values
78 protected boolean mBatteryCharged;
79 protected boolean mBatteryIsLow;
80
81 private Handler mHandler;
82
83 CharSequence mMessage;
84 boolean mShowingMessage;
Jim Miller187ec582013-04-15 18:27:54 -070085 private CharSequence mSeparator;
86 private LockPatternUtils mLockPatternUtils;
87
Jim Miller0b728242012-10-28 19:42:30 -070088 Runnable mClearMessageRunnable = new Runnable() {
89 @Override
90 public void run() {
91 mMessage = null;
92 mShowingMessage = false;
Chris Wrenc3451462012-10-30 11:22:58 -040093 if (mShowingBouncer) {
94 hideMessage(FADE_DURATION, true);
95 } else {
96 update();
97 }
Jim Miller0b728242012-10-28 19:42:30 -070098 }
99 };
100
101 public static class Helper implements SecurityMessageDisplay {
102 KeyguardMessageArea mMessageArea;
103 Helper(View v) {
104 mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
105 if (mMessageArea == null) {
106 throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
107 }
108 }
109
110 public void setMessage(CharSequence msg, boolean important) {
111 if (!TextUtils.isEmpty(msg) && important) {
112 mMessageArea.mMessage = msg;
113 mMessageArea.securityMessageChanged();
114 }
115 }
116
117 public void setMessage(int resId, boolean important) {
118 if (resId != 0 && important) {
119 mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
120 mMessageArea.securityMessageChanged();
121 }
122 }
123
124 public void setMessage(int resId, boolean important, Object... formatArgs) {
125 if (resId != 0 && important) {
126 mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
127 mMessageArea.securityMessageChanged();
128 }
129 }
130
131 @Override
Chris Wrenc3451462012-10-30 11:22:58 -0400132 public void showBouncer(int duration) {
133 mMessageArea.hideMessage(duration, false);
134 mMessageArea.mShowingBouncer = true;
135 }
136
137 @Override
138 public void hideBouncer(int duration) {
139 mMessageArea.showMessage(duration);
140 mMessageArea.mShowingBouncer = false;
141 }
142
143 @Override
Jim Miller0b728242012-10-28 19:42:30 -0700144 public void setTimeout(int timeoutMs) {
145 mMessageArea.mTimeout = timeoutMs;
146 }
147 }
148
149 private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
150 @Override
151 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
152 mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500153 mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
154 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Jim Miller0b728242012-10-28 19:42:30 -0700155 mBatteryLevel = status.level;
156 mBatteryCharged = status.isCharged();
157 mBatteryIsLow = status.isBatteryLow();
158 update();
159 }
Jim Miller20daffd2013-10-07 14:59:53 -0700160 public void onScreenTurnedOff(int why) {
161 setSelected(false);
162 };
163 public void onScreenTurnedOn() {
164 setSelected(true);
165 };
Jim Miller0b728242012-10-28 19:42:30 -0700166 };
167
168 public KeyguardMessageArea(Context context) {
169 this(context, null);
170 }
171
172 public KeyguardMessageArea(Context context, AttributeSet attrs) {
173 super(context, attrs);
Chris Craik362adb82013-11-05 17:41:21 -0800174 setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
Jim Miller0b728242012-10-28 19:42:30 -0700175
Jim Miller187ec582013-04-15 18:27:54 -0700176 mLockPatternUtils = new LockPatternUtils(context);
177
Jim Miller0b728242012-10-28 19:42:30 -0700178 // Registering this callback immediately updates the battery state, among other things.
179 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
180 mUpdateMonitor.registerCallback(mInfoCallback);
181 mHandler = new Handler(Looper.myLooper());
182
Jim Millere9be1402012-11-01 16:14:20 -0700183 mSeparator = getResources().getString(R.string.kg_text_message_separator);
184
Jim Miller0b728242012-10-28 19:42:30 -0700185 update();
186 }
187
Jim Miller20daffd2013-10-07 14:59:53 -0700188 @Override
189 protected void onFinishInflate() {
190 final boolean screenOn = KeyguardUpdateMonitor.getInstance(mContext).isScreenOn();
191 setSelected(screenOn); // This is required to ensure marquee works
192 }
193
Jim Miller0b728242012-10-28 19:42:30 -0700194 public void securityMessageChanged() {
Chris Wrenc3451462012-10-30 11:22:58 -0400195 setAlpha(1f);
Jim Miller0b728242012-10-28 19:42:30 -0700196 mShowingMessage = true;
197 update();
198 mHandler.removeCallbacks(mClearMessageRunnable);
199 if (mTimeout > 0) {
200 mHandler.postDelayed(mClearMessageRunnable, mTimeout);
201 }
Alan Viveretteb428b0f2013-02-07 12:19:15 -0800202 mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
203 mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
204 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
Jim Miller0b728242012-10-28 19:42:30 -0700205 }
206
207 /**
208 * Update the status lines based on these rules:
209 * AlarmStatus: Alarm state always gets it's own line.
210 * Status1 is shared between help, battery status and generic unlock instructions,
211 * prioritized in that order.
212 * @param showStatusLines status lines are shown if true
213 */
214 void update() {
215 MutableInt icon = new MutableInt(0);
216 CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
217 setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
218 setText(status);
219 }
220
Jim Millere9be1402012-11-01 16:14:20 -0700221 private CharSequence concat(CharSequence... args) {
Jim Miller0b728242012-10-28 19:42:30 -0700222 StringBuilder b = new StringBuilder();
Jim Millere9be1402012-11-01 16:14:20 -0700223 if (!TextUtils.isEmpty(args[0])) {
224 b.append(args[0]);
225 }
226 for (int i = 1; i < args.length; i++) {
227 CharSequence text = args[i];
228 if (!TextUtils.isEmpty(text)) {
229 if (b.length() > 0) {
230 b.append(mSeparator);
231 }
232 b.append(text);
Jim Miller0b728242012-10-28 19:42:30 -0700233 }
234 }
235 return b.toString();
236 }
237
Jim Miller0b728242012-10-28 19:42:30 -0700238 CharSequence getCurrentMessage() {
239 return mShowingMessage ? mMessage : null;
240 }
241
242 String getOwnerInfo() {
243 ContentResolver res = getContext().getContentResolver();
Jim Miller187ec582013-04-15 18:27:54 -0700244 String info = null;
245 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
246 if (ownerInfoEnabled && !mShowingMessage) {
247 info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
248 }
249 return info;
Jim Miller0b728242012-10-28 19:42:30 -0700250 }
251
252 private CharSequence getChargeInfo(MutableInt icon) {
253 CharSequence string = null;
254 if (mShowingBatteryInfo && !mShowingMessage) {
255 // Battery status
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500256 if (mCharging) {
Jim Miller0b728242012-10-28 19:42:30 -0700257 // Charging, charged or waiting to charge.
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500258 string = getContext().getString(mBatteryCharged
Jim Miller5ecd8112013-01-09 18:50:26 -0800259 ? R.string.keyguard_charged
260 : R.string.keyguard_plugged_in, mBatteryLevel);
Jim Miller0b728242012-10-28 19:42:30 -0700261 icon.value = CHARGING_ICON;
262 } else if (mBatteryIsLow) {
263 // Battery is low
Jim Miller5ecd8112013-01-09 18:50:26 -0800264 string = getContext().getString(R.string.keyguard_low_battery);
Jim Miller0b728242012-10-28 19:42:30 -0700265 icon.value = BATTERY_LOW_ICON;
266 }
267 }
268 return string;
269 }
270
Chris Wrenc3451462012-10-30 11:22:58 -0400271 private void hideMessage(int duration, boolean thenUpdate) {
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500272 if (duration > 0) {
273 Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
274 anim.setDuration(duration);
275 if (thenUpdate) {
276 anim.addListener(new AnimatorListenerAdapter() {
277 @Override
278 public void onAnimationEnd(Animator animation) {
279 update();
280 }
Chris Wrenc3451462012-10-30 11:22:58 -0400281 });
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500282 }
283 anim.start();
284 } else {
285 setAlpha(0f);
286 if (thenUpdate) {
287 update();
288 }
Chris Wrenc3451462012-10-30 11:22:58 -0400289 }
Chris Wrenc3451462012-10-30 11:22:58 -0400290 }
291
292 private void showMessage(int duration) {
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500293 if (duration > 0) {
294 Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
295 anim.setDuration(duration);
296 anim.start();
297 } else {
298 setAlpha(1f);
299 }
Chris Wrenc3451462012-10-30 11:22:58 -0400300 }
Alan Viveretteb428b0f2013-02-07 12:19:15 -0800301
302 /**
303 * Runnable used to delay accessibility announcements.
304 */
305 private static class AnnounceRunnable implements Runnable {
306 private final WeakReference<View> mHost;
307 private final CharSequence mTextToAnnounce;
308
309 public AnnounceRunnable(View host, CharSequence textToAnnounce) {
310 mHost = new WeakReference<View>(host);
311 mTextToAnnounce = textToAnnounce;
312 }
313
314 @Override
315 public void run() {
316 final View host = mHost.get();
317 if (host != null) {
318 host.announceForAccessibility(mTextToAnnounce);
319 }
320 }
321 }
Jim Miller0b728242012-10-28 19:42:30 -0700322}