diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 00:47:55 +0000 |
commit | 26a029d407be480d791972afb5975cf62c9360a6 (patch) | |
tree | f435a8308119effd964b339f76abb83a57c29483 /mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java | |
parent | Initial commit. (diff) | |
download | firefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz firefox-26a029d407be480d791972afb5975cf62c9360a6.zip |
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java')
-rw-r--r-- | mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java new file mode 100644 index 0000000000..19f489b399 --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoBatteryManager.java @@ -0,0 +1,200 @@ +/* -*- 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; +import org.mozilla.gecko.annotation.WrapForJNI; + +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 static final double kDefaultLevel = 1.0; + private static final boolean kDefaultCharging = true; + private static final double kDefaultRemainingTime = 0.0; + private static final double kUnknownRemainingTime = -1.0; + + private static long sLastLevelChange; + private static boolean sNotificationsEnabled; + private static double sLevel = kDefaultLevel; + private static boolean sCharging = kDefaultCharging; + private static double sRemainingTime = kDefaultRemainingTime; + + private static final GeckoBatteryManager sInstance = new GeckoBatteryManager(); + + private final 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 synchronized void start(final Context context) { + if (mIsEnabled) { + Log.w(LOGTAG, "Already started!"); + return; + } + + mApplicationContext = context.getApplicationContext(); + // 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) { + Log.w(LOGTAG, "Already stopped!"); + return; + } + + mApplicationContext.unregisterReceiver(this); + mApplicationContext = null; + mIsEnabled = false; + } + + @WrapForJNI(calledFrom = "ui", dispatchTo = "gecko") + private static native void onBatteryChange(double level, boolean charging, double remainingTime); + + @Override + public void onReceive(final Context context, final Intent intent) { + if (!intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) { + Log.e(LOGTAG, "Got an unexpected intent!"); + return; + } + + final boolean previousCharging = isCharging(); + final 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 advertises 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")) { + final 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. + final double current = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); + final double max = 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. + final long currentTime = SystemClock.elapsedRealtime(); + final long dt = (currentTime - sLastLevelChange) / 1000; + final double dLevel = sLevel - previousLevel; + + if (sCharging) { + if (dLevel < 0) { + 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())) { + onBatteryChange(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()}; + } +} |