blob: 1bb7edb5c1bc160c0c6d9da564618c52f46d0123 [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.power;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import com.android.systemui.SystemUI;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Arrays;
public class PowerUI extends SystemUI {
static final String TAG = "PowerUI";
static final boolean DEBUG = false;
private final Handler mHandler = new Handler();
private final SettingsObserver mObserver = new SettingsObserver(mHandler);
private final Receiver mReceiver = new Receiver();
private PowerManager mPowerManager;
private WarningsUI mWarnings;
private int mBatteryLevel = 100;
private int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN;
private int mPlugType = 0;
private int mInvalidCharger = 0;
private int mLowBatteryAlertCloseLevel;
private final int[] mLowBatteryReminderLevels = new int[2];
private long mScreenOffTime = -1;
public void start() {
mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mScreenOffTime = mPowerManager.isScreenOn() ? -1 : SystemClock.elapsedRealtime();
mWarnings = new PowerNotificationWarnings(mContext);
ContentObserver obs = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange) {
updateBatteryWarningLevels();
}
};
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevels();
mReceiver.init();
mObserver.init();
}
private void setSaverMode(boolean mode) {
mWarnings.showSaverMode(mode);
}
private void setSaverTrigger(int level) {
mWarnings.setSaverTrigger(level);
}
void updateBatteryWarningLevels() {
int critLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
final ContentResolver resolver = mContext.getContentResolver();
int defWarnLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
int warnLevel = Settings.Global.getInt(resolver,
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, defWarnLevel);
if (warnLevel == 0) {
warnLevel = defWarnLevel;
}
if (warnLevel < critLevel) {
warnLevel = critLevel;
}
mLowBatteryReminderLevels[0] = warnLevel;
mLowBatteryReminderLevels[1] = critLevel;
mLowBatteryAlertCloseLevel = mLowBatteryReminderLevels[0]
+ mContext.getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
}
/**
* Buckets the battery level.
*
* The code in this function is a little weird because I couldn't comprehend
* the bucket going up when the battery level was going down. --joeo
*
* 1 means that the battery is "ok"
* 0 means that the battery is between "ok" and what we should warn about.
* less than 0 means that the battery is low
*/
private int findBatteryLevelBucket(int level) {
if (level >= mLowBatteryAlertCloseLevel) {
return 1;
}
if (level > mLowBatteryReminderLevels[0]) {
return 0;
}
final int N = mLowBatteryReminderLevels.length;
for (int i=N-1; i>=0; i--) {
if (level <= mLowBatteryReminderLevels[i]) {
return -1-i;
}
}
throw new RuntimeException("not possible!");
}
private final class Receiver extends BroadcastReceiver {
public void init() {
// Register for Intent broadcasts for...
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED);
mContext.registerReceiver(this, filter, null, mHandler);
updateSaverMode();
}
private void updateSaverMode() {
setSaverMode(mPowerManager.isPowerSaveMode());
}
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
final int oldBatteryLevel = mBatteryLevel;
mBatteryLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 100);
final int oldBatteryStatus = mBatteryStatus;
mBatteryStatus = intent.getIntExtra(BatteryManager.EXTRA_STATUS,
BatteryManager.BATTERY_STATUS_UNKNOWN);
final int oldPlugType = mPlugType;
mPlugType = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 1);
final int oldInvalidCharger = mInvalidCharger;
mInvalidCharger = intent.getIntExtra(BatteryManager.EXTRA_INVALID_CHARGER, 0);
final boolean plugged = mPlugType != 0;
final boolean oldPlugged = oldPlugType != 0;
int oldBucket = findBatteryLevelBucket(oldBatteryLevel);
int bucket = findBatteryLevelBucket(mBatteryLevel);
if (DEBUG) {
Slog.d(TAG, "buckets ....." + mLowBatteryAlertCloseLevel
+ " .. " + mLowBatteryReminderLevels[0]
+ " .. " + mLowBatteryReminderLevels[1]);
Slog.d(TAG, "level " + oldBatteryLevel + " --> " + mBatteryLevel);
Slog.d(TAG, "status " + oldBatteryStatus + " --> " + mBatteryStatus);
Slog.d(TAG, "plugType " + oldPlugType + " --> " + mPlugType);
Slog.d(TAG, "invalidCharger " + oldInvalidCharger + " --> " + mInvalidCharger);
Slog.d(TAG, "bucket " + oldBucket + " --> " + bucket);
Slog.d(TAG, "plugged " + oldPlugged + " --> " + plugged);
}
mWarnings.update(mBatteryLevel, bucket, mScreenOffTime);
if (oldInvalidCharger == 0 && mInvalidCharger != 0) {
Slog.d(TAG, "showing invalid charger warning");
mWarnings.showInvalidChargerWarning();
return;
} else if (oldInvalidCharger != 0 && mInvalidCharger == 0) {
mWarnings.dismissInvalidChargerWarning();
} else if (mWarnings.isInvalidChargerWarningShowing()) {
// if invalid charger is showing, don't show low battery
return;
}
if (!plugged
&& (bucket < oldBucket || oldPlugged)
&& mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
&& bucket < 0) {
// only play SFX when the dialog comes up or the bucket changes
final boolean playSound = bucket != oldBucket || oldPlugged;
mWarnings.showLowBatteryWarning(playSound);
} else if (plugged || (bucket > oldBucket && bucket > 0)) {
mWarnings.dismissLowBatteryWarning();
} else {
mWarnings.updateLowBatteryWarning();
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
mScreenOffTime = SystemClock.elapsedRealtime();
} else if (Intent.ACTION_SCREEN_ON.equals(action)) {
mScreenOffTime = -1;
} else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)) {
updateSaverMode();
} else {
Slog.w(TAG, "unknown intent: " + intent);
}
}
};
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.print("mLowBatteryAlertCloseLevel=");
pw.println(mLowBatteryAlertCloseLevel);
pw.print("mLowBatteryReminderLevels=");
pw.println(Arrays.toString(mLowBatteryReminderLevels));
pw.print("mBatteryLevel=");
pw.println(Integer.toString(mBatteryLevel));
pw.print("mBatteryStatus=");
pw.println(Integer.toString(mBatteryStatus));
pw.print("mPlugType=");
pw.println(Integer.toString(mPlugType));
pw.print("mInvalidCharger=");
pw.println(Integer.toString(mInvalidCharger));
pw.print("mScreenOffTime=");
pw.print(mScreenOffTime);
if (mScreenOffTime >= 0) {
pw.print(" (");
pw.print(SystemClock.elapsedRealtime() - mScreenOffTime);
pw.print(" ago)");
}
pw.println();
pw.print("soundTimeout=");
pw.println(Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, 0));
pw.print("bucket: ");
pw.println(Integer.toString(findBatteryLevelBucket(mBatteryLevel)));
mWarnings.dump(pw);
}
public interface WarningsUI {
void update(int batteryLevel, int bucket, long screenOffTime);
void setSaverTrigger(int level);
void showSaverMode(boolean mode);
void dismissLowBatteryWarning();
void showLowBatteryWarning(boolean playSound);
void dismissInvalidChargerWarning();
void showInvalidChargerWarning();
void updateLowBatteryWarning();
boolean isInvalidChargerWarningShowing();
void dump(PrintWriter pw);
}
private final class SettingsObserver extends ContentObserver {
private final Uri LOW_POWER_MODE_TRIGGER_LEVEL_URI =
Settings.Global.getUriFor(Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL);
public SettingsObserver(Handler handler) {
super(handler);
}
public void init() {
onChange(true, LOW_POWER_MODE_TRIGGER_LEVEL_URI);
final ContentResolver cr = mContext.getContentResolver();
cr.registerContentObserver(LOW_POWER_MODE_TRIGGER_LEVEL_URI, false, this);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (LOW_POWER_MODE_TRIGGER_LEVEL_URI.equals(uri)) {
final int level = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0);
setSaverTrigger(level);
}
}
}
}