PowerUI.java revision 88400d3a31139c40c4014faf86c243647087ef6c
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 java.io.FileDescriptor;
20import java.io.PrintWriter;
21import java.util.Arrays;
22
23import android.app.AlertDialog;
24import android.content.BroadcastReceiver;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.net.Uri;
31import android.os.BatteryManager;
32import android.os.Handler;
33import android.media.AudioManager;
34import android.media.Ringtone;
35import android.media.RingtoneManager;
36import android.provider.Settings;
37import android.util.Slog;
38import android.view.View;
39import android.view.WindowManager;
40import android.widget.TextView;
41
42import com.android.systemui.R;
43import com.android.systemui.SystemUI;
44
45public 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
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     */
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
171    void dismissLowBatteryWarning() {
172        if (mLowBatteryDialog != null) {
173            Slog.i(TAG, "closing low battery warning: level=" + mBatteryLevel);
174            mLowBatteryDialog.dismiss();
175        }
176    }
177
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                    @Override
211                    public void onClick(DialogInterface dialog, int which) {
212                        mContext.startActivity(intent);
213                        dismissLowBatteryWarning();
214                    }
215                });
216            }
217
218            AlertDialog d = b.create();
219            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
220                    @Override
221                    public void onDismiss(DialogInterface dialog) {
222                        mLowBatteryDialog = null;
223                        mBatteryLevelTextView = null;
224                    }
225                });
226            d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
227            d.getWindow().getAttributes().privateFlags |=
228                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
229            d.show();
230            mLowBatteryDialog = d;
231        }
232    }
233
234    void playLowBatterySound() {
235        if (DEBUG) {
236            Slog.i(TAG, "playing low battery sound. WOMP-WOMP!");
237        }
238
239        final ContentResolver cr = mContext.getContentResolver();
240        if (Settings.System.getInt(cr, Settings.System.POWER_SOUNDS_ENABLED, 1) == 1) {
241            final String soundPath = Settings.System.getString(cr,
242                    Settings.System.LOW_BATTERY_SOUND);
243            if (soundPath != null) {
244                final Uri soundUri = Uri.parse("file://" + soundPath);
245                if (soundUri != null) {
246                    final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
247                    if (sfx != null) {
248                        sfx.setStreamType(AudioManager.STREAM_SYSTEM);
249                        sfx.play();
250                    }
251                }
252            }
253        }
254    }
255
256    void dismissInvalidChargerDialog() {
257        if (mInvalidChargerDialog != null) {
258            mInvalidChargerDialog.dismiss();
259        }
260    }
261
262    void showInvalidChargerDialog() {
263        Slog.d(TAG, "showing invalid charger dialog");
264
265        dismissLowBatteryWarning();
266
267        AlertDialog.Builder b = new AlertDialog.Builder(mContext);
268            b.setCancelable(true);
269            b.setMessage(R.string.invalid_charger);
270            b.setIconAttribute(android.R.attr.alertDialogIcon);
271            b.setPositiveButton(android.R.string.ok, null);
272
273        AlertDialog d = b.create();
274            d.setOnDismissListener(new DialogInterface.OnDismissListener() {
275                    public void onDismiss(DialogInterface dialog) {
276                        mInvalidChargerDialog = null;
277                        mBatteryLevelTextView = null;
278                    }
279                });
280
281        d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
282        d.show();
283        mInvalidChargerDialog = d;
284    }
285
286    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
287        pw.print("mLowBatteryAlertCloseLevel=");
288        pw.println(mLowBatteryAlertCloseLevel);
289        pw.print("mLowBatteryReminderLevels=");
290        pw.println(Arrays.toString(mLowBatteryReminderLevels));
291        pw.print("mInvalidChargerDialog=");
292        pw.println(mInvalidChargerDialog == null ? "null" : mInvalidChargerDialog.toString());
293        pw.print("mLowBatteryDialog=");
294        pw.println(mLowBatteryDialog == null ? "null" : mLowBatteryDialog.toString());
295        pw.print("mBatteryLevel=");
296        pw.println(Integer.toString(mBatteryLevel));
297        pw.print("mBatteryStatus=");
298        pw.println(Integer.toString(mBatteryStatus));
299        pw.print("mPlugType=");
300        pw.println(Integer.toString(mPlugType));
301        pw.print("mInvalidCharger=");
302        pw.println(Integer.toString(mInvalidCharger));
303        pw.print("bucket: ");
304        pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
305    }
306}
307
308