You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by fi...@apache.org on 2012/05/19 00:22:01 UTC

[2/3] webworks commit: [CB-465] Refactor Accelerometer code for addWatch / clearWatch.

[CB-465] Refactor Accelerometer code for addWatch / clearWatch.


Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-blackberry-webworks/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-blackberry-webworks/commit/b28459c1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-blackberry-webworks/tree/b28459c1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-blackberry-webworks/diff/b28459c1

Branch: refs/heads/master
Commit: b28459c1a552b38b9f1027b6c7e6735a482fd082
Parents: 3255ae8
Author: Drew Walters <de...@apache.org>
Authored: Wed May 16 15:31:09 2012 -0500
Committer: Drew Walters <de...@apache.org>
Committed: Wed May 16 15:31:09 2012 -0500

----------------------------------------------------------------------
 .../cordova/accelerometer/Accelerometer.java       |  387 ++++++++-------
 1 files changed, 210 insertions(+), 177 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cordova-blackberry-webworks/blob/b28459c1/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
----------------------------------------------------------------------
diff --git a/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java b/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
index 18de371..09e6aef 100644
--- a/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
+++ b/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
@@ -18,23 +18,24 @@
  */
 package org.apache.cordova.accelerometer;
 
-import org.apache.cordova.api.Plugin;
-import org.apache.cordova.api.PluginResult;
-import org.apache.cordova.json4j.JSONArray;
-import org.apache.cordova.json4j.JSONException;
-import org.apache.cordova.json4j.JSONObject;
-import org.apache.cordova.util.Logger;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
 
 import net.rim.device.api.system.AccelerometerData;
 import net.rim.device.api.system.AccelerometerListener;
 import net.rim.device.api.system.AccelerometerSensor;
 import net.rim.device.api.system.Application;
 
