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