Permalink
Browse files

Implement the battery status events specification.

Provide an implementation matching the current W3C proposal spec
for battery status events.  The implementation fires events when
the battery level changes, charging state changes and when the
battery level reaches predefined low and critical values.  The
spec is located here:

 - http://dev.w3.org/2009/dap/system-info/battery-status.html
  • Loading branch information...
1 parent df4fed4 commit 0fd772ba24a646b4db6cd7101fcdd97622ada902 @deedubbu deedubbu committed Aug 19, 2011
Showing with 339 additions and 1 deletion.
  1. +199 −0 framework/ext/src/com/phonegap/battery/Battery.java
  2. +138 −0 javascript/battery.js
  3. +2 −1 template/project/www/plugins.xml
@@ -0,0 +1,199 @@
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2005-2010, Nitobi Software Inc.
+ * Copyright (c) 2010-2011, IBM Corporation
+ */
+package com.phonegap.battery;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import net.rim.device.api.system.Application;
+import net.rim.device.api.system.DeviceInfo;
+import net.rim.device.api.system.SystemListener;
+
+import com.phonegap.api.Plugin;
+import com.phonegap.api.PluginResult;
+import com.phonegap.json4j.JSONArray;
+import com.phonegap.json4j.JSONException;
+import com.phonegap.json4j.JSONObject;
+import com.phonegap.util.Logger;
+
+/**
+ * The Battery plug-in. This class provides information about the state of the
+ * battery on the phone. The following actions are supported:
+ *
+ * start - Start listening for changes in battery level (%) and batter
+ * charging state.
+ * stop - Stop listening for changes in battery level and state.
+ */
+public class Battery extends Plugin {
+
+ /** Actions to start and stop listening for battery changes. */
+ private final static String ACTION_START = "start";
+ private final static String ACTION_STOP = "stop";
+
+ /** The percentage of battery remaining. */
+ private final static String LEVEL = "level";
+
+ /** Whether the battery is currently charging or not. */
+ private final static String CHARGING = "isPlugged";
+
+ // The set of call back IDs to send results to. Using Hashtable because
+ // BlackBerry does not support Collections. There should only ever be one
+ // call back ID, but this allows multiple.
+ private Hashtable callbackIds = new Hashtable();
+
+ private SystemListener batteryListener = null;
+
+ /**
+ * Executes the requested action and returns a PluginResult.
+ *
+ * @param action
+ * The action to execute.
+ * @param callbackId
+ * The callback ID to be invoked upon action completion
+ * @param args
+ * JSONArry of arguments for the action.
+ * @return A PluginResult object with a status and message.
+ */
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ PluginResult result = null;
+
+ if (ACTION_START.equals(action)) {
+ // Register a listener to detect battery changes.
+ addListener(callbackId);
+
+ // Don't return any result now, since battery status results are
+ // sent when listener is notified.
+ result = new PluginResult(PluginResult.Status.NO_RESULT);
+
+ // Must keep the call back active for future events.
+ result.setKeepCallback(true);
+ } else if (ACTION_STOP.equals(action)) {
+ // Remove the battery listener and cleanup call back IDs.
+ removeListener();
+ result = new PluginResult(PluginResult.Status.OK);
+ } else {
+ result = new PluginResult(PluginResult.Status.INVALID_ACTION,
+ "Battery: Invalid action: " + action);
+ }
+
+ return result;
+ }
+
+ /**
+ * Remove the listener when the application is destroyed. Note that onPause
+ * is not overridden, so the listener will continue if the application is
+ * simply paused instead of destroyed.
+ */
+ public void onDestroy() {
+ removeListener();
+ }
+
+ /**
+ * Adds a SystemListener to listen for changes to the battery state. The
+ * listener is only registered if one has not already been added. If a
+ * listener has already been registered the call back id is simply saved so
+ * that it can be notified upon next battery state change.
+ *
+ * @param callbackId
+ * The reference point to call back when a listener event occurs.
+ */
+ private synchronized void addListener(String callbackId) {
+ callbackIds.put(callbackId, callbackId);
+
+ // Only register a listener if one has not been registered.
+ if (batteryListener == null) {
+ batteryListener = new SystemListener() {
+ // Initialize the charging state and battery level.
+ private boolean prevChargeState = (DeviceInfo
+ .getBatteryStatus() & DeviceInfo.BSTAT_CHARGING) != 0;
+ private int prevLevel = DeviceInfo.getBatteryLevel();
+
+ public void batteryGood() { }
+ public void batteryLow() { }
+
+ public void batteryStatusChange(int status) {
+ // The status bits passed into this method are unreliable
+ // in determining when the battery level has changed.
+ // Instead, when any state change occurs, get the current
+ // battery level and report the change if it is different
+ // then previous value.
+ int newLevel = DeviceInfo.getBatteryLevel();
+ boolean newChargeState = (DeviceInfo.BSTAT_CHARGING & status) != 0;
+
+ // Report change if level or charge state is different then
+ // previous values.
+ if (newLevel != prevLevel || newChargeState != prevChargeState) {
+ prevChargeState = newChargeState;
+ prevLevel = newLevel;
+
+ // Store the retrieved properties in a JSON object.
+ JSONObject connectionInfo = new JSONObject();
+ try {
+ connectionInfo.put(LEVEL, newLevel);
+ connectionInfo.put(CHARGING, newChargeState);
+ } catch (JSONException e) {
+ Logger.error("JSONException: " + e.getMessage());
+ return;
+ }
+
+ PluginResult result = new PluginResult(
+ PluginResult.Status.OK, connectionInfo);
+
+ sendSuccessResult(result, true);
+ }
+ }
+
+ public void powerOff() { }
+ public void powerUp() { }
+ };
+ Application.getApplication().addSystemListener(batteryListener);
+ }
+ }
+
+ /**
+ * Remove the registered battery status listener and cleanup the call back
+ * IDs.
+ */
+ private synchronized void removeListener() {
+ if (batteryListener != null) {
+
+ // Remove the battery listener.
+ Application.getApplication().removeSystemListener(batteryListener);
+ batteryListener = null;
+
+ // Close out the call back IDs.
+ sendSuccessResult(new PluginResult(PluginResult.Status.OK), false);
+ callbackIds.clear();
+ }
+ }
+
+ /**
+ * Helper function to send the PluginResult to the saved call back IDs.
+ *
+ * @param result
+ * the PluginResult to return
+ * @param keepCallback
+ * Boolean value indicating whether to keep the call back id
+ * active.
+ */
+ private void sendSuccessResult(PluginResult result, boolean keepCallback) {
+
+ if (result != null) {
+ // Must keep the call back active for future events.
+ result.setKeepCallback(keepCallback);
+
+ // Iterate through the saved call back IDs. Really should only ever
+ // be one.
+ for (Enumeration callbacks = this.callbackIds.elements(); callbacks
+ .hasMoreElements();) {
+ success(result, (String) callbacks.nextElement());
+ }
+ }
+ }
+
+}
View
@@ -0,0 +1,138 @@
+
+/*
+ * PhoneGap is available under *either* the terms of the modified BSD license *or* the
+ * MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
+ *
+ * Copyright (c) 2005-2010, Nitobi Software Inc.
+ * Copyright (c) 2010-2011, IBM Corporation
+ */
+
+/**
+ * navigator.battery
+ */
+(function() {
+ /**
+ * Check to see that navigator.battery has not been initialized.
+ */
+ if (typeof navigator.battery !== "undefined") {
+ return;
+ }
+
+ /**
+ * This class contains information about the current battery status.
+ * @constructor
+ */
+ var Battery = function() {
+ this._level = null;
+ this._isPlugged = null;
+ this._batteryListener = [];
+ this._lowListener = [];
+ this._criticalListener = [];
+ };
+
+ /**
+ * Registers as an event producer for battery events.
+ *
+ * @param {Object} eventType
+ * @param {Object} handler
+ * @param {Object} add
+ */
+ Battery.prototype.eventHandler = function(eventType, handler, add) {
+ var me = navigator.battery;
+ if (add) {
+ // If there are no current registered event listeners start the
+ // battery listener on native side.
+ if (me._batteryListener.length === 0 && me._lowListener.length === 0
+ && me._criticalListener.length === 0) {
+ PhoneGap.exec(me._status, me._error, "Battery", "start", []);
+ }
+
+ // Register the event listener in the proper array
+ if (eventType === "batterystatus") {
+ var pos = me._batteryListener.indexOf(handler);
+ if (pos === -1) {
+ me._batteryListener.push(handler);
+ }
+ } else if (eventType === "batterylow") {
+ var pos = me._lowListener.indexOf(handler);
+ if (pos === -1) {
+ me._lowListener.push(handler);
+ }
+ } else if (eventType === "batterycritical") {
+ var pos = me._criticalListener.indexOf(handler);
+ if (pos === -1) {
+ me._criticalListener.push(handler);
+ }
+ }
+ } else {
+ // Remove the event listener from the proper array
+ if (eventType === "batterystatus") {
+ var pos = me._batteryListener.indexOf(handler);
+ if (pos > -1) {
+ me._batteryListener.splice(pos, 1);
+ }
+ } else if (eventType === "batterylow") {
+ var pos = me._lowListener.indexOf(handler);
+ if (pos > -1) {
+ me._lowListener.splice(pos, 1);
+ }
+ } else if (eventType === "batterycritical") {
+ var pos = me._criticalListener.indexOf(handler);
+ if (pos > -1) {
+ me._criticalListener.splice(pos, 1);
+ }
+ }
+
+ // If there are no more registered event listeners stop the battery
+ // listener on native side.
+ if (me._batteryListener.length === 0 && me._lowListener.length === 0
+ && me._criticalListener.length === 0) {
+ PhoneGap.exec(null, null, "Battery", "stop", []);
+ }
+ }
+ };
+
+ /**
+ * Callback for battery status
+ *
+ * @param {Object} info keys: level, isPlugged
+ */
+ Battery.prototype._status = function(info) {
+ if (info) {
+ var me = this;
+ if (me._level != info.level || me._isPlugged != info.isPlugged) {
+ // Fire batterystatus event
+ PhoneGap.fireWindowEvent("batterystatus", info);
+
+ // Fire low battery event
+ if (info.level == 20 || info.level == 5) {
+ if (info.level == 20) {
+ PhoneGap.fireWindowEvent("batterylow", info);
+ }
+ else {
+ PhoneGap.fireWindowEvent("batterycritical", info);
+ }
+ }
+ }
+ me._level = info.level;
+ me._isPlugged = info.isPlugged;
+ }
+ };
+
+ /**
+ * Error callback for battery start
+ */
+ Battery.prototype._error = function(e) {
+ console.log("Error initializing Battery: " + e);
+ };
+
+ PhoneGap.addConstructor(function() {
+ if (typeof navigator.battery === "undefined") {
+ navigator.battery = new Battery();
+ PhoneGap.addWindowEventHandler("batterystatus", navigator.battery.eventHandler);
+ PhoneGap.addWindowEventHandler("batterylow", navigator.battery.eventHandler);
+ PhoneGap.addWindowEventHandler("batterycritical", navigator.battery.eventHandler);
+ }
+ });
+
+}());
@@ -9,4 +9,5 @@
<plugin name="FileTransfer" value="com.phonegap.http.FileTransfer"/>
<plugin name="Contact" value="com.phonegap.pim.Contact"/>
<plugin name="MediaCapture" value="com.phonegap.media.MediaCapture"/>
-</plugins>
+ <plugin name="Battery" value="com.phonegap.battery.Battery"/>
+</plugins>

0 comments on commit 0fd772b

Please sign in to comment.