1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
|
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
public class GeckoBatteryManager extends BroadcastReceiver {
private static final String LOGTAG = "GeckoBatteryManager";
// Those constants should be keep in sync with the ones in:
// dom/battery/Constants.h
private final static double kDefaultLevel = 1.0;
private final static boolean kDefaultCharging = true;
private final static double kDefaultRemainingTime = 0.0;
private final static double kUnknownRemainingTime = -1.0;
private static long sLastLevelChange = 0;
private static boolean sNotificationsEnabled = false;
private static double sLevel = kDefaultLevel;
private static boolean sCharging = kDefaultCharging;
private static double sRemainingTime = kDefaultRemainingTime;
private static GeckoBatteryManager sInstance = new GeckoBatteryManager();
private IntentFilter mFilter;
private Context mApplicationContext;
private boolean mIsEnabled;
public static GeckoBatteryManager getInstance() {
return sInstance;
}
private GeckoBatteryManager() {
mFilter = new IntentFilter();
mFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
}
public void init(Context context) {
mApplicationContext = context.getApplicationContext();
}
public synchronized void start() {
if (!mIsEnabled) {
// registerReceiver will return null if registering fails
if (mApplicationContext.registerReceiver(this, mFilter) == null) {
Log.e(LOGTAG, "Registering receiver failed");
} else {
mIsEnabled = true;
}
}
}
public synchronized void stop() {
if (mIsEnabled) {
mApplicationContext.unregisterReceiver(this);
mIsEnabled = false;
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
Log.e(LOGTAG, "Got an unexpected intent!");
return;
}
boolean previousCharging = isCharging();
double previousLevel = getLevel();
// NOTE: it might not be common (in 2012) but technically, Android can run
// on a device that has no battery so we want to make sure it's not the case
// before bothering checking for battery state.
// However, the Galaxy Nexus phone advertizes itself as battery-less which
// force us to special-case the logic.
// See the Google bug: https://code.google.com/p/android/issues/detail?id=22035
if (intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false) ||
Build.MODEL.equals("Galaxy Nexus")) {
int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
if (plugged == -1) {
sCharging = kDefaultCharging;
Log.e(LOGTAG, "Failed to get the plugged status!");
} else {
// Likely, if plugged > 0, it's likely plugged and charging but the doc
// isn't clear about that.
sCharging = plugged != 0;
}
if (sCharging != previousCharging) {
sRemainingTime = kUnknownRemainingTime;
// The new remaining time is going to take some time to show up but
// it's the best way to show a not too wrong value.
sLastLevelChange = 0;
}
// We need two doubles because sLevel is a double.
double current = (double)intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
double max = (double)intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
if (current == -1 || max == -1) {
Log.e(LOGTAG, "Failed to get battery level!");
sLevel = kDefaultLevel;
} else {
sLevel = current / max;
}
if (sLevel == 1.0 && sCharging) {
sRemainingTime = kDefaultRemainingTime;
} else if (sLevel != previousLevel) {
// Estimate remaining time.
if (sLastLevelChange != 0) {
// Use elapsedRealtime() because we want to track time across device sleeps.
long currentTime = SystemClock.elapsedRealtime();
long dt = (currentTime - sLastLevelChange) / 1000;
double dLevel = sLevel - previousLevel;
if (sCharging) {
if (dLevel < 0) {
Log.w(LOGTAG, "When charging, level should increase!");
sRemainingTime = kUnknownRemainingTime;
} else {
sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel));
}
} else {
if (dLevel > 0) {
Log.w(LOGTAG, "When discharging, level should decrease!");
sRemainingTime = kUnknownRemainingTime;
} else {
sRemainingTime = Math.round(dt / -dLevel * sLevel);
}
}
sLastLevelChange = currentTime;
} else {
// That's the first time we got an update, we can't do anything.
sLastLevelChange = SystemClock.elapsedRealtime();
}
}
} else {
sLevel = kDefaultLevel;
sCharging = kDefaultCharging;
sRemainingTime = kDefaultRemainingTime;
}
/*
* We want to inform listeners if the following conditions are fulfilled:
* - we have at least one observer;
* - the charging state or the level has changed.
*
* Note: no need to check for a remaining time change given that it's only
* updated if there is a level change or a charging change.
*
* The idea is to prevent doing all the way to the DOM code in the child
* process to finally not send an event.
*/
if (sNotificationsEnabled &&
(previousCharging != isCharging() || previousLevel != getLevel())) {
GeckoAppShell.notifyBatteryChange(getLevel(), isCharging(), getRemainingTime());
}
}
public static boolean isCharging() {
return sCharging;
}
public static double getLevel() {
return sLevel;
}
public static double getRemainingTime() {
return sRemainingTime;
}
public static void enableNotifications() {
sNotificationsEnabled = true;
}
public static void disableNotifications() {
sNotificationsEnabled = false;
}
public static double[] getCurrentInformation() {
return new double[] { getLevel(), isCharging() ? 1.0 : 0.0, getRemainingTime() };
}
}
|