-import java.util.Enumeration;
-import java.util.Vector;
-import java.util.Hashtable;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.json4j.JSONArray;
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.json4j.JSONObject;
+import org.apache.cordova.util.Logger;
 
 public class Accelerometer extends Plugin implements AccelerometerListener {
+    private static final String LOG_TAG = "Accelerometer: ";
 
     private static final String ACTION_GET_ACCELERATION = "getAcceleration";
     private static final String ACTION_ADD_WATCH = "addWatch";
@@ -53,57 +54,37 @@ public class Accelerometer extends Plugin implements AccelerometerListener {
     // the single channel to the device sensor
     private static AccelerometerSensor.Channel _rawDataChannel = null;
 
-    private int status = STOPPED; // status of this listener
-
-    private Hashtable watches = new Hashtable();
-    private Vector callbacks = new Vector();
+    private int state = STOPPED; // state of this listener
+    private long initTime = 0;
 
-    private short x, y, z;
-    private long timestamp;
+    /**
+     * Hash of all the listeners created, keyed on callback ids.
+     */
+    private final Vector callbackIds = new Vector();
+    private final Hashtable watchIds = new Hashtable();
 
     public PluginResult execute(String action, JSONArray args, String callbackId) {
-
-        PluginResult result = null;
-
+        PluginResult result;
         try {
             if (!AccelerometerSensor.isSupported()) {
                 result = new PluginResult(
-                    PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION,
-                    "Accelerometer sensor not supported");
+                        PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION,
+                        "Accelerometer sensor not supported");
             } else if (ACTION_GET_ACCELERATION.equals(action)) {
-                if (this.status != RUNNING) {
-                    result = new PluginResult(PluginResult.Status.NO_RESULT);
-                    result.setKeepCallback(true);
-                    this.callbacks.addElement(callbackId);
-                    this.start();
-                } else {
-                    result = new PluginResult(
-                        PluginResult.Status.OK,
-                        this.getAccelerationJSON());
-                }
+                result = getAcceleration(callbackId);
             } else if (ACTION_ADD_WATCH.equals(action)) {
                 String watchId = args.getString(0);
-                this.watches.put(watchId, callbackId);
-                if (this.status != RUNNING) {
-                    this.start();
-                }
-                result = new PluginResult(PluginResult.Status.NO_RESULT);
-                result.setKeepCallback(true);
+                result = addWatch(watchId, callbackId);
             } else if (ACTION_CLEAR_WATCH.equals(action)) {
                 String watchId = args.getString(0);
-                if (this.watches.containsKey(watchId)) {
-                    this.watches.remove(watchId);
-                    if (this.size() == 0) {
-                        this.stop();
-                    }
-                }
-                result = new PluginResult(PluginResult.Status.OK);
+                result = clearWatch(watchId);
             } else {
                 result = new PluginResult(PluginResult.Status.INVALID_ACTION,
                         "Accelerometer: Invalid action:" + action);
             }
-        } catch(JSONException e) {
-            result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+        } catch (JSONException e) {
+            result = new PluginResult(PluginResult.Status.JSON_EXCEPTION,
+                    e.getMessage());
         }
 
         return result;
@@ -118,164 +99,158 @@ public class Accelerometer extends Plugin implements AccelerometerListener {
      * @return T=returns value
      */
     public boolean isSynch(String action) {
-        if (action.equals("getAcceleration") && this.status == RUNNING) {
+        if (ACTION_GET_ACCELERATION.equals(action) && state == RUNNING) {
             return true;
-        } else if (action.equals("addWatch") && this.status == RUNNING) {
+        } else if (ACTION_ADD_WATCH.equals(action) && state == RUNNING) {
             return true;
-        } else if (action.equals("clearWatch")) {
+        } else if (ACTION_CLEAR_WATCH.equals(action)) {
             return true;
         }
         return false;
     }
 
     /**
-     * Opens a raw data channel to the accelerometer sensor.
-     *
-     * @return the AccelerometerSensor.Channel for the application
-     */
-    private static AccelerometerSensor.Channel getChannel() {
-        // an application can only have one open channel
-        if (_rawDataChannel == null || !_rawDataChannel.isOpen()) {
-            _rawDataChannel = AccelerometerSensor
-                    .openRawDataChannel(Application.getApplication());
-            Logger.log(Accelerometer.class.getName()
-                    + ": sensor channel opened");
-        }
-        return _rawDataChannel;
-    }
-
-    /**
-     * Implements the AccelerometerListener method.
-     *
+     * Implements the AccelerometerListener method. We listen for the purpose of
+     * closing the application's accelerometer sensor channel after timeout has
+     * been exceeded.
      */
-
     public void onData(AccelerometerData accelData) {
-        if (this.status == STOPPED) return;
-
-        this.timestamp = accelData.getLastTimestamp();
-        this.x = accelData.getLastXAcceleration();
-        this.y = accelData.getLastYAcceleration();
-        this.z = accelData.getLastZAcceleration();
-
-        this.win();
+        short x = accelData.getLastXAcceleration();
+        short y = accelData.getLastYAcceleration();
+        short z = accelData.getLastZAcceleration();
 
-        if (this.size() == 0) {
-            this.stop();
+        // If any value is not zero, assume sensor is now active and set state.
+        if (state == STARTING && (x != 0 || y != 0 || z != 0)) {
+            state = RUNNING;
         }
-    }
 
-    /**
-     * Adds this listener to sensor channel.
-     */
-    private void start() {
-        // If already started or starting, return.
-        if (this.status == RUNNING || this.status == STARTING) return;
-
-        this.status = STARTING;
-
-        // open the sensor channel and register listener
-        getChannel().setAccelerometerListener(this);
-        Logger.log(this.getClass().getName() + ": sensor listener added");
-
-        // Need to wait until sensor is active. Otherwise, first query will
-        // return zeros for all values. Wait up to 2 seconds.
-        int waittime = 2000;
-        AccelerometerData accelData = null;
-        while (waittime > 0) {
-            accelData = getChannel().getAccelerometerData();
-            // If any value is not zero, assume sensor is active and break out.
-            if (accelData.getLastXAcceleration() != 0
-                    || accelData.getLastYAcceleration() != 0
-                    || accelData.getLastZAcceleration() != 0) {
-                break;
-            }
-
-            // Sleep and give sensor more time to warm up.
-            waittime -= 100;
+        if (state == RUNNING) {
+            // Send the new accelerometer data.
+            JSONObject accel = new JSONObject();
             try {
-                Thread.sleep(100);
-            } catch (InterruptedException e) {
-                e.printStackTrace();
+                accel.put("x", normalize(x));
+                accel.put("y", normalize(y));
+                accel.put("z", normalize(z));
+                accel.put("timestamp", accelData.getLastTimestamp());
+                sendResult(true,
+                        new PluginResult(PluginResult.Status.OK, accel), true);
+            } catch (JSONException e) {
+                sendResult(false, new PluginResult(
+                        PluginResult.Status.JSON_EXCEPTION, "JSONException:"
+                                + e.getMessage()), false);
             }
-        }
-
-        // Sensor failed to start.
-        if (waittime == 0) {
+        } else if ((System.currentTimeMillis() - initTime) > 2000) {
+            // If the sensor does not become active within 2 seconds of
+            // the request to start it, fail out.
             stop();
-            this.status = ERROR_FAILED_TO_START;
-            this.fail(this.status, "Accelerometer could not be started.");
-            return;
+            state = ERROR_FAILED_TO_START;
+            JSONObject errorObj = new JSONObject();
+            try {
+                errorObj.put("code", ERROR_FAILED_TO_START);
+                errorObj.put("message", "Accelerometer could not be started.");
+            } catch (JSONException e) {
+                Logger.log(LOG_TAG
+                        + "Failed to build JSON object for ERROR_FAILED_TO_START.");
+            }
+            sendResult(false, new PluginResult(PluginResult.Status.ERROR,
+                    errorObj), false);
         }
+    }
 
-        this.status = RUNNING;
+    /**
+     * Called when Plugin is destroyed.
+     */
+    public void onDestroy() {
+        // Close out the call back IDs and stop.
+        sendResult(true, new PluginResult(PluginResult.Status.NO_RESULT), false);
     }
 
     /**
-     * Stops accelerometer listener and closes the sensor channel.
+     * Adds a SystemListener to listen for changes to the battery state. The
+     * listener is only registered if one has not already been added.
      */
-    private void stop() {
-        // close the sensor channel
-        if (_rawDataChannel != null && _rawDataChannel.isOpen()) {
-            _rawDataChannel.close();
-            Logger.log(this.getClass().getName() + ": sensor channel closed");
+    private int addListener() {
+        if (_rawDataChannel == null || !_rawDataChannel.isOpen()) {
+            _rawDataChannel = AccelerometerSensor
+                    .openRawDataChannel(Application.getApplication());
+            Logger.log(LOG_TAG + "sensor channel opened");
+
+            initTime = System.currentTimeMillis();
+            state = STARTING;
+            _rawDataChannel.setAccelerometerListener(this);
+            Logger.log(LOG_TAG + "sensor listener added");
         }
 
-        this.status = STOPPED;
+        return state;
     }
 
-    private void fail(int code, String message) {
-        // Error object
-        JSONObject errorObj = new JSONObject();
-        try {
-            errorObj.put("code", code);
-            errorObj.put("message", message);
-        } catch (JSONException e) {
-            // TODO Auto-generated catch block
-            e.printStackTrace();
-        }
-        PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
-    	
-        for (Enumeration e = this.callbacks.elements(); e.hasMoreElements();)
-        {
-            this.error(err, (String)e.nextElement());
-        }
-        this.callbacks.removeAllElements();
-        
-        err.setKeepCallback(true);
+    /**
+     * Track the specified watch ID and start the accelerometer channel if it
+     * hasn't been started.
+     *
+     * @param watchId
+     * @param callbackId
+     * @return
+     */
+    private synchronized PluginResult addWatch(String watchId, String callbackId) {
+        watchIds.put(watchId, callbackId);
+        addListener();
+        PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT,
+                "");
+        result.setKeepCallback(true);
 
-        for (Enumeration e = this.watches.keys(); e.hasMoreElements();)
-        {
-            this.error(err, (String)this.watches.get((String)e.nextElement()));
-        }
+        return result;
     }
-    
-    private void win() {
-        // Success return object
-        PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
-        
-        for (Enumeration e = this.callbacks.elements(); e.hasMoreElements();)
-        {
-            this.success(result, (String)e.nextElement());
-        }
-        this.callbacks.removeAllElements();
 
-        result.setKeepCallback(true);
-        
-        for (Enumeration e = this.watches.keys(); e.hasMoreElements();)
-        {
-            this.success(result, (String)this.watches.get((String)e.nextElement()));
+    /**
+     * Removes the specified watch ID and stops the accelerometer channel if
+     * this it was the last active listener.
+     *
+     * @param watchId
+     * @return
+     */
+    private synchronized PluginResult clearWatch(String watchId) {
+        if (watchIds.containsKey(watchId)) {
+            watchIds.remove(watchId);
+            if (watchIds.size() == 0 && callbackIds.size() == 0) {
+                stop();
+            }
         }
+        return new PluginResult(PluginResult.Status.OK, "");
     }
 
     /**
-     * Called when Plugin is destroyed.
+     * If the sensor is active, return the last acquired accelerometer data,
+     * otherwise start the sensor and listen for data.
+     *
+     * @return AccelerometerData with last acceleration data
      */
-    public void onDestroy() {
-        stop();
-    }
+    private synchronized PluginResult getAcceleration(String callbackId) {
+        PluginResult result;
+
+        if (state != RUNNING) {
+            callbackIds.addElement(callbackId);
+            addListener();
+            result = new PluginResult(PluginResult.Status.NO_RESULT, "");
+            result.setKeepCallback(true);
+        } else {
+            // get the last acceleration
+            AccelerometerData accelData = _rawDataChannel
+                    .getAccelerometerData();
+            JSONObject accel = new JSONObject();
+            try {
+                accel.put("x", normalize(accelData.getLastXAcceleration()));
+                accel.put("y", normalize(accelData.getLastYAcceleration()));
+                accel.put("z", normalize(accelData.getLastZAcceleration()));
+                accel.put("timestamp", accelData.getLastTimestamp());
+                result = new PluginResult(PluginResult.Status.OK, accel);
+            } catch (JSONException e) {
+                result = new PluginResult(PluginResult.Status.JSON_EXCEPTION,
+                        "JSONException:" + e.getMessage());
+            }
+        }
 
-    private int size() {
-        return this.watches.size() + this.callbacks.size();
+        return result;
     }
 
     /**
@@ -303,15 +278,73 @@ public class Accelerometer extends Plugin implements AccelerometerListener {
         return Double.parseDouble(buf.toString());
     }
 
-    private JSONObject getAccelerationJSON() {
-        JSONObject accel = new JSONObject();
-        try {
-            accel.put("x", normalize(this.x));
-            accel.put("y", normalize(this.y));
-            accel.put("z", normalize(this.z));
-            accel.put("timestamp", this.timestamp);
-        } catch (JSONException e) {
+    /**
+     * Helper function to send a PluginResult to the saved call back IDs.
+     *
+     * @param issuccess
+     *            true if this is a successful result, false otherwise.
+     * @param result
+     *            the PluginResult to return
+     * @param keepCallback
+     *            Boolean value indicating whether to keep the call back id
+     *            active.
+     */
+    private synchronized void sendResult(boolean issuccess,
+            PluginResult result, boolean keepCallback) {
+
+        if (result != null) {
+            // Must keep the call back active for future watch events.
+            result.setKeepCallback(keepCallback);
+
+            // Iterate through the saved watch IDs.
+            for (Enumeration watches = watchIds.elements(); watches
+                    .hasMoreElements();) {
+                if (issuccess) {
+                    success(result, (String) watches.nextElement());
+                } else {
+                    error(result, (String) watches.nextElement());
+                }
+            }
+
+            // callbackIds are from getAcceleration() requests so they are
+            // one time and should not keep callback.
+            result.setKeepCallback(false);
+
+            // Iterate through the saved call back IDs.
+            for (Enumeration callbacks = callbackIds.elements(); callbacks
+                    .hasMoreElements();) {
+                if (issuccess) {
+                    success(result, (String) callbacks.nextElement());
+                } else {
+                    error(result, (String) callbacks.nextElement());
+                }
+            }
+        }
+
+        if (!keepCallback) {
+            watchIds.clear();
         }
-        return accel;
+        callbackIds.removeAllElements();
+
+        if (watchIds.size() == 0) {
+            stop();
+        }
+    }
+
+    /**
+     * Stops accelerometer listener and closes the sensor channel.
+     */
+    private synchronized void stop() {
+        if (_rawDataChannel != null && _rawDataChannel.isOpen()) {
+
+            // Remove the battery listener.
+            _rawDataChannel.removeAccelerometerListener();
+            _rawDataChannel.close();
+            _rawDataChannel = null;
+
+            Logger.log(LOG_TAG + "sensor channel closed");
+        }
+
+        state = STOPPED;
     }
 }