diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index 860a10cd4efe..18a40916e6a9 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -114,7 +114,7 @@ public class GeckoAppShell public static native void processNextNativeEvent(); - public static native void notifyBatteryChange(double aLevel, boolean aCharging); + public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime); // A looper thread, accessed by GeckoAppShell.getHandler private static class LooperThread extends Thread { diff --git a/embedding/android/GeckoBatteryManager.java b/embedding/android/GeckoBatteryManager.java index 36c477c61d76..974f6b0534b5 100644 --- a/embedding/android/GeckoBatteryManager.java +++ b/embedding/android/GeckoBatteryManager.java @@ -38,6 +38,7 @@ package org.mozilla.gecko; import java.lang.Math; +import java.util.Date; import android.util.Log; @@ -52,12 +53,15 @@ public class 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 kDefaultLevel = 1.0; + private final static boolean kDefaultCharging = true; + private final static double kUnknownRemainingTime = -1.0; - private static boolean sNotificationsEnabled = false; - private static double sLevel = kDefaultLevel; - private static boolean sCharging = kDefaultCharging; + private static Date sLastLevelChange = new Date(0); + private static boolean sNotificationsEnabled = false; + private static double sLevel = kDefaultLevel; + private static boolean sCharging = kDefaultCharging; + private static double sRemainingTime = kUnknownRemainingTime;; @Override public void onReceive(Context context, Intent intent) { @@ -80,6 +84,13 @@ public class GeckoBatteryManager 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 = new Date(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); @@ -89,9 +100,42 @@ public class GeckoBatteryManager } else { sLevel = current / max; } + + if (sLevel == 1.0 && sCharging) { + sRemainingTime = 0.0; + } else if (sLevel != previousLevel) { + // Estimate remaining time. + if (sLastLevelChange.getTime() != 0) { + Date currentTime = new Date(); + long dt = (currentTime.getTime() - sLastLevelChange.getTime()) / 1000; + double dLevel = sLevel - previousLevel; + + if (sCharging) { + if (dLevel < 0) { + Log.w("GeckoBatteryManager", "When charging, level should increase!"); + sRemainingTime = kUnknownRemainingTime; + } else { + sRemainingTime = Math.round(dt / dLevel * (1.0 - sLevel)); + } + } else { + if (dLevel > 0) { + Log.w("GeckoBatteryManager", "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 = new Date(); + } + } } else { sLevel = kDefaultLevel; sCharging = kDefaultCharging; + sRemainingTime = kUnknownRemainingTime; } /* @@ -99,12 +143,15 @@ public class GeckoBatteryManager * - 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()); + GeckoAppShell.notifyBatteryChange(getLevel(), isCharging(), getRemainingTime()); } } @@ -116,6 +163,10 @@ public class GeckoBatteryManager return sLevel; } + public static double getRemainingTime() { + return sRemainingTime; + } + public static void enableNotifications() { sNotificationsEnabled = true; } @@ -125,6 +176,6 @@ public class GeckoBatteryManager } public static double[] getCurrentInformation() { - return new double[] { getLevel(), isCharging() ? 1.0 : 0.0 }; + return new double[] { getLevel(), isCharging() ? 1.0 : 0.0, getRemainingTime() }; } } diff --git a/other-licenses/android/APKOpen.cpp b/other-licenses/android/APKOpen.cpp index 2b450bbb115e..b49d6eb1eb88 100644 --- a/other-licenses/android/APKOpen.cpp +++ b/other-licenses/android/APKOpen.cpp @@ -241,7 +241,7 @@ SHELL_WRAPPER1(onChangeNetworkLinkStatus, jstring) SHELL_WRAPPER1(reportJavaCrash, jstring) SHELL_WRAPPER0(executeNextRunnable) SHELL_WRAPPER1(cameraCallbackBridge, jbyteArray) -SHELL_WRAPPER2(notifyBatteryChange, jdouble, jboolean); +SHELL_WRAPPER3(notifyBatteryChange, jdouble, jboolean, jdouble); static void * xul_handle = NULL; static time_t apk_mtime = 0; diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index 3cef4275f22a..dac8c03f4644 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -1254,7 +1254,7 @@ AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInf // an array of double even if we actually want a double and a boolean. jobject obj = mJNIEnv->CallStaticObjectMethod(mGeckoAppShellClass, jGetCurrentBatteryInformation); jdoubleArray arr = static_cast(obj); - if (!arr || mJNIEnv->GetArrayLength(arr) != 2) { + if (!arr || mJNIEnv->GetArrayLength(arr) != 3) { return; } @@ -1262,8 +1262,7 @@ AndroidBridge::GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInf aBatteryInfo->level() = info[0]; aBatteryInfo->charging() = info[1] == 1.0f; - // TODO: this is temporary until the Android backend handles this property. - aBatteryInfo->remainingTime() = -1.0f; + aBatteryInfo->remainingTime() = info[2]; mJNIEnv->ReleaseDoubleArrayElements(arr, info, 0); } diff --git a/widget/src/android/AndroidJNI.cpp b/widget/src/android/AndroidJNI.cpp index bccbe9a08c72..af7f11662c30 100644 --- a/widget/src/android/AndroidJNI.cpp +++ b/widget/src/android/AndroidJNI.cpp @@ -74,7 +74,7 @@ extern "C" { NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *, jclass, jstring status); NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *, jclass, jstring stack); NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_executeNextRunnable(JNIEnv *, jclass); - NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass, jdouble, jboolean); + NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass, jdouble, jboolean, jdouble); } @@ -192,28 +192,29 @@ Java_org_mozilla_gecko_GeckoAppShell_executeNextRunnable(JNIEnv *, jclass) NS_EXPORT void JNICALL Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange(JNIEnv* jenv, jclass, jdouble aLevel, - jboolean aCharging) + jboolean aCharging, + jdouble aRemainingTime) { class NotifyBatteryChangeRunnable : public nsRunnable { public: - NotifyBatteryChangeRunnable(double aLevel, bool aCharging) + NotifyBatteryChangeRunnable(double aLevel, bool aCharging, double aRemainingTime) : mLevel(aLevel) , mCharging(aCharging) + , mRemainingTime(aRemainingTime) {} NS_IMETHODIMP Run() { - // TODO: -1 is temporary until the Android backend handles the - // remainingTime proporty. - hal::NotifyBatteryChange(hal::BatteryInformation(mLevel, mCharging, -1.0)); + hal::NotifyBatteryChange(hal::BatteryInformation(mLevel, mCharging, mRemainingTime)); return NS_OK; } private: double mLevel; bool mCharging; + double mRemainingTime; }; - nsCOMPtr runnable = new NotifyBatteryChangeRunnable(aLevel, aCharging); + nsCOMPtr runnable = new NotifyBatteryChangeRunnable(aLevel, aCharging, aRemainingTime); NS_DispatchToMainThread(runnable); }