• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.power;
18 
19 import java.io.FileDescriptor;
20 import java.io.PrintWriter;
21 import java.util.Arrays;
22 
23 import android.app.AlertDialog;
24 import android.content.BroadcastReceiver;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.net.Uri;
31 import android.os.BatteryManager;
32 import android.os.Handler;
33 import android.media.AudioManager;
34 import android.media.Ringtone;
35 import android.media.RingtoneManager;
36 import android.provider.Settings;
37 import android.util.Slog;
38 import android.view.View;
39 import android.view.WindowManager;
40 import android.widget.TextView;
41 
42 import com.android.systemui.R;
43 import com.android.systemui.SystemUI;
44 
45 public class PowerUI extends SystemUI {
46     static final String TAG = "PowerUI";
47 
48     static final boolean DEBUG = false;
49 
50     Handler mHandler = new Handler();
51 
52     int mBatteryLevel = 100;
53     int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
54     int mPlugType = 0;
55     int mInvalidCharger = 0;
56 
57     int mLowBatteryAlertCloseLevel;
58     int[] mLowBatteryReminderLevels = new int[2];
59 
60     AlertDialog mInvalidChargerDialog;
61     AlertDialog mLowBatteryDialog;
62     TextView mBatteryLevelTextView;
63 
start()64     public void start() {
65 
66         mLowBatteryAlertCloseLevel = mContext.getResources().getInteger(
67                 com.android.internal.R.integer.config_lowBatteryCloseWarningLevel);
68         mLowBatteryReminderLevels[0] = mContext.getResources().getInteger(
69                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
70         mLowBatteryReminderLevels[1] = mContext.getResources().getInteger(
71                 com.android.internal.R.integer.config_criticalBatteryWarningLevel);
72 
73         // Register for Intent broadcasts for...
74         IntentFilter filter = new IntentFilter();
75         filter.addAction(Intent.ACTION_BATTERY_CHANGED);
76         filter.addAction(Intent.ACTION_POWER_CONNECTED);
77         mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
78     }
79 
80     /**
81      * Buckets the battery level.
82      *
83      * The code in this function is a little weird because I couldn't comprehend
84      * the bucket going up when the battery level was going down. --joeo
85      *
86      * 1 means that the battery is "ok"
87      * 0 means that the battery is between "ok" and what we should warn about.
88      * less than 0 means that the battery is low
89      */
findBatteryLevelBucket(int level)90     private int findBatteryLevelBucket(int level) {
91         if (level >= mLowBatteryAlertCloseLevel) {
92             return 1;
93         }
94         if (level >= mLowBatteryReminderLevels[0]) {
95             return 0;
96         }
97         final int N = mLowBatteryReminderLevels.length;
98         for (int i=N-1; i>=0; i--) {
99             if (level <= mLowBatteryReminderLevels[i]) {
100                 return -1-i;
101             }
102         }
103         throw new RuntimeException("not possible!");
104     }
105 
106     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
107         @Override
108         public void onReceive(Context context, Intent intent) {
109             String action = intent.getAction();
110             if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
111                 final int oldBatteryLevel = mBatteryLevel;
112                 mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
113                 final int oldBatteryStatus = mBatteryStatus;
114                 mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
115                         BatteryManager.BATTERY_STATUS_UNKNOWN);
116                 final int oldPlugType = mPlugType;
117                 mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
118                 final int oldInvalidCharger = mInvalidCharger;
119                 mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
120 
121                 final boolean plugged = mPlugType != 0;
122                 final boolean oldPlugged = oldPlugType != 0;
123 
124                 int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
125                 int bucket = findBatteryLevelBucket(mBatteryLevel);
126 
127                 if (DEBUG) {
128                     Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
129                             + " .. " + mLowBatteryReminderLevels[0]
130                             + " .. " + mLowBatteryReminderLevels[1]);
131                     Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
132                     Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
133                     Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
134                     Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
135                     Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
136                     Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
137                 }
138 
139                 if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
140                     Slog.d(TAG, "showing invalid charger warning");
141                     showInvalidChargerDialog();
142                     return;
143                 } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
144                     dismissInvalidChargerDialog();
145                 } else if (mInvalidChargerDialog != null) {
146                     // if invalid charger is showing, don't show low battery
147                     return;
148                 }
149 
150                 if (!plugged
151                         && (bucket < oldBucket || oldPlugged)
152                         && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
153                         && bucket < 0) {
154                     showLowBatteryWarning();
155 
156                     // only play SFX when the dialog comes up or the bucket changes
157                     if (bucket != oldBucket || oldPlugged) {
158                         playLowBatterySound();
159                     }
160                 } else if (plugged || (bucket > oldBucket && bucket > 0)) {
161                     dismissLowBatteryWarning();
162                 } else if (mBatteryLevelTextView != null) {
163                     showLowBatteryWarning();
164                 }
165             } else {
166                 Slog.w(TAG, "unknown intent: " + intent);
167             }
168         }
169     };
170 
dismissLowBatteryWarning()171     void dismissLowBatteryWarning() {
172         if (mLowBatteryDialog != null) {
173             Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
174             mLowBatteryDialog.dismiss();
175         }
176     }
177 
showLowBatteryWarning()178     void showLowBatteryWarning() {
179         Slog.i(TAG,
180                 ((mBatteryLevelTextView == null) ? "showing" : "updating")
181                 + " low battery warning: level=" + mBatteryLevel
182                 + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");
183 
184         CharSequence levelText = mContext.getString(
185                 R.string.battery_low_percent_format, mBatteryLevel);
186 
187         if (mBatteryLevelTextView != null) {
188             mBatteryLevelTextView.setText(levelText);
189         } else {
190             View v = View.inflate(mContext, R.layout.battery_low, null);
191             mBatteryLevelTextView = (TextView)v.findViewById(R.id.level_percent);
192 
193             mBatteryLevelTextView.setText(levelText);
194 
195             AlertDialog.Builder b = new AlertDialog.Builder(mContext);
196                 b.setCancelable(true);
197                 b.setTitle(R.string.battery_low_title);
198                 b.setView(v);
199                 b.setIconAttribute(android.R.attr.alertDialogIcon);
200                 b.setPositiveButton(android.R.string.ok, null);
201 
202             final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
203             intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
204                     | Intent.FLAG_ACTIVITY_MULTIPLE_TASK
205                     | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
206                     | Intent.FLAG_ACTIVITY_NO_HISTORY);
207             if (intent.resolveActivity(mContext.getPackageManager()) != null) {
208                 b.setNegativeButton(R.string.battery_low_why,
209                         new DialogInterface.OnClickListener() {
210                     public void onClick(DialogInterface dialog, int which) {
211                         mContext.startActivity(intent);
212                         dismissLowBatteryWarning();
213                     }
214                 });
215             }
216 
217             AlertDialog d = b.create();
218             d.setOnDismissListener(new DialogInterface.OnDismissListener() {
219                     public void onDismiss(DialogInterface dialog) {
220                         mLowBatteryDialog = null;
221                         mBatteryLevelTextView = null;
222                     }
223                 });
224             d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
225             d.show();
226             mLowBatteryDialog = d;
227         }
228     }
229 
playLowBatterySound()230     void playLowBatterySound() {
231         if (DEBUG) {
232             Slog.i(TAG, "playing low battery sound. WOMP-WOMP!");
233         }
234 
235         final ContentResolver cr = mContext.getContentResolver();
236         if (Settings.System.getInt(cr, Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) {
237             final String soundPath = Settings.System.getString(cr,
238                     Settings.System.LOW_BATTERY_SOUND);
239             if (soundPath != null) {
240                 final Uri soundUri = Uri.parse("file://" + soundPath);
241                 if (soundUri != null) {
242                     final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
243                     if (sfx != null) {
244                         sfx.setStreamType(AudioManager.STREAM_NOTIFICATION);
245                         sfx.play();
246                     }
247                 }
248             }
249         }
250     }
251 
dismissInvalidChargerDialog()252     void dismissInvalidChargerDialog() {
253         if (mInvalidChargerDialog != null) {
254             mInvalidChargerDialog.dismiss();
255         }
256     }
257 
showInvalidChargerDialog()258     void showInvalidChargerDialog() {
259         Slog.d(TAG, "showing invalid charger dialog");
260 
261         dismissLowBatteryWarning();
262 
263         AlertDialog.Builder b = new AlertDialog.Builder(mContext);
264             b.setCancelable(true);
265             b.setMessage(R.string.invalid_charger);
266             b.setIconAttribute(android.R.attr.alertDialogIcon);
267             b.setPositiveButton(android.R.string.ok, null);
268 
269         AlertDialog d = b.create();
270             d.setOnDismissListener(new DialogInterface.OnDismissListener() {
271                     public void onDismiss(DialogInterface dialog) {
272                         mInvalidChargerDialog = null;
273                         mBatteryLevelTextView = null;
274                     }
275                 });
276 
277         d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
278         d.show();
279         mInvalidChargerDialog = d;
280     }
281 
dump(FileDescriptor fd, PrintWriter pw, String[] args)282     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
283         pw.print("mLowBatteryAlertCloseLevel=");
284         pw.println(mLowBatteryAlertCloseLevel);
285         pw.print("mLowBatteryReminderLevels=");
286         pw.println(Arrays.toString(mLowBatteryReminderLevels));
287         pw.print("mInvalidChargerDialog=");
288         pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
289         pw.print("mLowBatteryDialog=");
290         pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
291         pw.print("mBatteryLevel=");
292         pw.println(Integer.toString(mBatteryLevel));
293         pw.print("mBatteryStatus=");
294         pw.println(Integer.toString(mBatteryStatus));
295         pw.print("mPlugType=");
296         pw.println(Integer.toString(mPlugType));
297         pw.print("mInvalidCharger=");
298         pw.println(Integer.toString(mInvalidCharger));
299         pw.print("bucket: ");
300         pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
301     }
302 }
303 
304