blob: ad59c0267ce7a030ab3644d615f9441b4d226e43 [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;
Jim Miller187ec582013-04-15 18:27:54 -070026import android.os.IBinder;
Jim Miller0b728242012-10-28 19:42:30 -070027import android.os.Looper;
Jim Miller187ec582013-04-15 18:27:54 -070028import android.os.RemoteException;
29import android.os.ServiceManager;
Alan Viveretteb428b0f2013-02-07 12:19:15 -080030import android.os.SystemClock;
Jim Miller0b728242012-10-28 19:42:30 -070031import android.os.UserHandle;
32import android.provider.Settings;
33import android.text.TextUtils;
34import android.util.AttributeSet;
Jim Miller187ec582013-04-15 18:27:54 -070035import android.util.Slog;
Jim Miller0b728242012-10-28 19:42:30 -070036import android.view.View;
37import android.widget.TextView;
38
39import libcore.util.MutableInt;
40
Alan Viveretteb428b0f2013-02-07 12:19:15 -080041import java.lang.ref.WeakReference;
42
Jim Miller187ec582013-04-15 18:27:54 -070043import com.android.internal.widget.LockPatternUtils;
Jim Miller0b728242012-10-28 19:42:30 -070044
45/***
46 * Manages a number of views inside of the given layout. See below for a list of widgets.
47 */
48class KeyguardMessageArea extends TextView {
Alan Viveretteb428b0f2013-02-07 12:19:15 -080049 /** Handler token posted with accessibility announcement runnables. */
50 private static final Object ANNOUNCE_TOKEN = new Object();
51
52 /**
53 * Delay before speaking an accessibility announcement. Used to prevent
54 * lift-to-type from interrupting itself.
55 */
56 private static final long ANNOUNCEMENT_DELAY = 250;
57
Jim Miller0b728242012-10-28 19:42:30 -070058 static final int CHARGING_ICON = 0; //R.drawable.ic_lock_idle_charging;
59 static final int BATTERY_LOW_ICON = 0; //R.drawable.ic_lock_idle_low_battery;
60
61 static final int SECURITY_MESSAGE_DURATION = 5000;
Chris Wrenc3451462012-10-30 11:22:58 -040062 protected static final int FADE_DURATION = 750;
Jim Miller0b728242012-10-28 19:42:30 -070063
Jim Miller187ec582013-04-15 18:27:54 -070064 private static final String TAG = "KeyguardMessageArea";
65
Jim Miller0b728242012-10-28 19:42:30 -070066 // are we showing battery information?
67 boolean mShowingBatteryInfo = false;
68
Chris Wrenc3451462012-10-30 11:22:58 -040069 // is the bouncer up?
70 boolean mShowingBouncer = false;
71
Jim Miller0b728242012-10-28 19:42:30 -070072 // last known plugged in state
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -050073 boolean mCharging = false;
Jim Miller0b728242012-10-28 19:42:30 -070074
75 // last known battery level
76 int mBatteryLevel = 100;
77
78 KeyguardUpdateMonitor mUpdateMonitor;
79
80 // Timeout before we reset the message to show charging/owner info
81 long mTimeout = SECURITY_MESSAGE_DURATION;
82
83 // Shadowed text values
84 protected boolean mBatteryCharged;
85 protected boolean mBatteryIsLow;
86
87 private Handler mHandler;
88
89 CharSequence mMessage;
90 boolean mShowingMessage;
Jim Miller187ec582013-04-15 18:27:54 -070091 private CharSequence mSeparator;
92 private LockPatternUtils mLockPatternUtils;
93
Jim Miller0b728242012-10-28 19:42:30 -070094 Runnable mClearMessageRunnable = new Runnable() {
95 @Override
96 public void run() {
97 mMessage = null;
98 mShowingMessage = false;
Chris Wrenc3451462012-10-30 11:22:58 -040099 if (mShowingBouncer) {
100 hideMessage(FADE_DURATION, true);
101 } else {
102 update();
103 }
Jim Miller0b728242012-10-28 19:42:30 -0700104 }
105 };
106
107 public static class Helper implements SecurityMessageDisplay {
108 KeyguardMessageArea mMessageArea;
109 Helper(View v) {
110 mMessageArea = (KeyguardMessageArea) v.findViewById(R.id.keyguard_message_area);
111 if (mMessageArea == null) {
112 throw new RuntimeException("Can't find keyguard_message_area in " + v.getClass());
113 }
114 }
115
116 public void setMessage(CharSequence msg, boolean important) {
117 if (!TextUtils.isEmpty(msg) && important) {
118 mMessageArea.mMessage = msg;
119 mMessageArea.securityMessageChanged();
120 }
121 }
122
123 public void setMessage(int resId, boolean important) {
124 if (resId != 0 && important) {
125 mMessageArea.mMessage = mMessageArea.getContext().getResources().getText(resId);
126 mMessageArea.securityMessageChanged();
127 }
128 }
129
130 public void setMessage(int resId, boolean important, Object... formatArgs) {
131 if (resId != 0 && important) {
132 mMessageArea.mMessage = mMessageArea.getContext().getString(resId, formatArgs);
133 mMessageArea.securityMessageChanged();
134 }
135 }
136
137 @Override
Chris Wrenc3451462012-10-30 11:22:58 -0400138 public void showBouncer(int duration) {
139 mMessageArea.hideMessage(duration, false);
140 mMessageArea.mShowingBouncer = true;
141 }
142
143 @Override
144 public void hideBouncer(int duration) {
145 mMessageArea.showMessage(duration);
146 mMessageArea.mShowingBouncer = false;
147 }
148
149 @Override
Jim Miller0b728242012-10-28 19:42:30 -0700150 public void setTimeout(int timeoutMs) {
151 mMessageArea.mTimeout = timeoutMs;
152 }
153 }
154
155 private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
156 @Override
157 public void onRefreshBatteryInfo(KeyguardUpdateMonitor.BatteryStatus status) {
158 mShowingBatteryInfo = status.isPluggedIn() || status.isBatteryLow();
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500159 mCharging = status.status == BatteryManager.BATTERY_STATUS_CHARGING
160 || status.status == BatteryManager.BATTERY_STATUS_FULL;
Jim Miller0b728242012-10-28 19:42:30 -0700161 mBatteryLevel = status.level;
162 mBatteryCharged = status.isCharged();
163 mBatteryIsLow = status.isBatteryLow();
164 update();
165 }
166 };
167
168 public KeyguardMessageArea(Context context) {
169 this(context, null);
170 }
171
172 public KeyguardMessageArea(Context context, AttributeSet attrs) {
173 super(context, attrs);
174
Jim Miller187ec582013-04-15 18:27:54 -0700175 mLockPatternUtils = new LockPatternUtils(context);
176
Jim Miller0b728242012-10-28 19:42:30 -0700177 // This is required to ensure marquee works
178 setSelected(true);
179
180 // Registering this callback immediately updates the battery state, among other things.
181 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(getContext());
182 mUpdateMonitor.registerCallback(mInfoCallback);
183 mHandler = new Handler(Looper.myLooper());
184
Jim Millere9be1402012-11-01 16:14:20 -0700185 mSeparator = getResources().getString(R.string.kg_text_message_separator);
186
Jim Miller0b728242012-10-28 19:42:30 -0700187 update();
188 }
189
190 public void securityMessageChanged() {
Chris Wrenc3451462012-10-30 11:22:58 -0400191 setAlpha(1f);
Jim Miller0b728242012-10-28 19:42:30 -0700192 mShowingMessage = true;
193 update();
194 mHandler.removeCallbacks(mClearMessageRunnable);
195 if (mTimeout > 0) {
196 mHandler.postDelayed(mClearMessageRunnable, mTimeout);
197 }
Alan Viveretteb428b0f2013-02-07 12:19:15 -0800198 mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
199 mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
200 (SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
Jim Miller0b728242012-10-28 19:42:30 -0700201 }
202
203 /**
204 * Update the status lines based on these rules:
205 * AlarmStatus: Alarm state always gets it's own line.
206 * Status1 is shared between help, battery status and generic unlock instructions,
207 * prioritized in that order.
208 * @param showStatusLines status lines are shown if true
209 */
210 void update() {
211 MutableInt icon = new MutableInt(0);
212 CharSequence status = concat(getChargeInfo(icon), getOwnerInfo(), getCurrentMessage());
213 setCompoundDrawablesWithIntrinsicBounds(icon.value, 0, 0, 0);
214 setText(status);
215 }
216
Jim Millere9be1402012-11-01 16:14:20 -0700217 private CharSequence concat(CharSequence... args) {
Jim Miller0b728242012-10-28 19:42:30 -0700218 StringBuilder b = new StringBuilder();
Jim Millere9be1402012-11-01 16:14:20 -0700219 if (!TextUtils.isEmpty(args[0])) {
220 b.append(args[0]);
221 }
222 for (int i = 1; i < args.length; i++) {
223 CharSequence text = args[i];
224 if (!TextUtils.isEmpty(text)) {
225 if (b.length() > 0) {
226 b.append(mSeparator);
227 }
228 b.append(text);
Jim Miller0b728242012-10-28 19:42:30 -0700229 }
230 }
231 return b.toString();
232 }
233
Jim Miller0b728242012-10-28 19:42:30 -0700234 CharSequence getCurrentMessage() {
235 return mShowingMessage ? mMessage : null;
236 }
237
238 String getOwnerInfo() {
239 ContentResolver res = getContext().getContentResolver();
Jim Miller187ec582013-04-15 18:27:54 -0700240 String info = null;
241 final boolean ownerInfoEnabled = mLockPatternUtils.isOwnerInfoEnabled();
242 if (ownerInfoEnabled && !mShowingMessage) {
243 info = mLockPatternUtils.getOwnerInfo(mLockPatternUtils.getCurrentUser());
244 }
245 return info;
Jim Miller0b728242012-10-28 19:42:30 -0700246 }
247
248 private CharSequence getChargeInfo(MutableInt icon) {
249 CharSequence string = null;
250 if (mShowingBatteryInfo && !mShowingMessage) {
251 // Battery status
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500252 if (mCharging) {
Jim Miller0b728242012-10-28 19:42:30 -0700253 // Charging, charged or waiting to charge.
Daniel Sandlerfe0e1e42012-11-29 15:11:50 -0500254 string = getContext().getString(mBatteryCharged
Jim Miller5ecd8112013-01-09 18:50:26 -0800255 ? R.string.keyguard_charged
256 : R.string.keyguard_plugged_in, mBatteryLevel);
Jim Miller0b728242012-10-28 19:42:30 -0700257 icon.value = CHARGING_ICON;
258 } else if (mBatteryIsLow) {
259 // Battery is low
Jim Miller5ecd8112013-01-09 18:50:26 -0800260 string = getContext().getString(R.string.keyguard_low_battery);
Jim Miller0b728242012-10-28 19:42:30 -0700261 icon.value = BATTERY_LOW_ICON;
262 }
263 }
264 return string;
265 }
266
Chris Wrenc3451462012-10-30 11:22:58 -0400267 private void hideMessage(int duration, boolean thenUpdate) {
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500268 if (duration > 0) {
269 Animator anim = ObjectAnimator.ofFloat(this, "alpha", 0f);
270 anim.setDuration(duration);
271 if (thenUpdate) {
272 anim.addListener(new AnimatorListenerAdapter() {
273 @Override
274 public void onAnimationEnd(Animator animation) {
275 update();
276 }
Chris Wrenc3451462012-10-30 11:22:58 -0400277 });
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500278 }
279 anim.start();
280 } else {
281 setAlpha(0f);
282 if (thenUpdate) {
283 update();
284 }
Chris Wrenc3451462012-10-30 11:22:58 -0400285 }
Chris Wrenc3451462012-10-30 11:22:58 -0400286 }
287
288 private void showMessage(int duration) {
Chris Wrenc0ae9e62012-11-05 13:16:31 -0500289 if (duration > 0) {
290 Animator anim = ObjectAnimator.ofFloat(this, "alpha", 1f);
291 anim.setDuration(duration);
292 anim.start();
293 } else {
294 setAlpha(1f);
295 }
Chris Wrenc3451462012-10-30 11:22:58 -0400296 }
Alan Viveretteb428b0f2013-02-07 12:19:15 -0800297
298 /**
299 * Runnable used to delay accessibility announcements.
300 */
301 private static class AnnounceRunnable implements Runnable {
302 private final WeakReference<View> mHost;
303 private final CharSequence mTextToAnnounce;
304
305 public AnnounceRunnable(View host, CharSequence textToAnnounce) {
306 mHost = new WeakReference<View>(host);
307 mTextToAnnounce = textToAnnounce;
308 }
309
310 @Override
311 public void run() {
312 final View host = mHost.get();
313 if (host != null) {
314 host.announceForAccessibility(mTextToAnnounce);
315 }
316 }
317 }
Jim Miller0b728242012-10-28 19:42:30 -0700318}