PowerUI.java revision de84f0e77ea2bf713d15c290264059a413c2486a
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
17package com.android.systemui.power;
18
19import 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;
26import android.media.AudioManager;
27import android.media.Ringtone;
28import android.media.RingtoneManager;
29import android.net.Uri;
30import android.os.BatteryManager;
31import android.os.Handler;
32import android.os.UserHandle;
33import android.provider.Settings;
34import android.util.Log;
35import android.view.View;
36import android.view.WindowManager;
37import android.widget.TextView;
38
39import com.android.systemui.R;
40import com.android.systemui.SystemUI;
41
42import java.io.FileDescriptor;
43import java.io.PrintWriter;
44import java.util.Arrays;
45
46public class PowerUI extends SystemUI {
47    static final String TAG = "PowerUI";
48
49    static final boolean DEBUG = false;
50
51    Handler mHandler = new Handler();
52
53    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;
62    AlertDialog mLowBatteryDialog;
63    TextView mBatteryLevelTextView;
64
65    public void start() {
66
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
74        // Register for Intent broadcasts for...
75        IntentFilter filter = new IntentFilter();
76        filter.addAction(Intent.ACTION_BATTERY_CHANGED);
77        filter.addAction(Intent.ACTION_POWER_CONNECTED);
78        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
79    }
80
81    /**
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
107    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)) {
112                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
128                if (DEBUG) {
129                    Log.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
130                            + " .. " + mLowBatteryReminderLevels[0]
131                            + " .. " + mLowBatteryReminderLevels[1]);
132                    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);
138                }
139
140                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
141                    Log.d(TAG, "showing invalid charger warning");
142                    showInvalidChargerDialog();
143                    return;
144                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
145                    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) {
155                    showLowBatteryWarning();
156
157                    // only play SFX when the dialog comes up or the bucket changes
158                    if (bucket != oldBucket || oldPlugged) {
159                        playLowBatterySound();
160                    }
161                } else if (plugged || (bucket > oldBucket && bucket > 0)) {
162                    dismissLowBatteryWarning();
163                } else if (mBatteryLevelTextView != null) {
164                    showLowBatteryWarning();
165                }
166            } else {
167                Log.w(TAG, "unknown intent: " + intent);
168            }
169        }
170    };
171
172    void dismissLowBatteryWarning() {
173        if (mLowBatteryDialog != null) {
174            Log.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
175            mLowBatteryDialog.dismiss();
176        }
177    }
178
179    void showLowBatteryWarning() {
180        Log.i(TAG,
181                ((mBatteryLevelTextView == null) ? "showing" : "updating")
182                + " low battery warning: level=" + mBatteryLevel
183                + " [" + findBatteryLevelBucket(mBatteryLevel) + "]");
184
185        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);
200                b.setIconAttribute(android.R.attr.alertDialogIcon);
201                b.setPositiveButton(android.R.string.ok, null);
202
203            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() {
211                    @Override
212                    public void onClick(DialogInterface dialog, int which) {
213                        mContext.startActivityAsUser(intent, UserHandle.CURRENT);
214                        dismissLowBatteryWarning();
215                    }
216                });
217            }
218
219            AlertDialog d = b.create();
220            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
221                    @Override
222                    public void onDismiss(DialogInterface dialog) {
223                        mLowBatteryDialog = null;
224                        mBatteryLevelTextView = null;
225                    }
226                });
227            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
228            d.getWindow().getAttributes().privateFlags |=
229                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
230            d.show();
231            mLowBatteryDialog = d;
232        }
233    }
234
235    void playLowBatterySound() {
236        if (DEBUG) {
237            Log.i(TAG, "playing low battery sound. WOMP-WOMP!");
238        }
239
240        final ContentResolver cr = mContext.getContentResolver();
241        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);
244            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) {
249                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
250                        sfx.play();
251                    }
252                }
253            }
254        }
255    }
256
257    void dismissInvalidChargerDialog() {
258        if (mInvalidChargerDialog != null) {
259            mInvalidChargerDialog.dismiss();
260        }
261    }
262
263    void showInvalidChargerDialog() {
264        Log.d(TAG, "showing invalid charger dialog");
265
266        dismissLowBatteryWarning();
267
268        AlertDialog.Builder b = new AlertDialog.Builder(mContext);
269            b.setCancelable(true);
270            b.setMessage(R.string.invalid_charger);
271            b.setIconAttribute(android.R.attr.alertDialogIcon);
272            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    }
286
287    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
288        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)));
306    }
307}
308
309