blob: a08eb9b0a7228214b1a1cb5e537e1c1742b02d1a [file] [log] [blame]
Joe Onorato10523b4d2010-10-25 10:42:46 -07001/*
2 * Copyright (C) 2008 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
17package com.android.systemui.power;
18
Joe Onorato10523b4d2010-10-25 10:42:46 -070019import android.app.AlertDialog;
20import android.content.BroadcastReceiver;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.Intent;
25import android.content.IntentFilter;
John Spurlockde84f0e2013-06-12 12:41:00 -040026import android.media.AudioManager;
27import android.media.Ringtone;
28import android.media.RingtoneManager;
Joe Onorato10523b4d2010-10-25 10:42:46 -070029import android.net.Uri;
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070030import android.os.BatteryManager;
Joe Onorato10523b4d2010-10-25 10:42:46 -070031import android.os.Handler;
John Spurlockf55769f2012-11-07 11:50:33 -050032import android.os.UserHandle;
Joe Onorato10523b4d2010-10-25 10:42:46 -070033import android.provider.Settings;
John Spurlockcd686b52013-06-05 10:13:46 -040034import android.util.Log;
Joe Onorato10523b4d2010-10-25 10:42:46 -070035import android.view.View;
36import android.view.WindowManager;
37import android.widget.TextView;
38
39import com.android.systemui.R;
40import com.android.systemui.SystemUI;
41
John Spurlockde84f0e2013-06-12 12:41:00 -040042import java.io.FileDescriptor;
43import java.io.PrintWriter;
44import java.util.Arrays;
45
Joe Onorato10523b4d2010-10-25 10:42:46 -070046public class PowerUI extends SystemUI {
47 static final String TAG = "PowerUI";
48
Daniel Sandler71986622011-07-26 13:06:49 -040049 static final boolean DEBUG = false;
50
Joe Onorato10523b4d2010-10-25 10:42:46 -070051 Handler mHandler = new Handler();
52
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070053 int mBatteryLevel = 100;
54 int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
55 int mPlugType = 0;
56 int mInvalidCharger = 0;
57
58 int mLowBatteryAlertCloseLevel;
59 int[] mLowBatteryReminderLevels = new int[2];
60
61 AlertDialog mInvalidChargerDialog;
Joe Onorato10523b4d2010-10-25 10:42:46 -070062 AlertDialog mLowBatteryDialog;
Joe Onorato10523b4d2010-10-25 10:42:46 -070063 TextView mBatteryLevelTextView;
64
65 public void start() {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070066
67 mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
68 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
69 mLowBatteryReminderLevels[0] = mContext.getResources().getInteger(
70 com.android.internal.R.integer.config_lowBatteryWarningLevel);
71 mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
72 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
73
Joe Onorato10523b4d2010-10-25 10:42:46 -070074 // Register for Intent broadcasts for...
75 IntentFilter filter = new IntentFilter();
76 filter.addAction(Intent.ACTION_BATTERY_CHANGED);
Joe Onorato10523b4d2010-10-25 10:42:46 -070077 filter.addAction(Intent.ACTION_POWER_CONNECTED);
78 mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
79 }
80
Joe Onorato4ca7f1e2010-10-27 15:32:23 -070081 /**
82 * Buckets the battery level.
83 *
84 * The code in this function is a little weird because I couldn't comprehend
85 * the bucket going up when the battery level was going down. --joeo
86 *
87 * 1 means that the battery is "ok"
88 * 0 means that the battery is between "ok" and what we should warn about.
89 * less than 0 means that the battery is low
90 */
91 private int findBatteryLevelBucket(int level) {
92 if (level >= mLowBatteryAlertCloseLevel) {
93 return 1;
94 }
95 if (level >= mLowBatteryReminderLevels[0]) {
96 return 0;
97 }
98 final int N = mLowBatteryReminderLevels.length;
99 for (int i=N-1; i>=0; i--) {
100 if (level <= mLowBatteryReminderLevels[i]) {
101 return -1-i;
102 }
103 }
104 throw new RuntimeException("not possible!");
105 }
106
Joe Onorato10523b4d2010-10-25 10:42:46 -0700107 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
108 @Override
109 public void onReceive(Context context, Intent intent) {
110 String action = intent.getAction();
111 if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700112 final int oldBatteryLevel = mBatteryLevel;
113 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
114 final int oldBatteryStatus = mBatteryStatus;
115 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
116 BatteryManager.BATTERY_STATUS_UNKNOWN);
117 final int oldPlugType = mPlugType;
118 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
119 final int oldInvalidCharger = mInvalidCharger;
120 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
121
122 final boolean plugged = mPlugType != 0;
123 final boolean oldPlugged = oldPlugType != 0;
124
125 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
126 int bucket = findBatteryLevelBucket(mBatteryLevel);
127
Daniel Sandler71986622011-07-26 13:06:49 -0400128 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400129 Log.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700130 + " .. " + mLowBatteryReminderLevels[0]
131 + " .. " + mLowBatteryReminderLevels[1]);
John Spurlockcd686b52013-06-05 10:13:46 -0400132 Log.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
133 Log.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
134 Log.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
135 Log.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
136 Log.d(TAG, "bucket " + oldBucket + " --> " + bucket);
137 Log.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700138 }
139
140 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
John Spurlockcd686b52013-06-05 10:13:46 -0400141 Log.d(TAG, "showing invalid charger warning");
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700142 showInvalidChargerDialog();
143 return;
144 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700145 dismissInvalidChargerDialog();
146 } else if (mInvalidChargerDialog != null) {
147 // if invalid charger is showing, don't show low battery
148 return;
149 }
150
151 if (!plugged
152 && (bucket < oldBucket || oldPlugged)
153 && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
154 && bucket < 0) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700155 showLowBatteryWarning();
Daniel Sandler71986622011-07-26 13:06:49 -0400156
157 // only play SFX when the dialog comes up or the bucket changes
158 if (bucket != oldBucket || oldPlugged) {
159 playLowBatterySound();
160 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700161 } else if (plugged || (bucket > oldBucket && bucket > 0)) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700162 dismissLowBatteryWarning();
163 } else if (mBatteryLevelTextView != null) {
164 showLowBatteryWarning();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700165 }
166 } else {
John Spurlockcd686b52013-06-05 10:13:46 -0400167 Log.w(TAG, "unknown intent: " + intent);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700168 }
169 }
170 };
171
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700172 void dismissLowBatteryWarning() {
173 if (mLowBatteryDialog != null) {
John Spurlockcd686b52013-06-05 10:13:46 -0400174 Log.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700175 mLowBatteryDialog.dismiss();
176 }
177 }
178
Joe Onorato10523b4d2010-10-25 10:42:46 -0700179 void showLowBatteryWarning() {
John Spurlockcd686b52013-06-05 10:13:46 -0400180 Log.i(TAG,
Daniel Sandler71986622011-07-26 13:06:49 -0400181 ((mBatteryLevelTextView == null) ? "showing" : "updating")
182 + " low battery warning: level=" + mBatteryLevel
183 + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");
184
Joe Onorato10523b4d2010-10-25 10:42:46 -0700185 CharSequence levelText = mContext.getString(
186 R.string.battery_low_percent_format, mBatteryLevel);
187
188 if (mBatteryLevelTextView != null) {
189 mBatteryLevelTextView.setText(levelText);
190 } else {
191 View v = View.inflate(mContext, R.layout.battery_low, null);
192 mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
193
194 mBatteryLevelTextView.setText(levelText);
195
196 AlertDialog.Builder b = new AlertDialog.Builder(mContext);
197 b.setCancelable(true);
198 b.setTitle(R.string.battery_low_title);
199 b.setView(v);
Adam Powell947f7822011-01-07 22:30:48 -0800200 b.setIconAttribute(android.R.attr.alertDialogIcon);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700201 b.setPositiveButton(android.R.string.ok, null);
202
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700203 final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
204 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
205 | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
206 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
207 | Intent.FLAG_ACTIVITY_NO_HISTORY);
208 if (intent.resolveActivity(mContext.getPackageManager()) != null) {
209 b.setNegativeButton(R.string.battery_low_why,
210 new DialogInterface.OnClickListener() {
Craig Mautner88400d32012-09-30 12:35:45 -0700211 @Override
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700212 public void onClick(DialogInterface dialog, int which) {
John Spurlockf55769f2012-11-07 11:50:33 -0500213 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
Daniel Sandler71986622011-07-26 13:06:49 -0400214 dismissLowBatteryWarning();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700215 }
216 });
217 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700218
219 AlertDialog d = b.create();
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700220 d.setOnDismissListener(new DialogInterface.OnDismissListener() {
Craig Mautner88400d32012-09-30 12:35:45 -0700221 @Override
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700222 public void onDismiss(DialogInterface dialog) {
223 mLowBatteryDialog = null;
224 mBatteryLevelTextView = null;
225 }
226 });
Joe Onorato10523b4d2010-10-25 10:42:46 -0700227 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
Craig Mautner88400d32012-09-30 12:35:45 -0700228 d.getWindow().getAttributes().privateFlags |=
229 WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
Joe Onorato10523b4d2010-10-25 10:42:46 -0700230 d.show();
231 mLowBatteryDialog = d;
232 }
Daniel Sandler71986622011-07-26 13:06:49 -0400233 }
234
235 void playLowBatterySound() {
236 if (DEBUG) {
John Spurlockcd686b52013-06-05 10:13:46 -0400237 Log.i(TAG, "playing low battery sound. WOMP-WOMP!");
Daniel Sandler71986622011-07-26 13:06:49 -0400238 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700239
240 final ContentResolver cr = mContext.getContentResolver();
Jeff Sharkey8d9a1f62012-10-18 15:38:14 -0700241 if (Settings.Global.getInt(cr, Settings.Global.POWER_SOUNDS_ENABLED, 1) == 1) {
242 final String soundPath = Settings.Global.getString(cr,
243 Settings.Global.LOW_BATTERY_SOUND);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700244 if (soundPath != null) {
245 final Uri soundUri = Uri.parse("file://" + soundPath);
246 if (soundUri != null) {
247 final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
248 if (sfx != null) {
Eric Laurent6d517662012-04-23 18:42:39 -0700249 sfx.setStreamType(AudioManager.STREAM_SYSTEM);
Joe Onorato10523b4d2010-10-25 10:42:46 -0700250 sfx.play();
251 }
252 }
253 }
254 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700255 }
256
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700257 void dismissInvalidChargerDialog() {
258 if (mInvalidChargerDialog != null) {
259 mInvalidChargerDialog.dismiss();
Joe Onorato10523b4d2010-10-25 10:42:46 -0700260 }
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700261 }
Joe Onorato10523b4d2010-10-25 10:42:46 -0700262
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700263 void showInvalidChargerDialog() {
John Spurlockcd686b52013-06-05 10:13:46 -0400264 Log.d(TAG, "showing invalid charger dialog");
Daniel Sandler71986622011-07-26 13:06:49 -0400265
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700266 dismissLowBatteryWarning();
267
268 AlertDialog.Builder b = new AlertDialog.Builder(mContext);
269 b.setCancelable(true);
Joe Onoratoa9ad6b82010-10-30 12:13:13 -0700270 b.setMessage(R.string.invalid_charger);
Adam Powell947f7822011-01-07 22:30:48 -0800271 b.setIconAttribute(android.R.attr.alertDialogIcon);
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700272 b.setPositiveButton(android.R.string.ok, null);
273
274 AlertDialog d = b.create();
275 d.setOnDismissListener(new DialogInterface.OnDismissListener() {
276 public void onDismiss(DialogInterface dialog) {
277 mInvalidChargerDialog = null;
278 mBatteryLevelTextView = null;
279 }
280 });
281
282 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
283 d.show();
284 mInvalidChargerDialog = d;
285 }
John Spurlock209bede2013-07-17 12:23:27 -0400286
Joe Onorato10523b4d2010-10-25 10:42:46 -0700287 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Joe Onorato4ca7f1e2010-10-27 15:32:23 -0700288 pw.print("mLowBatteryAlertCloseLevel=");
289 pw.println(mLowBatteryAlertCloseLevel);
290 pw.print("mLowBatteryReminderLevels=");
291 pw.println(Arrays.toString(mLowBatteryReminderLevels));
292 pw.print("mInvalidChargerDialog=");
293 pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
294 pw.print("mLowBatteryDialog=");
295 pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
296 pw.print("mBatteryLevel=");
297 pw.println(Integer.toString(mBatteryLevel));
298 pw.print("mBatteryStatus=");
299 pw.println(Integer.toString(mBatteryStatus));
300 pw.print("mPlugType=");
301 pw.println(Integer.toString(mPlugType));
302 pw.print("mInvalidCharger=");
303 pw.println(Integer.toString(mInvalidCharger));
304 pw.print("bucket: ");
305 pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
Joe Onorato10523b4d2010-10-25 10:42:46 -0700306 }
307}
308