PowerUI.java revision 8de4311c51229efbe2f2d0afbf298982c5cadd96
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.content.BroadcastReceiver;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.database.ContentObserver;
25import android.os.BatteryManager;
26import android.os.Handler;
27import android.os.PowerManager;
28import android.os.SystemClock;
29import android.os.UserHandle;
30import android.provider.Settings;
31import android.util.Log;
32import android.util.Slog;
33
34import com.android.systemui.SystemUI;
35import com.android.systemui.statusbar.phone.PhoneStatusBar;
36
37import java.io.FileDescriptor;
38import java.io.PrintWriter;
39import java.util.Arrays;
40
41public class PowerUI extends SystemUI {
42    static final String TAG = "PowerUI";
43    static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
44
45    private final Handler mHandler = new Handler();
46    private final Receiver mReceiver = new Receiver();
47
48    private PowerManager mPowerManager;
49    private WarningsUI mWarnings;
50    private int mBatteryLevel = 100;
51    private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
52    private int mPlugType = 0;
53    private int mInvalidCharger = 0;
54
55    private int mLowBatteryAlertCloseLevel;
56    private final int[] mLowBatteryReminderLevels = new int[2];
57
58    private long mScreenOffTime = -1;
59
60    public void start() {
61        mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
62        mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
63        mWarnings = new PowerNotificationWarnings(mContext, getComponent(PhoneStatusBar.class));
64
65        ContentObserver obs = new ContentObserver(mHandler) {
66            @Override
67            public void onChange(boolean selfChange) {
68                updateBatteryWarningLevels();
69            }
70        };
71        final ContentResolver resolver = mContext.getContentResolver();
72        resolver.registerContentObserver(Settings.Global.getUriFor(
73                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
74                false, obs, UserHandle.USER_ALL);
75        updateBatteryWarningLevels();
76        mReceiver.init();
77    }
78
79    private void setSaverMode(boolean mode) {
80        mWarnings.showSaverMode(mode);
81    }
82
83    void updateBatteryWarningLevels() {
84        int critLevel = mContext.getResources().getInteger(
85                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
86
87        final ContentResolver resolver = mContext.getContentResolver();
88        int defWarnLevel = mContext.getResources().getInteger(
89                com.android.internal.R.integer.config_lowBatteryWarningLevel);
90        int warnLevel = Settings.Global.getInt(resolver,
91                Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
92        if (warnLevel == 0) {
93            warnLevel = defWarnLevel;
94        }
95        if (warnLevel < critLevel) {
96            warnLevel = critLevel;
97        }
98
99        mLowBatteryReminderLevels[0] = warnLevel;
100        mLowBatteryReminderLevels[1] = critLevel;
101        mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
102                + mContext.getResources().getInteger(
103                        com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
104    }
105
106    /**
107     * Buckets the battery level.
108     *
109     * The code in this function is a little weird because I couldn't comprehend
110     * the bucket going up when the battery level was going down. --joeo
111     *
112     * 1 means that the battery is "ok"
113     * 0 means that the battery is between "ok" and what we should warn about.
114     * less than 0 means that the battery is low
115     */
116    private int findBatteryLevelBucket(int level) {
117        if (level >= mLowBatteryAlertCloseLevel) {
118            return 1;
119        }
120        if (level > mLowBatteryReminderLevels[0]) {
121            return 0;
122        }
123        final int N = mLowBatteryReminderLevels.length;
124        for (int i=N-1; i>=0; i--) {
125            if (level <= mLowBatteryReminderLevels[i]) {
126                return -1-i;
127            }
128        }
129        throw new RuntimeException("not possible!");
130    }
131
132    private final class Receiver extends BroadcastReceiver {
133
134        public void init() {
135            // Register for Intent broadcasts for...
136            IntentFilter filter = new IntentFilter();
137            filter.addAction(Intent.ACTION_BATTERY_CHANGED);
138            filter.addAction(Intent.ACTION_SCREEN_OFF);
139            filter.addAction(Intent.ACTION_SCREEN_ON);
140            filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGING);
141            filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
142            mContext.registerReceiver(this, filter, null, mHandler);
143            updateSaverMode();
144        }
145
146        private void updateSaverMode() {
147            setSaverMode(mPowerManager.isPowerSaveMode());
148        }
149
150        @Override
151        public void onReceive(Context context, Intent intent) {
152            String action = intent.getAction();
153            if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
154                final int oldBatteryLevel = mBatteryLevel;
155                mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
156                final int oldBatteryStatus = mBatteryStatus;
157                mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
158                        BatteryManager.BATTERY_STATUS_UNKNOWN);
159                final int oldPlugType = mPlugType;
160                mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
161                final int oldInvalidCharger = mInvalidCharger;
162                mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
163
164                final boolean plugged = mPlugType != 0;
165                final boolean oldPlugged = oldPlugType != 0;
166
167                int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
168                int bucket = findBatteryLevelBucket(mBatteryLevel);
169
170                if (DEBUG) {
171                    Slog.d(TAG, "buckets   ....." + mLowBatteryAlertCloseLevel
172                            + " .. " + mLowBatteryReminderLevels[0]
173                            + " .. " + mLowBatteryReminderLevels[1]);
174                    Slog.d(TAG, "level          " + oldBatteryLevel + " --> " + mBatteryLevel);
175                    Slog.d(TAG, "status         " + oldBatteryStatus + " --> " + mBatteryStatus);
176                    Slog.d(TAG, "plugType       " + oldPlugType + " --> " + mPlugType);
177                    Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
178                    Slog.d(TAG, "bucket         " + oldBucket + " --> " + bucket);
179                    Slog.d(TAG, "plugged        " + oldPlugged + " --> " + plugged);
180                }
181
182                mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
183                if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
184                    Slog.d(TAG, "showing invalid charger warning");
185                    mWarnings.showInvalidChargerWarning();
186                    return;
187                } else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
188                    mWarnings.dismissInvalidChargerWarning();
189                } else if (mWarnings.isInvalidChargerWarningShowing()) {
190                    // if invalid charger is showing, don't show low battery
191                    return;
192                }
193
194                if (!plugged
195                        && (bucket < oldBucket || oldPlugged)
196                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
197                        && bucket < 0) {
198                    // only play SFX when the dialog comes up or the bucket changes
199                    final boolean playSound = bucket != oldBucket || oldPlugged;
200                    mWarnings.showLowBatteryWarning(playSound);
201                } else if (plugged || (bucket > oldBucket && bucket > 0)) {
202                    mWarnings.dismissLowBatteryWarning();
203                } else {
204                    mWarnings.updateLowBatteryWarning();
205                }
206            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
207                mScreenOffTime = SystemClock.elapsedRealtime();
208            } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
209                mScreenOffTime = -1;
210            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
211                updateSaverMode();
212            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGING.equals(action)) {
213                setSaverMode(intent.getBooleanExtra(PowerManager.EXTRA_POWER_SAVE_MODE, false));
214            } else {
215                Slog.w(TAG, "unknown intent: " + intent);
216            }
217        }
218    };
219
220    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
221        pw.print("mLowBatteryAlertCloseLevel=");
222        pw.println(mLowBatteryAlertCloseLevel);
223        pw.print("mLowBatteryReminderLevels=");
224        pw.println(Arrays.toString(mLowBatteryReminderLevels));
225        pw.print("mBatteryLevel=");
226        pw.println(Integer.toString(mBatteryLevel));
227        pw.print("mBatteryStatus=");
228        pw.println(Integer.toString(mBatteryStatus));
229        pw.print("mPlugType=");
230        pw.println(Integer.toString(mPlugType));
231        pw.print("mInvalidCharger=");
232        pw.println(Integer.toString(mInvalidCharger));
233        pw.print("mScreenOffTime=");
234        pw.print(mScreenOffTime);
235        if (mScreenOffTime >= 0) {
236            pw.print(" (");
237            pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
238            pw.print(" ago)");
239        }
240        pw.println();
241        pw.print("soundTimeout=");
242        pw.println(Settings.Global.getInt(mContext.getContentResolver(),
243                Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
244        pw.print("bucket: ");
245        pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
246        mWarnings.dump(pw);
247    }
248
249    public interface WarningsUI {
250        void update(int batteryLevel, int bucket, long screenOffTime);
251        void showSaverMode(boolean mode);
252        void dismissLowBatteryWarning();
253        void showLowBatteryWarning(boolean playSound);
254        void dismissInvalidChargerWarning();
255        void showInvalidChargerWarning();
256        void updateLowBatteryWarning();
257        boolean isInvalidChargerWarningShowing();
258        void dump(PrintWriter pw);
259    }
260}
261
262