You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by lo...@apache.org on 2013/06/19 20:32:22 UTC
[31/36] [CB-3401] renamed blackberry root folder to bbos
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/CordovaExtension.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/CordovaExtension.java b/bbos/framework/ext/src/org/apache/cordova/CordovaExtension.java
new file mode 100644
index 0000000..62943b4
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/CordovaExtension.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ * Copyright (c) 2011, Research In Motion Limited.
+ */
+package org.apache.cordova;
+
+import net.rim.device.api.browser.field2.BrowserField;
+import net.rim.device.api.script.ScriptEngine;
+import net.rim.device.api.system.Application;
+import net.rim.device.api.web.WidgetConfig;
+import net.rim.device.api.web.WidgetExtension;
+import net.rim.device.api.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.cordova.api.PluginManager;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.notification.Notification;
+import org.apache.cordova.util.Log;
+import org.apache.cordova.util.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * CordovaExtension is a BlackBerry WebWorks JavaScript extension. It
+ * represents a single feature that can be used to access device capabilities.
+ */
+public final class CordovaExtension implements WidgetExtension {
+
+ // Weak reference encapsulating BrowserField object used to display the application
+ // We use the weak reference because keeping a direct reference can
+ // cause memory leak issues in WebWorks. Original solution described
+ // and suggested by Tim Neil on the BlackBerry support forums.
+ // Thanks Tim!
+ protected static WeakReference browser = null;
+
+ // Browser script engine
+ //
+ protected static ScriptEngine script;
+
+ // Application name
+ //
+ protected static String appName;
+
+ // Application GUID
+ //
+ protected static long appID;
+
+ // Plugin Manager
+ //
+ protected PluginManager pluginManager;
+
+ // Feature ID
+ //
+ private static final String FEATURE_ID ="org.apache.cordova";
+
+ // Called when the BlackBerry Widget references this extension for the first time.
+ // It provides a list of feature IDs exposed by this extension.
+ //
+ public String[] getFeatureList() {
+ return new String[] {FEATURE_ID};
+ }
+
+ // Called whenever a widget loads a resource that requires a feature ID that is supplied
+ // in the getFeatureList
+ //
+ public void loadFeature(String feature, String version, Document doc,
+ ScriptEngine scriptEngine) throws Exception {
+ script = scriptEngine;
+ // Not sure why logger is not already enabled?
+ Logger.enableLogging();
+ if (feature.equals(FEATURE_ID)) {
+ pluginManager = new PluginManager(this);
+
+ // create and parse the plugins.xml
+ Document c = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(Application.class.getResourceAsStream("/plugins.xml"));
+
+ NodeList plugins = c.getElementsByTagName("plugin");
+ if (plugins.getLength() == 0) {
+ Logger.warn("If you are using any Cordova APIs you will need to "+
+ "specify them in the config.xml using <gap:plugin name=\"MyPlugin\" "+
+ "value=\"com.example.MyPlugin\"/>");
+ }
+ for(int i=0; i<plugins.getLength() ; i++){
+ Node plugin = plugins.item(i);
+ Logger.log("Found plugin " + plugin.getAttributes().getNamedItem("name").getNodeValue() + " = " +
+ plugin.getAttributes().getNamedItem("value").getNodeValue());
+ pluginManager.addService(plugin.getAttributes().getNamedItem("name").getNodeValue(),
+ plugin.getAttributes().getNamedItem("value").getNodeValue());
+ }
+
+ scriptEngine.addExtension("org.apache.cordova.JavaPluginManager", pluginManager);
+ scriptEngine.addExtension("org.apache.cordova.Logger", new Log());
+
+ // let Cordova JavaScript know that extensions have been loaded
+ // if this is premature, we at least set the _nativeReady flag to true
+ // so that when the JS side is ready, it knows native side is too
+ Logger.log(this.getClass().getName() + ": invoking Cordova.onNativeReady.fire()");
+ scriptEngine.executeScript("try {cordova.require('cordova/channel').onNativeReady.fire();} catch(e) {_nativeReady = true;}", null);
+ }
+ }
+
+ // Called so that the extension can get a reference to the configuration or browser field object
+ //
+ public void register(WidgetConfig widgetConfig, BrowserField browserField) {
+ browser = new WeakReference(browserField);
+
+ // grab widget application name and use it to generate a unique ID
+ appName = widgetConfig.getName();
+ appID = Long.parseLong(Math.abs(("org.apache.cordova."+appName).hashCode())+"",16);
+
+ // create a notification profile for the application
+ Notification.registerProfile();
+ }
+
+ /**
+ * Called to clean up any features when the extension is unloaded. This is
+ * invoked by the WebWorks Framework when another URL is loaded.
+ *
+ * @see net.rim.device.api.web.WidgetExtension#unloadFeatures(org.w3c.dom.Document)
+ */
+ public void unloadFeatures(Document doc) {
+ // Cleanup plugin resources.
+ if (pluginManager != null) {
+ pluginManager.destroy();
+ }
+ }
+
+ public static void invokeScript(final String js) {
+ // Use a new thread so that JavaScript is invoked asynchronously.
+ // Otherwise executeScript doesn't return until JavaScript call back
+ // is finished.
+ (new Thread() {
+ public void run() {
+ try {
+ script.executeScript(js, null);
+ } catch (Exception e) {
+ // This is likely an IllegalStateException which is thrown
+ // because the framework is in the process of being shutdown
+ // so communication to JavaScript side is not allowed.
+ Logger.log("Caught exception while executing script: "
+ + e.getMessage());
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Invokes the Cordova success callback specified by callbackId.
+ * @param callbackId unique callback ID
+ * @param result Cordova PluginResult containing result
+ */
+ public static void invokeSuccessCallback(String callbackId, PluginResult result) {
+ invokeScript(result.toSuccessCallbackString(callbackId));
+ }
+
+ /**
+ * Invokes the Cordova error callback specified by callbackId.
+ * @param callbackId unique callback ID
+ * @param result Cordova PluginResult containing result
+ */
+ public static void invokeErrorCallback(String callbackId, PluginResult result) {
+ invokeScript(result.toErrorCallbackString(callbackId));
+ }
+
+ /**
+ * Provides access to the browser instance for the application.
+ */
+ public static BrowserField getBrowserField() {
+ Object o = browser.get();
+ if ( o instanceof BrowserField ) {
+ return (BrowserField)o;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the widget application name.
+ */
+ public static String getAppName() {
+ return appName;
+ }
+
+ /**
+ * Returns unique ID of the widget application.
+ */
+ public static long getAppID() {
+ return appID;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java b/bbos/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
new file mode 100644
index 0000000..2088122
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/accelerometer/Accelerometer.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.accelerometer;
+
+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 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_START = "start";
+ private static final String ACTION_STOP = "stop";
+
+ private static final int STOPPED = 0;
+ private static final int STARTING = 1;
+ private static final int RUNNING = 2;
+ private static final int ERROR_FAILED_TO_START = 3;
+
+ // BlackBerry uses a value of 1000 (AccelerometerSensor.G_FORCE_VALUE) to
+ // represent g force constant. Spec uses m/s^2. This constant is used
+ // to normalize BlackBerry values to the spec.
+ private static final short G_FORCE_NORMALIZE = 981;
+
+ // the single channel to the device sensor
+ private static AccelerometerSensor.Channel _rawDataChannel = null;
+
+ private int state = STOPPED; // state of this listener
+ private long initTime = 0;
+
+ /**
+ * Reference to single start callbackid
+ */
+ private String callbackId;
+
+ public PluginResult execute(String action, JSONArray args, String callbackId) {
+ PluginResult result;
+ if (!AccelerometerSensor.isSupported()) {
+ result = new PluginResult(
+ PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION,
+ "Accelerometer sensor not supported");
+ } else if (ACTION_START.equals(action)) {
+ result = start(callbackId);
+ } else if (ACTION_STOP.equals(action)) {
+ result = stop();
+ } else {
+ result = new PluginResult(PluginResult.Status.INVALID_ACTION,
+ "Accelerometer: Invalid action:" + action);
+ }
+
+ return result;
+ }
+
+ /**
+ * 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) {
+ short x = accelData.getLastXAcceleration();
+ short y = accelData.getLastYAcceleration();
+ short z = accelData.getLastZAcceleration();
+
+ // 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;
+ }
+
+ if (state == RUNNING) {
+ // Send the new accelerometer data.
+ JSONObject accel = new JSONObject();
+ try {
+ 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);
+ }
+ } 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();
+ 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);
+ }
+ }
+
+ /**
+ * Called when Plugin is destroyed.
+ */
+ public void onDestroy() {
+ stop();
+ }
+
+ /**
+ * Adds a SystemListener to listen for changes to the battery state. The
+ * listener is only registered if one has not already been added.
+ */
+ private PluginResult start(String callbackId) {
+ this.callbackId = callbackId;
+ 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");
+ }
+
+ PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ return result;
+ }
+
+ /**
+ * Normalize the range of values returned by BlackBerry to the agreed upon
+ * cross platform range.
+ *
+ * @param value
+ * @return normalized value
+ */
+ private double normalize(short value) {
+ // Integer multiplication is less troublesome then floating point.
+ StringBuffer buf = new StringBuffer(String.valueOf(value
+ * G_FORCE_NORMALIZE));
+
+ // Manipulate the string to properly insert zeros and decimal point so
+ // something like -708910 becomes -7.08910 and 764 becomes .00764.
+ // Due to the values returned by BlackBerry there will always be 5
+ // decimal precision in the normalized value.
+ int idx = buf.charAt(0) == '-' ? 1 : 0;
+ while (buf.length() < (5 + idx)) {
+ buf.insert(idx, '0');
+ }
+ buf.insert(buf.length() - 5, '.');
+
+ return Double.parseDouble(buf.toString());
+ }
+
+ /**
+ * Helper function to send a PluginResult to the saved call back ID.
+ *
+ * @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);
+
+ if (issuccess) {
+ success(result, this.callbackId);
+ } else {
+ error(result, this.callbackId);
+ }
+ }
+ }
+
+ /**
+ * Stops accelerometer listener and closes the sensor channel.
+ */
+ private synchronized PluginResult 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;
+
+ return new PluginResult(PluginResult.Status.OK);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/api/IPlugin.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/api/IPlugin.java b/bbos/framework/ext/src/org/apache/cordova/api/IPlugin.java
new file mode 100644
index 0000000..7e09d31
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/api/IPlugin.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.api;
+
+import org.apache.cordova.CordovaExtension;
+import org.apache.cordova.json4j.JSONArray;
+
+/**
+ * Plugin interface must be implemented by any plugin classes.
+ *
+ * The execute method is called by the PluginManager.
+ */
+public interface IPlugin {
+
+ /**
+ * Executes the request and returns PluginResult.
+ *
+ * @param action The action to execute.
+ * @param args JSONArry of arguments for the plugin.
+ * @param callbackId The callback id used when calling back into JavaScript.
+ * @return A PluginResult object with a status and message.
+ */
+ PluginResult execute(String action, JSONArray args, String callbackId);
+
+ /**
+ * Identifies if action to be executed returns a value and should be run synchronously.
+ *
+ * @param action The action to execute
+ * @return T=returns value
+ */
+ public boolean isSynch(String action);
+
+ /**
+ * Sets the context of the Plugin. This can then be used to do things like
+ * get file paths associated with the Activity.
+ *
+ * @param ctx The main application class.
+ */
+ void setContext(CordovaExtension ctx);
+
+ /**
+ * Called when the system is about to start resuming a previous activity.
+ */
+ void onPause();
+
+ /**
+ * Called when the activity will start interacting with the user.
+ */
+ void onResume();
+
+ /**
+ * The final call you receive before your activity is destroyed.
+ */
+ void onDestroy();
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/api/Plugin.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/api/Plugin.java b/bbos/framework/ext/src/org/apache/cordova/api/Plugin.java
new file mode 100644
index 0000000..6f70b85
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/api/Plugin.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.api;
+
+import org.apache.cordova.CordovaExtension;
+import org.apache.cordova.json4j.JSONArray;
+
+/**
+ * Plugin interface must be implemented by any plugin classes.
+ *
+ * The execute method is called by the PluginManager.
+ */
+public abstract class Plugin implements IPlugin {
+
+ public CordovaExtension ctx; // Main application object
+
+ /**
+ * Executes the request and returns PluginResult.
+ *
+ * @param action The action to execute.
+ * @param args JSONArry of arguments for the plugin.
+ * @param callbackId The callback id used when calling back into JavaScript.
+ * @return A PluginResult object with a status and message.
+ */
+ public abstract PluginResult execute(String action, JSONArray args, String callbackId);
+
+ /**
+ * Identifies if action to be executed returns a value and should be run synchronously.
+ *
+ * @param action The action to execute
+ * @return T=returns value
+ */
+ public boolean isSynch(String action) {
+ return false;
+ }
+
+ /**
+ * Sets the context of the Plugin. This can then be used to do things like
+ * get file paths associated with the Activity.
+ *
+ * @param ctx The context of the main Activity.
+ */
+ public void setContext(CordovaExtension ctx) {
+ this.ctx = ctx;
+ }
+
+ /**
+ * Called when Plugin is paused.
+ */
+ public void onPause() {
+ }
+
+ /**
+ * Called when Plugin is resumed.
+ */
+ public void onResume() {
+ }
+
+ /**
+ * Called when Plugin is destroyed.
+ */
+ public void onDestroy() {
+ }
+
+ /**
+ * Send generic JavaScript statement back to JavaScript.
+ * success(...) and error(...) should be used instead where possible.
+ *
+ * @param statement
+ */
+ public void invokeScript(String statement) {
+ CordovaExtension.invokeScript(statement);
+ }
+
+ /**
+ * Call the JavaScript success callback for this plugin.
+ *
+ * This can be used if the execute code for the plugin is asynchronous meaning
+ * that execute should return null and the callback from the async operation can
+ * call success(...) or error(...)
+ *
+ * @param pluginResult The result to return.
+ * @param callbackId The callback id used when calling back into JavaScript.
+ */
+ public static void success(PluginResult pluginResult, String callbackId) {
+ CordovaExtension.invokeSuccessCallback(callbackId, pluginResult);
+ }
+
+ /**
+ * Call the JavaScript error callback for this plugin.
+ *
+ * @param pluginResult The result to return.
+ * @param callbackId The callback id used when calling back into JavaScript.
+ */
+ public static void error(PluginResult pluginResult, String callbackId) {
+ CordovaExtension.invokeErrorCallback(callbackId, pluginResult);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/api/PluginManager.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/api/PluginManager.java b/bbos/framework/ext/src/org/apache/cordova/api/PluginManager.java
new file mode 100644
index 0000000..a8e5e9b
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/api/PluginManager.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.api;
+
+import java.util.Hashtable;
+
+import org.apache.cordova.CordovaExtension;
+import org.apache.cordova.util.FileUtils;
+import org.apache.cordova.util.Logger;
+
+import net.rim.device.api.script.Scriptable;
+import net.rim.device.api.script.ScriptableFunction;
+
+/**
+ * PluginManager represents an object in the script engine. It can be accessed
+ * from the script environment using <code>cordova.PluginManager</code>.
+ *
+ * PluginManager provides a function, <code>exec</code>, that can be invoked
+ * from the script environment: <code>cordova.PluginManager.exec(...)</code>.
+ * Invoking this function causes the script engine to load the appropriate
+ * Cordova Plugin and perform the specified action.
+ */
+public final class PluginManager extends Scriptable {
+
+ /**
+ * Field used to invoke Plugin actions.
+ */
+ public static String FIELD_EXEC = "exec";
+
+ /**
+ * Field used to cleanup Plugins.
+ */
+ public static String FIELD_DESTROY = "destroy";
+
+ /**
+ * Field used to indicate application has been brought to foreground.
+ */
+ public static String FIELD_RESUME = "resume";
+
+ /**
+ * Field used to indicate application has been sent to background
+ */
+ public static String FIELD_PAUSE = "pause";
+
+ /**
+ * Field used to register a Plugin.
+ */
+ public static String FIELD_ADD_PLUGIN = "addPlugin";
+
+ /**
+ * Loads the appropriate Cordova Plugins and invokes their actions.
+ */
+ private final PluginManagerFunction pluginManagerFunction;
+
+ /**
+ * Maps available services to Java class names.
+ */
+ private Hashtable services = new Hashtable();
+
+ /**
+ * Constructor. Adds available Cordova services.
+ * @param ext The Cordova JavaScript Extension
+ */
+ public PluginManager(CordovaExtension ext) {
+ this.pluginManagerFunction = new PluginManagerFunction(ext, this);
+ }
+
+ /**
+ * The following fields are supported from the script environment:
+ *
+ * <code>cordova.pluginManager.exec</code> - Loads the appropriate
+ * Plugin and invokes the specified action.
+ *
+ * <code>cordova.pluginManager.destroy</code> - Invokes the <code>onDestroy</code>
+ * method on all Plugins to give them a chance to cleanup before exit.
+ */
+ public Object getField(String name) throws Exception {
+ if (name.equals(FIELD_EXEC)) {
+ return this.pluginManagerFunction;
+ }
+ else if (name.equals(FIELD_DESTROY)) {
+ return new ScriptableFunction() {
+ public Object invoke(Object obj, Object[] oargs) throws Exception {
+ destroy();
+ return null;
+ }
+ };
+ }
+ else if (name.equals(FIELD_RESUME)) {
+ final PluginManagerFunction plugin_mgr = this.pluginManagerFunction;
+ return new ScriptableFunction() {
+ public Object invoke(Object obj, Object[] oargs) throws Exception {
+ plugin_mgr.onResume();
+ return null;
+ }
+ };
+ }
+ else if (name.equals(FIELD_PAUSE)) {
+ final PluginManagerFunction plugin_mgr = this.pluginManagerFunction;
+ return new ScriptableFunction() {
+ public Object invoke(Object obj, Object[] oargs) throws Exception {
+ plugin_mgr.onPause();
+ return null;
+ }
+ };
+ }
+ else if (name.equals(FIELD_ADD_PLUGIN)) {
+ Logger.log("Plugins are now added through the plugins.xml in the application root.");
+ }
+ return super.getField(name);
+ }
+
+ /**
+ * Add a class that implements a service.
+ *
+ * @param serviceName The service name.
+ * @param className The Java class name that implements the service.
+ */
+ public void addService(String serviceName, String className) {
+ this.services.put(serviceName, className);
+ }
+
+ /**
+ * Cleanup the plugin resources and delete temporary directory that may have
+ * been created.
+ */
+ public void destroy() {
+ // allow plugins to clean up
+ pluginManagerFunction.onDestroy();
+
+ // delete temporary application directory
+ // NOTE: doing this on a background thread doesn't work because the app
+ // is closing and the thread is killed before it completes.
+ try {
+ FileUtils.deleteApplicationTempDirectory();
+ } catch (Exception e) {
+ Logger.log(this.getClass().getName()
+ + ": error deleting application temp directory: "
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Get the class that implements a service.
+ *
+ * @param serviceName The service name.
+ * @return The Java class name that implements the service.
+ */
+ public String getClassForService(String serviceName) {
+ return (String)this.services.get(serviceName);
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/api/PluginManagerFunction.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/api/PluginManagerFunction.java b/bbos/framework/ext/src/org/apache/cordova/api/PluginManagerFunction.java
new file mode 100644
index 0000000..13a0e77
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/api/PluginManagerFunction.java
@@ -0,0 +1,240 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.api;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import net.rim.device.api.script.ScriptableFunction;
+
+import org.apache.cordova.CordovaExtension;
+import org.apache.cordova.json4j.JSONArray;
+import org.apache.cordova.json4j.JSONException;
+import org.apache.cordova.util.Logger;
+
+/**
+ * PluginManagerFunction represents a function that can be invoked from the
+ * script environment of the widget framework. It manages the plugins for
+ * the Cordova JavaScript Extension.
+ *
+ * Calling <code>cordova.pluginManager.exec(...)</code> from JavaScript will
+ * result in this class' <code>invoke()</code> method being called.
+ */
+public class PluginManagerFunction extends ScriptableFunction {
+
+ private final static int ARG_SERVICE = 0;
+ private final static int ARG_ACTION = 1;
+ private final static int ARG_CALLBACK_ID = 2;
+ private final static int ARG_ARGS = 3;
+ private final static int ARG_ASYNC = 4;
+
+ private Hashtable plugins = new Hashtable();
+
+ private final CordovaExtension ext;
+ private final PluginManager pluginManager;
+
+ /**
+ * Constructor.
+ * @param ext The Cordova JavaScript Extension
+ * @param pluginManager The PluginManager that exposes the scriptable object.
+ */
+ public PluginManagerFunction(CordovaExtension ext, PluginManager pluginManager) {
+ this.ext = ext;
+ this.pluginManager = pluginManager;
+ }
+
+ /**
+ * The invoke method is called when cordova.pluginManager.exec(...) is
+ * used from the script environment. It instantiates the appropriate plugin
+ * and invokes the specified action. JavaScript arguments are passed in
+ * as an array of objects.
+ *
+ * @param service String containing the service to run
+ * @param action String containing the action that the service is supposed to perform. This is
+ * passed to the plugin execute method and it is up to the plugin developer
+ * how to deal with it.
+ * @param callbackId String containing the id of the callback that is executed in JavaScript if
+ * this is an async plugin call.
+ * @param args An Array literal string containing any arguments needed in the
+ * plugin execute method.
+ * @param async Boolean indicating whether the calling JavaScript code is expecting an
+ * immediate return value. If true, either CordovaExtension.callbackSuccess(...) or
+ * CordovaExtension.callbackError(...) is called once the plugin code has executed.
+ *
+ * @return JSON encoded string with a response message and status.
+ *
+ * @see net.rim.device.api.script.ScriptableFunction#invoke(java.lang.Object, java.lang.Object[])
+ */
+ public Object invoke(Object obj, Object[] oargs) throws Exception {
+ final String service = (String)oargs[ARG_SERVICE];
+ final String action = (String)oargs[ARG_ACTION];
+ final String callbackId = (String)oargs[ARG_CALLBACK_ID];
+ boolean async = (oargs[ARG_ASYNC].toString().equals("true") ? true : false);
+ PluginResult pr = null;
+
+ try {
+ // action arguments
+ final JSONArray args = new JSONArray((String)oargs[ARG_ARGS]);
+
+ // get the class for the specified service
+ String clazz = this.pluginManager.getClassForService(service);
+ Class c = null;
+ if (clazz != null) {
+ c = getClassByName(clazz);
+ }
+
+ if (isCordovaPlugin(c)) {
+ // Create a new instance of the plugin and set the context
+ final Plugin plugin = this.loadPlugin(clazz, c);
+ async = async && !plugin.isSynch(action);
+ if (async) {
+ // Run this async on a background thread so that JavaScript can continue on
+ Thread thread = new Thread(new Runnable() {
+ public void run() {
+ // Call execute on the plugin so that it can do it's thing
+ final PluginResult result = plugin.execute(action, args, callbackId);
+
+ if (result != null) {
+ int status = result.getStatus();
+
+ // If plugin status is OK,
+ // or plugin is not going to send an immediate result (NO_RESULT)
+ if (status == PluginResult.Status.OK.ordinal() ||
+ status == PluginResult.Status.NO_RESULT.ordinal()) {
+ CordovaExtension.invokeSuccessCallback(callbackId, result);
+ }
+ // error
+ else {
+ CordovaExtension.invokeErrorCallback(callbackId, result);
+ }
+ }
+ }
+ });
+ thread.start();
+ return "";
+ } else {
+ // Call execute on the plugin so that it can do it's thing
+ pr = plugin.execute(action, args, callbackId);
+ }
+ }
+ } catch (ClassNotFoundException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ pr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION, "ClassNotFoundException: " + e.getMessage());
+ } catch (IllegalAccessException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ pr = new PluginResult(PluginResult.Status.ILLEGAL_ACCESS_EXCEPTION, "IllegalAccessException:" + e.getMessage());
+ } catch (InstantiationException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ pr = new PluginResult(PluginResult.Status.INSTANTIATION_EXCEPTION, "InstantiationException: " + e.getMessage());
+ } catch (JSONException e) {
+ Logger.log(this.getClass().getName() + ": " + e);
+ pr = new PluginResult(PluginResult.Status.JSON_EXCEPTION, "JSONException: " + e.getMessage());
+ }
+ // if async we have already returned at this point unless there was an error...
+ if (async) {
+ CordovaExtension.invokeErrorCallback(callbackId, pr);
+ }
+ return ( pr != null ? pr.getJSONString() : "{ status: 0, message: 'all good' }" );
+ }
+
+ /**
+ * Get the class.
+ *
+ * @param clazz
+ * @return
+ * @throws ClassNotFoundException
+ */
+ private Class getClassByName(final String clazz) throws ClassNotFoundException {
+ return Class.forName(clazz);
+ }
+
+ /**
+ * Determines if the class implements org.apache.cordova.api.Plugin interface.
+ *
+ * @param c The class to check.
+ * @return Boolean indicating if the class implements org.apache.cordova.api.Plugin
+ */
+ private boolean isCordovaPlugin(Class c) {
+ if (c != null) {
+ return org.apache.cordova.api.Plugin.class.isAssignableFrom(c) || org.apache.cordova.api.IPlugin.class.isAssignableFrom(c);
+ }
+ return false;
+ }
+
+ /**
+ * Add plugin to be loaded and cached.
+ * If plugin is already created, then just return it.
+ *
+ * @param className The class to load
+ * @return The plugin
+ */
+ public Plugin loadPlugin(String className, Class clazz) throws IllegalAccessException, InstantiationException {
+ if (this.plugins.containsKey(className)) {
+ return this.getPlugin(className);
+ }
+ Logger.log(this.getClass().getName() + ": Loading plugin " + clazz);
+ Plugin plugin = (Plugin)clazz.newInstance();
+ this.plugins.put(className, plugin);
+ plugin.setContext(this.ext);
+ return plugin;
+ }
+
+ /**
+ * Get the loaded plugin.
+ *
+ * @param className The class of the loaded plugin.
+ * @return
+ */
+ public Plugin getPlugin(String className) {
+ return (Plugin)this.plugins.get(className);
+ }
+
+ /**
+ * Called when application is paused.
+ */
+ public void onPause() {
+ Enumeration e = this.plugins.elements();
+ while (e.hasMoreElements()) {
+ Plugin plugin = (Plugin)e.nextElement();
+ plugin.onPause();
+ }
+ }
+
+ /**
+ * Called when application is resumed.
+ */
+ public void onResume() {
+ Enumeration e = this.plugins.elements();
+ while (e.hasMoreElements()) {
+ Plugin plugin = (Plugin)e.nextElement();
+ plugin.onResume();
+ }
+ }
+
+ /**
+ * Called when application is destroyed.
+ */
+ public void onDestroy() {
+ Enumeration e = this.plugins.elements();
+ while (e.hasMoreElements()) {
+ Plugin plugin = (Plugin)e.nextElement();
+ plugin.onDestroy();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/api/PluginResult.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/api/PluginResult.java b/bbos/framework/ext/src/org/apache/cordova/api/PluginResult.java
new file mode 100644
index 0000000..ce84884
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/api/PluginResult.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.api;
+
+import org.apache.cordova.json4j.JSONArray;
+import org.apache.cordova.json4j.JSONObject;
+
+/**
+ * This class defines the standard object that should be returned as the
+ * result of any Cordova plugin invocation.
+ */
+public class PluginResult {
+
+ private final int status;
+ private final String message;
+ private boolean keepCallback = false;
+
+ public PluginResult(Status status) {
+ this.status = status.ordinal();
+ this.message = JSONObject.quote(status.getMessage());
+ }
+
+ public PluginResult(Status status, String message) {
+ this.status = status.ordinal();
+ this.message = JSONObject.quote(message);
+ }
+
+ public PluginResult(Status status, JSONArray message) {
+ this.status = status.ordinal();
+ this.message = message.toString();
+ }
+
+ public PluginResult(Status status, JSONObject message) {
+ this.status = status.ordinal();
+ this.message = (message != null) ? message.toString(): "null";
+ }
+
+ public PluginResult(Status status, int i) {
+ this.status = status.ordinal();
+ this.message = ""+i;
+ }
+
+ public PluginResult(Status status, float f) {
+ this.status = status.ordinal();
+ this.message = ""+f;
+ }
+
+ public PluginResult(Status status, boolean b) {
+ this.status = status.ordinal();
+ this.message = ""+b;
+ }
+
+ public PluginResult(Status status, long l) {
+ this.status = status.ordinal();
+ this.message = ""+l;
+ }
+
+ public int getStatus() {
+ return status;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setKeepCallback(boolean b) {
+ this.keepCallback = b;
+ }
+
+ public boolean getKeepCallback() {
+ return this.keepCallback;
+ }
+
+ public String getJSONString() {
+ return "{\"status\":" + this.status + ",\"message\":" + this.message + ",\"keepCallback\":" + this.keepCallback + "}";
+ }
+
+ /**
+ * Returns the JavaScript string that executes the success callback for the
+ * appropriate Cordova plugin. The string is intended to be passed to the
+ * JavaScript engine.
+ * @param callbackId Unique id of the callback that is associated with the invoked plugin
+ * @return JavaScript string that invokes the appropriate plugin success callback
+ */
+ public String toSuccessCallbackString(String callbackId) {
+ return "try { cordova.callbackSuccess('"+callbackId+"', " + this.getJSONString() + "); } catch(e) { alert('error in callbackSuccess:' + e.message); }";
+ }
+
+ /**
+ * Returns the JavaScript string that executes the error callback for the
+ * appropriate Cordova plugin. The string is intended to be passed to the
+ * JavaScript engine.
+ * @param callbackId Unique id of the callback that is associated with the invoked plugin
+ * @return JavaScript string that invokes the appropriate plugin error callback
+ */
+ public String toErrorCallbackString(String callbackId) {
+ return "try { cordova.callbackError('"+callbackId+"', " + this.getJSONString() + "); } catch(e) { alert('error in callbackError:' + e.message); }";
+ }
+
+ public String toErrorString() {
+ return "alert('general error');";
+ }
+
+ /**
+ * Enumerates PluginResult status.
+ */
+ public static class Status
+ {
+ private int val;
+ private String message;
+
+ protected Status(int val, String message) {
+ this.val = val;
+ this.message = message;
+ }
+
+ public int ordinal() {
+ return this.val;
+ }
+
+ public String getMessage() {
+ return this.message;
+ }
+
+ public static final Status NO_RESULT = new Status(0, "No result");
+ public static final Status OK = new Status(1, "OK");
+ public static final Status CLASS_NOT_FOUND_EXCEPTION = new Status(2, "Class not found");
+ public static final Status ILLEGAL_ACCESS_EXCEPTION = new Status(3, "Illegal access");
+ public static final Status INSTANTIATION_EXCEPTION = new Status(4, "Instantiation error");
+ public static final Status MALFORMED_URL_EXCEPTION = new Status(5, "Malformed URL");
+ public static final Status IO_EXCEPTION = new Status(6, "IO error");
+ public static final Status INVALID_ACTION = new Status(7, "Invalid action");
+ public static final Status JSON_EXCEPTION = new Status(8, "JSON error");
+ public static final Status ERROR = new Status(9, "Error");
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/app/App.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/app/App.java b/bbos/framework/ext/src/org/apache/cordova/app/App.java
new file mode 100644
index 0000000..38ae205
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/app/App.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.app;
+
+import org.apache.cordova.CordovaExtension;
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.apache.cordova.json4j.JSONArray;
+
+import net.rim.device.api.browser.field2.BrowserFieldHistory;
+import net.rim.device.api.system.Application;
+import net.rim.device.api.system.SystemListener2;
+
+/**
+ * The App plug-in. This class provides access to application specific
+ * management. The following actions are supported:
+ *
+ * clearHistory - Clear the browser history.
+ * backHistory - Navigate back in the browser history.
+ * detectBacklight - Start a system listener for backlight changes.
+ * ignoreBacklight - Stop the system listener for backlight changes.
+ */
+public class App extends Plugin {
+
+ private final static String ACTION_CLEAR_HISTORY = "clearHistory";
+ private final static String ACTION_BACK_HISTORY = "backHistory";
+ private final static String ACTION_DETECT_BACKLIGHT = "detectBacklight";
+ private final static String ACTION_IGNORE_BACKLIGHT = "ignoreBacklight";
+
+ private SystemListener2 listener = null;
+ private String callbackId = 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,
+ final String callbackId) {
+ PluginResult result = null;
+
+ if (ACTION_CLEAR_HISTORY.equals(action)) {
+ BrowserFieldHistory history = CordovaExtension.getBrowserField()
+ .getHistory();
+ if (history != null) {
+ history.clearHistory();
+ }
+ result = new PluginResult(PluginResult.Status.OK);
+ } else if (ACTION_BACK_HISTORY.equals(action)) {
+ CordovaExtension.getBrowserField().back();
+ result = new PluginResult(PluginResult.Status.OK);
+ } else if (ACTION_DETECT_BACKLIGHT.equals(action)) {
+ addListener(callbackId);
+ result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ } else if (ACTION_IGNORE_BACKLIGHT.equals(action)) {
+ removeListener();
+ result = new PluginResult(PluginResult.Status.OK);
+ } else {
+ result = new PluginResult(PluginResult.Status.INVALID_ACTION,
+ "App: Invalid action: " + action);
+ }
+
+ return result;
+ }
+
+ /**
+ * Called when Plugin is destroyed.
+ */
+ public void onDestroy() {
+ removeListener();
+ }
+
+ /**
+ * Register a system listener for backlight changes if one has not already
+ * been registered.
+ *
+ * @param callbackId
+ * the callback ID associated with the system listener
+ */
+ private synchronized void addListener(final String callbackId) {
+ if (listener == null) {
+ listener = new SystemListener2() {
+ public void batteryGood() {}
+ public void batteryLow() {}
+ public void batteryStatusChange(int status) {}
+ public void powerOff() {}
+ public void powerUp() {}
+
+ public void backlightStateChange(boolean on) {
+ PluginResult result = new PluginResult(
+ PluginResult.Status.OK, on);
+
+ // Must keep the call back active for future events.
+ result.setKeepCallback(true);
+ success(result, callbackId);
+ }
+
+ public void cradleMismatch(boolean mismatch) {}
+ public void fastReset() {}
+ public void powerOffRequested(int reason) {}
+ public void usbConnectionStateChange(int state) {}
+ };
+
+ this.callbackId = callbackId;
+ Application.getApplication().addSystemListener(listener);
+ }
+ }
+
+ /**
+ * Remove the system listener if it is registered and close out the
+ * callback handler.
+ */
+ private synchronized void removeListener() {
+ if (listener != null) {
+ Application.getApplication().removeSystemListener(listener);
+ listener = null;
+
+ if (callbackId != null) {
+ success(new PluginResult(PluginResult.Status.NO_RESULT),
+ callbackId);
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/battery/Battery.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/battery/Battery.java b/bbos/framework/ext/src/org/apache/cordova/battery/Battery.java
new file mode 100644
index 0000000..55356a5
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/battery/Battery.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.battery;
+
+import java.util.Enumeration;
+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;
+
+import net.rim.device.api.system.Application;
+import net.rim.device.api.system.DeviceInfo;
+import net.rim.device.api.system.SystemListener;
+
+/**
+ * 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());
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/camera/Camera.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/camera/Camera.java b/bbos/framework/ext/src/org/apache/cordova/camera/Camera.java
new file mode 100644
index 0000000..d54483f
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/camera/Camera.java
@@ -0,0 +1,470 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.camera;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Date;
+
+import javax.microedition.io.Connector;
+import javax.microedition.io.file.FileConnection;
+
+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.util.Logger;
+
+import net.rim.blackberry.api.invoke.CameraArguments;
+import net.rim.blackberry.api.invoke.Invoke;
+import net.rim.device.api.io.Base64OutputStream;
+import net.rim.device.api.io.IOUtilities;
+import net.rim.device.api.system.ApplicationDescriptor;
+import net.rim.device.api.system.Bitmap;
+import net.rim.device.api.system.Characters;
+import net.rim.device.api.system.ControlledAccessException;
+import net.rim.device.api.system.EncodedImage;
+import net.rim.device.api.system.EventInjector;
+import net.rim.device.api.system.JPEGEncodedImage;
+import net.rim.device.api.system.PNGEncodedImage;
+import net.rim.device.api.ui.UiApplication;
+
+/**
+ * The Camera plugin interface.
+ *
+ * The Camera class can invoke the following actions:
+ *
+ * - takePicture: takes photo and returns base64 encoded image or image file URI
+ *
+ * future?
+ * - captureVideo...
+ *
+ */
+public class Camera extends Plugin
+{
+ /**
+ * Possible actions.
+ */
+ public static final String ACTION_TAKE_PICTURE = "takePicture";
+
+ /**
+ * Maximum image encoding size (in bytes) to allow. (Obtained unofficially
+ * through trial and error). Anything larger will cause stability issues
+ * when sending back to the browser.
+ */
+ private static final long MAX_ENCODING_SIZE = 1500000L;
+
+ /**
+ * 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;
+
+ // take a picture
+ if (action != null && action.equals(ACTION_TAKE_PICTURE))
+ {
+ // Parse the options specified for the take picture action.
+ CameraOptions options;
+ try {
+ options = CameraOptions.fromJSONArray(args);
+ } catch (NumberFormatException e) {
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "One of the camera options is not a valid number.");
+ } catch (JSONException e) {
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "One of the camera options is not valid JSON.");
+ }
+
+ // launch native camera application
+ launchCamera(new PhotoListener(options, callbackId));
+
+ // The native camera application runs in a separate process, so we
+ // must now wait for the listener to retrieve the photo taken.
+ // Return NO_RESULT status so plugin manager does not invoke a callback,
+ // but keep the callback so the listener can invoke it later.
+ result = new PluginResult(PluginResult.Status.NO_RESULT);
+ result.setKeepCallback(true);
+ return result;
+ }
+ else
+ {
+ result = new PluginResult(PluginResult.Status.INVALID_ACTION, "Camera: Invalid action:" + action);
+ }
+
+ return result;
+ }
+
+ /**
+ * Launches the native camera application.
+ */
+ private static void launchCamera(PhotoListener listener)
+ {
+ // MMAPI interface doesn't use the native Camera application or interface
+ // (we would have to replicate it). So, we invoke the native Camera application,
+ // which doesn't allow us to set any options.
+ synchronized(UiApplication.getEventLock()) {
+ UiApplication.getUiApplication().addFileSystemJournalListener(listener);
+ Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA, new CameraArguments());
+ }
+ }
+
+ /**
+ * Closes the native camera application.
+ */
+ public static void closeCamera()
+ {
+ // simulate two escape characters to exit native camera application
+ // no, there is no other way to do this
+ UiApplication.getUiApplication().invokeLater(new Runnable() {
+ public void run() {
+ try
+ {
+ EventInjector.KeyEvent inject = new EventInjector.KeyEvent(
+ EventInjector.KeyEvent.KEY_DOWN, Characters.ESCAPE, 0);
+ inject.post();
+ inject.post();
+ }
+ catch (ControlledAccessException e)
+ {
+ // the application doesn't have key injection permissions
+ Logger.log(Camera.class.getName() + ": Unable to close camera. " +
+ ApplicationDescriptor.currentApplicationDescriptor().getName() +
+ " does not have key injection permissions.");
+ }
+ }
+ });
+ }
+
+ /**
+ * Returns the image file URI or the Base64-encoded image.
+ * @param filePath The full path of the image file
+ * @param options Specifies the format of the image and the result
+ * @param callbackId The id of the callback to receive the result
+ */
+ public static void processImage(String filePath, CameraOptions options,
+ String callbackId) {
+ PluginResult result = null;
+ try
+ {
+ // wait for the file to be fully written to the file system
+ // to avoid premature access to it (yes, this has happened)
+ waitForImageFile(filePath);
+
+ // Reformat the image if the specified options require it,
+ // otherwise, get encoded string if base 64 string is output format.
+ String imageURIorData = filePath;
+
+ // save to file:///store/home/user/ as oppsed to photo album
+ // so it doesn't show up in the camera's photo album viewer
+ if(!options.saveToPhotoAlbum){
+ FileConnection fconnIn = null;
+ FileConnection fconnOut = null;
+ InputStream in = null;
+ OutputStream out = null;
+ String newOutName = "";
+ try
+ {
+ fconnIn = (FileConnection)Connector.open(filePath);
+ if (fconnIn.exists())
+ {
+ newOutName = "file:///store/home/user/"+fconnIn.getName();
+ fconnOut = (FileConnection)Connector.open(newOutName);
+ if (!fconnOut.exists())
+ {
+ fconnOut.create();
+ in = fconnIn.openInputStream();
+ out = fconnOut.openOutputStream();
+ out.write(IOUtilities.streamToBytes(in, 96*1024));
+ fconnIn.delete();
+ out.close();
+ imageURIorData = newOutName;
+ filePath = newOutName;
+ waitForImageFile(newOutName);
+ }
+ }
+ }
+ finally
+ {
+ if (in != null) in.close();
+ if (out != null) out.close();
+ if (fconnIn != null) fconnIn.close();
+ if (fconnOut != null) fconnOut.close();
+ }
+
+ }
+
+ if (options.reformat) {
+ imageURIorData = reformatImage(filePath, options);
+ } else if (options.destinationType == CameraOptions.DESTINATION_DATA_URL) {
+ imageURIorData = encodeImage(filePath);
+ }
+
+ // we have to check the size to avoid memory errors in the browser
+ if (imageURIorData.length() > MAX_ENCODING_SIZE)
+ {
+ // it's a big one. this is for your own good.
+ String msg = "Encoded image is too large. Try reducing camera image size.";
+ Logger.log(Camera.class.getName() + ": " + msg);
+ result = new PluginResult(PluginResult.Status.ERROR, msg);
+ }
+ else
+ {
+ result = new PluginResult(PluginResult.Status.OK, imageURIorData);
+ }
+ }
+ catch (Exception e)
+ {
+ result = new PluginResult(PluginResult.Status.IO_EXCEPTION, e.toString());
+ }
+
+ // send result back to JavaScript
+ sendResult(result, callbackId);
+ }
+
+ /**
+ * Waits for the image file to be fully written to the file system.
+ * @param filePath Full path of the image file
+ * @throws IOException
+ */
+ private static void waitForImageFile(String filePath) throws IOException
+ {
+ long start = (new Date()).getTime();
+ FileConnection fconn = null;
+ try
+ {
+ fconn = (FileConnection)Connector.open(filePath, Connector.READ);
+ if (fconn.exists())
+ {
+ long fileSize = fconn.fileSize();
+ long size = 0;
+ while (true)
+ {
+ try { Thread.sleep(100); } catch (InterruptedException e) {}
+ size = fconn.fileSize();
+ if (size == fileSize) {
+ break;
+ }
+ fileSize = size;
+ }
+ Logger.log(Camera.class.getName() + ": " + filePath +
+ " size=" + Long.toString(fileSize) + " bytes");
+ }
+ }
+ finally
+ {
+ if (fconn != null) fconn.close();
+ }
+ long end = (new Date()).getTime();
+ Logger.log(Camera.class.getName() + ": wait time=" + Long.toString(end-start) + " ms");
+ }
+
+ /**
+ * Opens the specified image file and converts its contents to a Base64-encoded string.
+ * @param filePath Full path of the image file
+ * @return file contents as a Base64-encoded String
+ */
+ private static String encodeImage(String filePath) throws IOException
+ {
+ String imageData = null;
+
+ // open the image file
+ FileConnection fconn = null;
+ InputStream in = null;
+ ByteArrayOutputStream byteArrayOS = null;
+ try
+ {
+ fconn = (FileConnection)Connector.open(filePath);
+ if (fconn.exists())
+ {
+ // encode file contents using BASE64 encoding
+ in = fconn.openInputStream();
+ byteArrayOS = new ByteArrayOutputStream();
+ Base64OutputStream base64OS = new Base64OutputStream(byteArrayOS);
+ base64OS.write(IOUtilities.streamToBytes(in, 96*1024));
+ base64OS.flush();
+ base64OS.close();
+ imageData = byteArrayOS.toString();
+
+ Logger.log(Camera.class.getName() + ": Base64 encoding size=" +
+ Integer.toString(imageData.length()));
+ }
+ }
+ finally
+ {
+ if (in != null) in.close();
+ if (fconn != null) fconn.close();
+ if (byteArrayOS != null) byteArrayOS.close();
+ }
+
+ return imageData;
+ }
+
+ /**
+ * Reformats the image taken with the camera based on the options specified.
+ *
+ * Unfortunately, reformatting the image will cause EXIF data in the photo
+ * to be lost. Most importantly the orientation data is lost so the
+ * picture is not auto rotated by software that recognizes EXIF data.
+ *
+ * @param filePath
+ * The full path of the image file
+ * @param options
+ * Specifies the format of the image and the result
+ * @return the reformatted image file URI or Base64-encoded image
+ * @throws IOException
+ */
+ private static String reformatImage(String filePath, CameraOptions options)
+ throws IOException {
+ long start = (new Date()).getTime();
+
+ // Open the original image created by the camera application and read
+ // it into an EncodedImage object.
+ FileConnection fconn = null;
+ InputStream in = null;
+ Bitmap originalImage = null;
+ try {
+ fconn = (FileConnection) Connector.open(filePath);
+ in = fconn.openInputStream();
+ originalImage = Bitmap.createBitmapFromBytes(IOUtilities.streamToBytes(in, 96*1024), 0, -1, 1);
+ } finally {
+ if (in != null)
+ in.close();
+ if (fconn != null)
+ fconn.close();
+ }
+
+ int newWidth = options.targetWidth;
+ int newHeight = options.targetHeight;
+ int origWidth = originalImage.getWidth();
+ int origHeight = originalImage.getHeight();
+
+ // If only width or only height was specified, the missing dimension is
+ // set based on the current aspect ratio of the image.
+ if (newWidth > 0 && newHeight <= 0) {
+ newHeight = (newWidth * origHeight) / origWidth;
+ } else if (newWidth <= 0 && newHeight > 0) {
+ newWidth = (newHeight * origWidth) / origHeight;
+ } else if (newWidth <= 0 && newHeight <= 0) {
+ newWidth = origWidth;
+ newHeight = origHeight;
+ } else {
+ // If the user specified both a positive width and height
+ // (potentially different aspect ratio) then the width or height is
+ // scaled so that the image fits while maintaining aspect ratio.
+ // Alternatively, the specified width and height could have been
+ // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
+ // would result in whitespace in the new image.
+ double newRatio = newWidth / (double)newHeight;
+ double origRatio = origWidth / (double)origHeight;
+
+ if (origRatio > newRatio) {
+ newHeight = (newWidth * origHeight) / origWidth;
+ } else if (origRatio < newRatio) {
+ newWidth = (newHeight * origWidth) / origHeight;
+ }
+ }
+
+ Bitmap newImage = new Bitmap(newWidth, newHeight);
+ originalImage.scaleInto(newImage, options.imageFilter, Bitmap.SCALE_TO_FILL);
+
+ // Convert the image to the appropriate encoding. PNG does not allow
+ // quality to be specified so the only affect that the quality option
+ // has for a PNG is on the seelction of the image filter.
+ EncodedImage encodedImage;
+ if (options.encoding == CameraOptions.ENCODING_PNG) {
+ encodedImage = PNGEncodedImage.encode(newImage);
+ } else {
+ encodedImage = JPEGEncodedImage.encode(newImage, options.quality);
+ }
+
+ // Rewrite the modified image back out to the same file. This is done
+ // to ensure that for every picture taken, only one shows up in the
+ // gallery. If the encoding changed the file extension will differ
+ // from the original.
+ OutputStream out = null;
+ int dirIndex = filePath.lastIndexOf('/');
+ String filename = filePath.substring(dirIndex + 1, filePath.lastIndexOf('.'))
+ + options.fileExtension;
+ try {
+ fconn = (FileConnection) Connector.open(filePath);
+ fconn.truncate(0);
+ out = fconn.openOutputStream();
+ out.write(encodedImage.getData());
+ fconn.rename(filename);
+ } finally {
+ if (out != null)
+ out.close();
+ if (fconn != null)
+ fconn.close();
+ }
+
+ // Return either the Base64-encoded string or the image URI for the
+ // new image.
+ String imageURIorData;
+ if (options.destinationType == CameraOptions.DESTINATION_DATA_URL) {
+ ByteArrayOutputStream byteArrayOS = null;
+
+ try {
+ byteArrayOS = new ByteArrayOutputStream();
+ Base64OutputStream base64OS = new Base64OutputStream(
+ byteArrayOS);
+ base64OS.write(encodedImage.getData());
+ base64OS.flush();
+ base64OS.close();
+ imageURIorData = byteArrayOS.toString();
+ Logger.log(Camera.class.getName() + ": Base64 encoding size="
+ + Integer.toString(imageURIorData.length()));
+ } finally {
+ if (byteArrayOS != null) {
+ byteArrayOS.close();
+ }
+ }
+ } else {
+ imageURIorData = filePath.substring(0, dirIndex + 1) + filename;
+ }
+
+ long end = (new Date()).getTime();
+ Logger.log(Camera.class.getName() + ": reformat time=" + Long.toString(end-start) + " ms");
+
+ return imageURIorData;
+ }
+
+ /**
+ * Sends result back to JavaScript.
+ * @param result PluginResult
+ */
+ private static void sendResult(PluginResult result, String callbackId)
+ {
+ // invoke the appropriate callback
+ if (result.getStatus() == PluginResult.Status.OK.ordinal())
+ {
+ success(result, callbackId);
+ }
+ else
+ {
+ error(result, callbackId);
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cordova-blackberry/blob/041e4481/bbos/framework/ext/src/org/apache/cordova/camera/CameraOptions.java
----------------------------------------------------------------------
diff --git a/bbos/framework/ext/src/org/apache/cordova/camera/CameraOptions.java b/bbos/framework/ext/src/org/apache/cordova/camera/CameraOptions.java
new file mode 100644
index 0000000..8bfa0df
--- /dev/null
+++ b/bbos/framework/ext/src/org/apache/cordova/camera/CameraOptions.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cordova.camera;
+
+import org.apache.cordova.json4j.JSONArray;
+import org.apache.cordova.json4j.JSONException;
+
+import net.rim.device.api.system.Bitmap;
+
+/**
+ * A helper class to hold all the options specified when using the camera api.
+ */
+public class CameraOptions {
+
+ /** Return the result as a Base-64 encoded string. */
+ public static final int DESTINATION_DATA_URL = 0;
+
+ /** Return the result as a file URI. */
+ public static final int DESTINATION_FILE_URI = 1;
+
+ /** JPEG image encoding. */
+ public static final int ENCODING_JPEG = 0;
+
+ /** PNG image encoding. */
+ public static final int ENCODING_PNG = 1;
+
+ /** Select image from picture library. */
+ public static final int SOURCE_PHOTOLIBRARY = 0;
+
+ /** Take picture from camera. */
+ public static final int SOURCE_CAMERA = 1;
+
+ /** Select image from picture library. */
+ public static final int SOURCE_SAVEDPHOTOALBUM = 2;
+
+ // Class members with defaults set.
+ public int quality = 80;
+ public int destinationType = DESTINATION_DATA_URL;
+ public int sourceType = SOURCE_CAMERA;
+ public int targetWidth = -1;
+ public int targetHeight = -1;
+ public int encoding = ENCODING_JPEG;
+ public String fileExtension = ".jpg";
+ public int imageFilter = Bitmap.FILTER_LANCZOS;
+ public boolean reformat = false;
+ public boolean saveToPhotoAlbum = true;
+
+ /**
+ * Defines the order of args in the JSONArray
+ *
+ * [ 80, // quality
+ * Camera.DestinationType.DATA_URL, // destinationType
+ * Camera.PictureSourceType.PHOTOLIBRARY // sourceType (ignored)
+ * 400, // targetWidth
+ * 600, // targetHeight
+ * Camera.EncodingType.JPEG // encoding
+ * Camera.mediaType
+ * Camera.allowEdit
+ * Camera.correctOrientation
+ * Camera.saveToPhotoAlbum // save to photo album
+ * Camera.popoverOptions]
+ */
+ private static final int ARG_QUALITY = 0;
+ private static final int ARG_DESTINATION_TYPE = 1;
+ private static final int ARG_SOURCE_TYPE = 2;
+ private static final int ARG_TARGET_WIDTH = 3;
+ private static final int ARG_TARGET_HEIGHT = 4;
+ private static final int ARG_ENCODING = 5;
+ private static final int ARG_SAVETOPHOTOALBUM = 9;
+
+ /**
+ * Parse the JSONArray and populate the class members with the values.
+ *
+ * @param args
+ * a JSON Array of camera options.
+ * @return a new CameraOptions object with values set.
+ * @throws NumberFormatException
+ * @throws JSONException
+ */
+ public static CameraOptions fromJSONArray(JSONArray args)
+ throws NumberFormatException, JSONException {
+ CameraOptions options = new CameraOptions();
+
+ if (args != null && args.length() > 0) {
+ // Use the quality value to determine what image filter to use
+ // if a reformat is necessary. The possible values in order from
+ // fastest (poorest quality) to slowest (best quality) are:
+ //
+ // FILTER_BOX -> FILTER_BILINEAR -> FILTER_LANCZOS
+ if (!args.isNull(ARG_QUALITY)) {
+ int quality = Integer.parseInt(args.getString(ARG_QUALITY));
+ if (quality > 0) {
+ options.quality = quality > 100 ? 100 : quality;
+ if (options.quality < 30) {
+ options.imageFilter = Bitmap.FILTER_BOX;
+ } else if (options.quality < 60) {
+ options.imageFilter = Bitmap.FILTER_BILINEAR;
+ }
+ }
+ }
+
+ if (!args.isNull(ARG_DESTINATION_TYPE)) {
+ int destType = Integer.parseInt(args
+ .getString(ARG_DESTINATION_TYPE));
+ if (destType == DESTINATION_FILE_URI) {
+ options.destinationType = DESTINATION_FILE_URI;
+ }
+ }
+
+ if (!args.isNull(ARG_SOURCE_TYPE)) {
+ options.sourceType = Integer.parseInt(args
+ .getString(ARG_SOURCE_TYPE));
+ }
+
+ if (!args.isNull(ARG_TARGET_WIDTH)) {
+ options.targetWidth = Integer.parseInt(args
+ .getString(ARG_TARGET_WIDTH));
+ }
+
+ if (!args.isNull(ARG_TARGET_HEIGHT)) {
+ options.targetHeight = Integer.parseInt(args
+ .getString(ARG_TARGET_HEIGHT));
+ }
+
+ if (!args.isNull(ARG_ENCODING)) {
+ int encoding = Integer.parseInt(args.getString(ARG_ENCODING));
+ if (encoding == ENCODING_PNG) {
+ options.encoding = ENCODING_PNG;
+ options.fileExtension = ".png";
+ }
+ }
+
+ // A reformat of the picture taken from the camera is only performed
+ // if a custom width or height was specified or the user wants
+ // the output in an encoded form which is not JPEG.
+ if (options.targetWidth > 0 || options.targetHeight > 0
+ || options.encoding != ENCODING_JPEG) {
+ options.reformat = true;
+ }
+
+ if (!args.isNull(ARG_SAVETOPHOTOALBUM)) {
+ options.saveToPhotoAlbum = parseBoolean(args.getString(ARG_SAVETOPHOTOALBUM));
+ }
+
+ }
+
+ return options;
+ }
+
+ /**
+ * no parseBoolean in JDK 1.3 :(
+ */
+ public static boolean parseBoolean(String s) {
+ if(s.equals("true")){
+ return true;
+ }else{
+ return false;
+ }
+ }
+
+ /**
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ StringBuffer str = new StringBuffer();
+ str.append("Destination: " + destinationType + "\n");
+ str.append("Source: " + sourceType + "\n");
+ str.append("Quality: " + quality + "\n");
+ str.append("Width: " + targetWidth + "\n");
+ str.append("Height: " + targetHeight + "\n");
+ str.append("Encoding: " + encoding + "\n");
+ str.append("Filter: " + imageFilter + "\n");
+ str.append("Reformat: " + reformat);
+ str.append("Save To Photo Album: " + saveToPhotoAlbum);
+ return str.toString();
+ }
+}