diff --git a/README.md b/README.md
index 5bcd8fd7..d2d2e99f 100755
--- a/README.md
+++ b/README.md
@@ -15,8 +15,8 @@ indicate that the project has yet to be fully endorsed by the ASF.
Requires
---
-- Java JDK 1.5
-- Apache ANT
+- Java JDK 1.5 or greater
+- Apache ANT 1.8.0 or greater
- Android SDK [http://developer.android.com](http://developer.android.com)
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
diff --git a/bin/create b/bin/create
index 8f535248..e38dd0c5 100755
--- a/bin/create
+++ b/bin/create
@@ -30,10 +30,10 @@ then
exit 0
fi
-BUILD_PATH=$( cd "$( dirname "$0" )/.." && pwd )
+BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
VERSION=$(cat "$BUILD_PATH"/VERSION)
-PROJECT_PATH=${1:-'./example'}
+PROJECT_PATH="${1:-'./example'}"
PACKAGE=${2:-"org.apache.cordova.example"}
ACTIVITY=${3:-"cordovaExample"}
@@ -87,19 +87,19 @@ function replace {
trap on_error ERR
trap on_exit EXIT
-ANDROID_BIN=$( which android )
+ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
MANIFEST_PATH="$PROJECT_PATH"/AndroidManifest.xml
-TARGET=$($ANDROID_BIN list targets | grep id: | tail -1 | cut -f 2 -d ' ' )
-API_LEVEL=$($ANDROID_BIN list target | grep "API level:" | tail -n 1 | cut -f 2 -d ':' | tr -d ' ')
+TARGET=$("$ANDROID_BIN" list targets | grep id: | tail -1 | cut -f 2 -d ' ' )
+API_LEVEL=$("$ANDROID_BIN" list target | grep "API level:" | tail -n 1 | cut -f 2 -d ':' | tr -d ' ')
# if this a distribution release no need to build a jar
if [ ! -e "$BUILD_PATH"/cordova-$VERSION.jar ] && [ -d "$BUILD_PATH"/framework ]
then
# update the cordova-android framework for the desired target
- $ANDROID_BIN update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
+ "$ANDROID_BIN" update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
# Use curl to get the jar (TODO: Support Apache Mirrors)
@@ -116,7 +116,7 @@ then
fi
# create new android project
-$ANDROID_BIN create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
+"$ANDROID_BIN" create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
# copy project template
cp -r "$BUILD_PATH"/bin/templates/project/assets "$PROJECT_PATH"
diff --git a/bin/create.js b/bin/create.js
index 2e774945..40bce8ff 100644
--- a/bin/create.js
+++ b/bin/create.js
@@ -163,40 +163,40 @@ if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
// copy in the project template
WScript.Echo("Copying template files...");
-exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
-exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /E /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
+exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
+exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /E /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
// check if we have the source or the distro files
WScript.Echo("Copying js, jar & config.xml files...");
if(fso.FolderExists(ROOT + '\\framework')) {
- exec('%comspec% /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
- exec('%comspec% /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
+ exec('%comspec% /c copy "'+ROOT+'"\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
+ exec('%comspec% /c copy "'+ROOT+'"\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
- exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
+ exec('%comspec% /c copy "'+ROOT+'"\\framework\\res\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
} else {
// copy in cordova.js
- exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
+ exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
// copy in cordova.jar
- exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
+ exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
// copy in xml
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
- exec('%comspec% /c copy '+ROOT+'\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
+ exec('%comspec% /c copy "'+ROOT+'"\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
}
// copy cordova scripts
fso.CreateFolder(PROJECT_PATH + '\\cordova');
createAppInfoJar();
WScript.Echo("Copying cordova command tools...");
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\debug.bat ' + PROJECT_PATH + '\\cordova\\debug.bat /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\emulate.bat ' + PROJECT_PATH + '\\cordova\\emulate.bat /Y');
-exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\BOOM.bat ' + PROJECT_PATH + '\\cordova\\BOOM.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\debug.bat ' + PROJECT_PATH + '\\cordova\\debug.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\emulate.bat ' + PROJECT_PATH + '\\cordova\\emulate.bat /Y');
+exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\BOOM.bat ' + PROJECT_PATH + '\\cordova\\BOOM.bat /Y');
// interpolate the activity name and package
WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
diff --git a/bin/tests/test_create_win.js b/bin/tests/test_create_win.js
index 238e9165..c634a940 100644
--- a/bin/tests/test_create_win.js
+++ b/bin/tests/test_create_win.js
@@ -88,14 +88,9 @@ create_project.on('exit', function(code) {
// TODO check that package name and activity name were substituted properly
});
- // make sure plugins.xml was added
- path.exists(util.format('%s/res/xml/plugins.xml', project_path), function(exists) {
- assert(exists, 'plugins.xml did not get created');
- });
-
- // make sure cordova.xml was added
- path.exists(util.format('%s/res/xml/cordova.xml', project_path), function(exists) {
- assert(exists, 'plugins.xml did not get created');
+ // make sure config.xml was added
+ path.exists(util.format('%s/res/xml/config.xml', project_path), function(exists) {
+ assert(exists, 'config.xml did not get created');
});
// make sure cordova.jar was added
diff --git a/framework/.classpath b/framework/.classpath
index 350a98b6..1b06df2c 100644
--- a/framework/.classpath
+++ b/framework/.classpath
@@ -3,7 +3,7 @@
-
+
diff --git a/framework/assets/js/cordova.android.js b/framework/assets/js/cordova.android.js
index 7a7806e7..e2e5bfd4 100644
--- a/framework/assets/js/cordova.android.js
+++ b/framework/assets/js/cordova.android.js
@@ -1,6 +1,6 @@
-// commit d30179b30152b9383a80637e609cf2d785e1aa3e
+// commit 968764b2f67ff2ed755eace083b83f395cf0e9c2
-// File generated at :: Tue Sep 18 2012 11:34:26 GMT-0400 (EDT)
+// File generated at :: Fri Sep 28 2012 14:33:38 GMT-0400 (EDT)
/*
Licensed to the Apache Software Foundation (ASF) under one
@@ -137,7 +137,7 @@ window.addEventListener = function(evt, handler, capture) {
document.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
- // If unsubcribing from an event that is handled by a plugin
+ // If unsubscribing from an event that is handled by a plugin
if (typeof documentEventHandlers[e] != "undefined") {
documentEventHandlers[e].unsubscribe(handler);
} else {
@@ -147,7 +147,7 @@ document.removeEventListener = function(evt, handler, capture) {
window.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
- // If unsubcribing from an event that is handled by a plugin
+ // If unsubscribing from an event that is handled by a plugin
if (typeof windowEventHandlers[e] != "undefined") {
windowEventHandlers[e].unsubscribe(handler);
} else {
@@ -196,7 +196,7 @@ var cordova = {
delete documentEventHandlers[event];
},
/**
- * Retreive original event handlers that were replaced by Cordova
+ * Retrieve original event handlers that were replaced by Cordova
*
* @return object
*/
@@ -245,7 +245,9 @@ var cordova = {
/**
* Plugin callback mechanism.
*/
- callbackId: 0,
+ // Randomize the starting callbackId to avoid collisions after refreshing or navigating.
+ // This way, it's very unlikely that any new callback would get the same callbackId as an old callback.
+ callbackId: Math.floor(Math.random() * 2000000000),
callbacks: {},
callbackStatus: {
NO_RESULT: 0,
@@ -412,7 +414,7 @@ function recursiveMerge(target, src) {
module.exports = {
build: function (objects) {
return {
- intoButDontClobber: function (target) {
+ intoButDoNotClobber: function (target) {
include(target, objects, false, false);
},
intoAndClobber: function(target) {
@@ -729,6 +731,9 @@ module.exports = {
geolocation: {
path: 'cordova/plugin/geolocation'
},
+ globalization: {
+ path: 'cordova/plugin/globalization'
+ },
network: {
children: {
connection: {
@@ -844,6 +849,9 @@ module.exports = {
Flags: {
path: 'cordova/plugin/Flags'
},
+ GlobalizationError: {
+ path: 'cordova/plugin/GlobalizationError'
+ },
LocalFileSystem: {
path: 'cordova/plugin/LocalFileSystem'
},
@@ -889,7 +897,7 @@ define("cordova/exec", function(require, exports, module) {
* Execute a cordova command. It is up to the native side whether this action
* is synchronous or asynchronous. The native side can return:
* Synchronous: PluginResult object as a JSON string
- * Asynchrounous: Empty string ""
+ * Asynchronous: Empty string ""
* If async, the native side will cordova.callbackSuccess or cordova.callbackError,
* depending upon the result of the action.
*
@@ -900,11 +908,7 @@ define("cordova/exec", function(require, exports, module) {
* @param {String[]} [args] Zero or more arguments to pass to the method
*/
var cordova = require('cordova'),
- callback = require('cordova/plugin/android/callback'),
- polling = require('cordova/plugin/android/polling'),
nativeApiProvider = require('cordova/plugin/android/nativeapiprovider'),
- jsToNativeBridgeMode,
- nativeToJsBridgeMode,
jsToNativeModes = {
PROMPT: 0,
JS_OBJECT: 1,
@@ -916,9 +920,6 @@ var cordova = require('cordova'),
nativeToJsModes = {
// Polls for messages using the JS->Native bridge.
POLLING: 0,
- // Does an XHR to a local server, which will send back messages. This is
- // broken on ICS when a proxy server is configured.
- HANGING_GET: 1,
// For LOAD_URL to be viable, it would need to have a work-around for
// the bug where the soft-keyboard gets dismissed when a message is sent.
LOAD_URL: 2,
@@ -930,20 +931,17 @@ var cordova = require('cordova'),
// to be executed.
// Requires Android 3.2.4 or above.
PRIVATE_API: 4
- };
+ },
+ jsToNativeBridgeMode, // Set lazily.
+ nativeToJsBridgeMode = nativeToJsModes.ONLINE_EVENT
+ pollEnabled = false;
function androidExec(success, fail, service, action, args) {
// Set default bridge modes if they have not already been set.
if (jsToNativeBridgeMode === undefined) {
- androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
- }
- if (nativeToJsBridgeMode === undefined) {
- if (callback.isAvailable()) {
- androidExec.setNativeToJsBridgeMode(nativeToJsModes.HANGING_GET);
- } else {
- androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING);
- }
+ androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT);
}
+
var callbackId = service + cordova.callbackId++,
argsJson = JSON.stringify(args);
if (success || fail) {
@@ -963,6 +961,18 @@ function androidExec(success, fail, service, action, args) {
}
}
+function pollOnce() {
+ var msg = nativeApiProvider.get().retrieveJsMessages();
+ androidExec.processMessages(msg);
+}
+
+function pollingTimerFunc() {
+ if (pollEnabled) {
+ pollOnce();
+ setTimeout(pollingTimerFunc, 50);
+ }
+}
+
function hookOnlineApis() {
function proxyEvent(e) {
cordova.fireWindowEvent(e.type);
@@ -971,8 +981,8 @@ function hookOnlineApis() {
// It currently fires them only on document though, so we bridge them
// to window here (while first listening for exec()-releated online/offline
// events).
- window.addEventListener('online', polling.pollOnce, false);
- window.addEventListener('offline', polling.pollOnce, false);
+ window.addEventListener('online', pollOnce, false);
+ window.addEventListener('offline', pollOnce, false);
cordova.addWindowEventHandler('online');
cordova.addWindowEventHandler('offline');
document.addEventListener('online', proxyEvent, false);
@@ -998,9 +1008,7 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
return;
}
if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
- polling.stop();
- } else if (nativeToJsBridgeMode == nativeToJsModes.HANGING_GET) {
- callback.stop();
+ pollEnabled = false;
}
nativeToJsBridgeMode = mode;
@@ -1008,9 +1016,8 @@ androidExec.setNativeToJsBridgeMode = function(mode) {
nativeApiProvider.get().setNativeToJsBridgeMode(mode);
if (mode == nativeToJsModes.POLLING) {
- polling.start();
- } else if (mode == nativeToJsModes.HANGING_GET) {
- callback.start();
+ pollEnabled = true;
+ setTimeout(pollingTimerFunc, 1);
}
};
@@ -1055,7 +1062,7 @@ function processMessage(message) {
androidExec.processMessages = function(messages) {
while (messages) {
if (messages == '*') {
- window.setTimeout(polling.pollOnce, 0);
+ window.setTimeout(pollOnce, 0);
break;
}
var spaceIdx = messages.indexOf(' ');
@@ -1140,16 +1147,6 @@ module.exports = {
}, [channel.onCordovaReady]);
},
objects: {
- cordova: {
- children: {
- JSCallback:{
- path:"cordova/plugin/android/callback"
- },
- JSCallbackPolling:{
- path:"cordova/plugin/android/polling"
- }
- }
- },
navigator: {
children: {
app:{
@@ -1224,6 +1221,7 @@ for (var key in Camera) {
* @param {Object} options
*/
cameraExport.getPicture = function(successCallback, errorCallback, options) {
+ options = options || {};
// successCallback required
if (typeof successCallback != "function") {
console.log("Camera Error: successCallback is not a function");
@@ -1237,9 +1235,9 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
}
var quality = 50;
- if (options && typeof options.quality == "number") {
+ if (typeof options.quality == "number") {
quality = options.quality;
- } else if (options && typeof options.quality == "string") {
+ } else if (typeof options.quality == "string") {
var qlity = parseInt(options.quality, 10);
if (isNaN(qlity) === false) {
quality = qlity.valueOf();
@@ -1462,7 +1460,7 @@ define("cordova/plugin/CompassError", function(require, exports, module) {
/**
* CompassError.
- * An error code assigned by an implementation when an error has occured
+ * An error code assigned by an implementation when an error has occurred
* @constructor
*/
var CompassError = function(err) {
@@ -1748,7 +1746,7 @@ define("cordova/plugin/ContactError", function(require, exports, module) {
/**
* ContactError.
- * An error code assigned by an implementation when an error has occured
+ * An error code assigned by an implementation when an error has occurred
* @constructor
*/
var ContactError = function(err) {
@@ -1955,7 +1953,7 @@ DirectoryEntry.prototype.createReader = function() {
* Creates or looks up a directory
*
* @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
- * @param {Flags} options to create or excluively create the directory
+ * @param {Flags} options to create or exclusively create the directory
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
@@ -1987,7 +1985,7 @@ DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCall
* Creates or looks up a file
*
* @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
- * @param {Flags} options to create or excluively create the file
+ * @param {Flags} options to create or exclusively create the file
* @param {Function} successCallback is called with the new entry
* @param {Function} errorCallback is called with a FileError
*/
@@ -2431,7 +2429,7 @@ var FileReader = function() {
// Event handlers
this.onloadstart = null; // When the read starts.
- this.onprogress = null; // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
+ this.onprogress = null; // While reading (and decoding) file or fileBlob data, and reporting partial file data (progress.loaded/progress.total)
this.onload = null; // When the read has successfully completed.
this.onerror = null; // When the read has failed (see errors).
this.onloadend = null; // When the request has completed (either in success or failure).
@@ -2685,13 +2683,27 @@ module.exports = FileSystem;
define("cordova/plugin/FileTransfer", function(require, exports, module) {
var exec = require('cordova/exec'),
- FileTransferError = require('cordova/plugin/FileTransferError');
+ FileTransferError = require('cordova/plugin/FileTransferError'),
+ ProgressEvent = require('cordova/plugin/ProgressEvent');
+
+function newProgressEvent(result) {
+ var pe = new ProgressEvent();
+ pe.lengthComputable = result.lengthComputable;
+ pe.loaded = result.loaded;
+ pe.total = result.total;
+ return pe;
+}
+
+var idCounter = 0;
/**
* FileTransfer uploads a file to a remote server.
* @constructor
*/
-var FileTransfer = function() {};
+var FileTransfer = function() {
+ this._id = ++idCounter;
+ this.onprogress = null; // optional callback
+};
/**
* Given an absolute file path, uploads a file on the device to a remote server
@@ -2734,7 +2746,17 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
errorCallback(error);
};
- exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]);
+ var self = this;
+ var win = function(result) {
+ if (typeof result.lengthComputable != "undefined") {
+ if (self.onprogress) {
+ return self.onprogress(newProgressEvent(result));
+ }
+ } else {
+ return successCallback(result);
+ }
+ };
+ exec(win, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers, this._id]);
};
/**
@@ -2743,23 +2765,31 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
* @param target {String} Full path of the file on the device
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
+ * @param trustAllHosts {Boolean} Optional trust all hosts (e.g. for self-signed certs), defaults to false
*/
-FileTransfer.prototype.download = function(source, target, successCallback, errorCallback) {
+FileTransfer.prototype.download = function(source, target, successCallback, errorCallback, trustAllHosts) {
// sanity parameter checking
if (!source || !target) throw new Error("FileTransfer.download requires source URI and target URI parameters at the minimum.");
+ var self = this;
var win = function(result) {
- var entry = null;
- if (result.isDirectory) {
- entry = new (require('cordova/plugin/DirectoryEntry'))();
+ if (typeof result.lengthComputable != "undefined") {
+ if (self.onprogress) {
+ return self.onprogress(newProgressEvent(result));
+ }
+ } else {
+ var entry = null;
+ if (result.isDirectory) {
+ entry = new (require('cordova/plugin/DirectoryEntry'))();
+ }
+ else if (result.isFile) {
+ entry = new (require('cordova/plugin/FileEntry'))();
+ }
+ entry.isDirectory = result.isDirectory;
+ entry.isFile = result.isFile;
+ entry.name = result.name;
+ entry.fullPath = result.fullPath;
+ successCallback(entry);
}
- else if (result.isFile) {
- entry = new (require('cordova/plugin/FileEntry'))();
- }
- entry.isDirectory = result.isDirectory;
- entry.isFile = result.isFile;
- entry.name = result.name;
- entry.fullPath = result.fullPath;
- successCallback(entry);
};
var fail = function(e) {
@@ -2767,9 +2797,18 @@ FileTransfer.prototype.download = function(source, target, successCallback, erro
errorCallback(error);
};
- exec(win, errorCallback, 'FileTransfer', 'download', [source, target]);
+ exec(win, errorCallback, 'FileTransfer', 'download', [source, target, trustAllHosts, this._id]);
};
+/**
+ * Aborts the ongoing file transfer on this object
+ * @param successCallback {Function} Callback to be invoked upon success
+ * @param errorCallback {Function} Callback to be invoked upon error
+ */
+FileTransfer.prototype.abort = function(successCallback, errorCallback) {
+ exec(successCallback, errorCallback, 'FileTransfer', 'abort', [this._id]);
+}
+
module.exports = FileTransfer;
});
@@ -2791,6 +2830,7 @@ var FileTransferError = function(code, source, target, status) {
FileTransferError.FILE_NOT_FOUND_ERR = 1;
FileTransferError.INVALID_URL_ERR = 2;
FileTransferError.CONNECTION_ERR = 3;
+FileTransferError.ABORT_ERR = 4;
module.exports = FileTransferError;
@@ -3118,6 +3158,32 @@ module.exports = Flags;
});
+// file: lib/common/plugin/GlobalizationError.js
+define("cordova/plugin/GlobalizationError", function(require, exports, module) {
+
+
+/**
+ * Globalization error object
+ *
+ * @constructor
+ * @param code
+ * @param message
+ */
+var GlobalizationError = function(code, message) {
+ this.code = code || null;
+ this.message = message || '';
+};
+
+// Globalization error codes
+GlobalizationError.UNKNOWN_ERROR = 0;
+GlobalizationError.FORMATTING_ERROR = 1;
+GlobalizationError.PARSING_ERROR = 2;
+GlobalizationError.PATTERN_ERROR = 3;
+
+module.exports = GlobalizationError;
+
+});
+
// file: lib/common/plugin/LocalFileSystem.js
define("cordova/plugin/LocalFileSystem", function(require, exports, module) {
@@ -3608,7 +3674,7 @@ function removeListeners(l) {
var accelerometer = {
/**
- * Asynchronously aquires the current acceleration.
+ * Asynchronously acquires the current acceleration.
*
* @param {Function} successCallback The function to call when the acceleration data is available
* @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
@@ -3639,7 +3705,7 @@ var accelerometer = {
},
/**
- * Asynchronously aquires the acceleration repeatedly at a given interval.
+ * Asynchronously acquires the acceleration repeatedly at a given interval.
*
* @param {Function} successCallback The function to call each time the acceleration data is available
* @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
@@ -3780,80 +3846,6 @@ module.exports = {
}
};
-});
-
-// file: lib/android/plugin/android/callback.js
-define("cordova/plugin/android/callback", function(require, exports, module) {
-
-var port = null,
- token = null,
- xmlhttp;
-
-function startXhr() {
- // cordova/exec depends on this module, so we can't require cordova/exec on the module level.
- var exec = require('cordova/exec'),
- xmlhttp = new XMLHttpRequest();
-
- // Callback function when XMLHttpRequest is ready
- xmlhttp.onreadystatechange=function(){
- if (!xmlhttp) {
- return;
- }
- if (xmlhttp.readyState === 4){
- // If callback has JavaScript statement to execute
- if (xmlhttp.status === 200) {
-
- // Need to url decode the response
- var msg = decodeURIComponent(xmlhttp.responseText);
- setTimeout(startXhr, 1);
- exec.processMessages(msg);
- }
-
- // If callback ping (used to keep XHR request from timing out)
- else if (xmlhttp.status === 404) {
- setTimeout(startXhr, 10);
- }
-
- // 0 == Page is unloading.
- // 400 == Bad request.
- // 403 == invalid token.
- // 503 == server stopped.
- else {
- console.log("JSCallback Error: Request failed with status " + xmlhttp.status);
- exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING);
- }
- }
- };
-
- if (port === null) {
- port = prompt("getPort", "gap_callbackServer:");
- }
- if (token === null) {
- token = prompt("getToken", "gap_callbackServer:");
- }
- xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
- xmlhttp.send();
-}
-
-module.exports = {
- start: function() {
- startXhr();
- },
-
- stop: function() {
- if (xmlhttp) {
- var tmp = xmlhttp;
- xmlhttp = null;
- tmp.abort();
- }
- },
-
- isAvailable: function() {
- return ("true" != prompt("usePolling", "gap_callbackServer:"));
- }
-};
-
-
});
// file: lib/android/plugin/android/device.js
@@ -3880,7 +3872,7 @@ module.exports = {
* DEPRECATED
* This is only for Android.
*
- * This resets the back button to the default behaviour
+ * This resets the back button to the default behavior
*/
resetBackButton:function() {
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
@@ -3973,42 +3965,6 @@ module.exports = {
}
};
-});
-
-// file: lib/android/plugin/android/polling.js
-define("cordova/plugin/android/polling", function(require, exports, module) {
-
-var cordova = require('cordova'),
- nativeApiProvider = require('cordova/plugin/android/nativeapiprovider'),
- POLL_INTERVAL = 50,
- enabled = false;
-
-function pollOnce() {
- var exec = require('cordova/exec'),
- msg = nativeApiProvider.get().retrieveJsMessages();
- exec.processMessages(msg);
-}
-
-function doPoll() {
- if (!enabled) {
- return;
- }
- pollOnce();
- setTimeout(doPoll, POLL_INTERVAL);
-}
-
-module.exports = {
- start: function() {
- enabled = true;
- setTimeout(doPoll, 1);
- },
- stop: function() {
- enabled = false;
- },
- pollOnce: pollOnce
-};
-
-
});
// file: lib/android/plugin/android/promptbasednativeapi.js
@@ -4832,7 +4788,7 @@ console.table = function(data, columns) {
//------------------------------------------------------------------------------
// return a new function that calls both functions passed as args
//------------------------------------------------------------------------------
-function wrapperedOrigCall(orgFunc, newFunc) {
+function wrappedOrigCall(orgFunc, newFunc) {
return function() {
var args = [].slice.call(arguments);
try { orgFunc.apply(WinConsole, args); } catch (e) {}
@@ -4847,7 +4803,7 @@ function wrapperedOrigCall(orgFunc, newFunc) {
//------------------------------------------------------------------------------
for (var key in console) {
if (typeof WinConsole[key] == "function") {
- console[key] = wrapperedOrigCall(WinConsole[key], console[key]);
+ console[key] = wrappedOrigCall(WinConsole[key], console[key]);
}
}
@@ -4992,7 +4948,7 @@ define("cordova/plugin/echo", function(require, exports, module) {
var exec = require('cordova/exec');
/**
- * Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback.
+ * Sends the given message through exec() to the Echo plugin, which sends it back to the successCallback.
* @param successCallback invoked with a FileSystem object
* @param errorCallback invoked if error occurs retrieving file system
* @param message The string to be echoed.
@@ -5059,7 +5015,7 @@ function createTimeout(errorCallback, timeout) {
var geolocation = {
lastPosition:null, // reference to last known (cached) position returned
/**
- * Asynchronously aquires the current position.
+ * Asynchronously acquires the current position.
*
* @param {Function} successCallback The function to call when the position data is available
* @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL)
@@ -5205,6 +5161,545 @@ module.exports = geolocation;
});
+// file: lib/common/plugin/globalization.js
+define("cordova/plugin/globalization", function(require, exports, module) {
+
+var exec = require('cordova/exec'),
+ GlobalizationError = require('cordova/plugin/GlobalizationError');
+
+var globalization = {
+
+/**
+* Returns the string identifier for the client's current language.
+* It returns the language identifier string to the successCB callback with a
+* properties object as a parameter. If there is an error getting the language,
+* then the errorCB callback is invoked.
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+*
+* @return Object.value {String}: The language identifier
+*
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+* globalization.getPreferredLanguage(function (language) {alert('language:' + language.value + '\n');},
+* function () {});
+*/
+getPreferredLanguage:function(successCB, failureCB) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getPreferredLanguage Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getPreferredLanguage Error: failureCB is not a function");
+ return;
+ }
+
+ exec(successCB, failureCB, "Globalization","getPreferredLanguage", []);
+},
+
+/**
+* Returns the string identifier for the client's current locale setting.
+* It returns the locale identifier string to the successCB callback with a
+* properties object as a parameter. If there is an error getting the locale,
+* then the errorCB callback is invoked.
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+*
+* @return Object.value {String}: The locale identifier
+*
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+* globalization.getLocaleName(function (locale) {alert('locale:' + locale.value + '\n');},
+* function () {});
+*/
+getLocaleName:function(successCB, failureCB) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getLocaleName Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getLocaleName Error: failureCB is not a function");
+ return;
+ }
+ exec(successCB, failureCB, "Globalization","getLocaleName", []);
+},
+
+
+/**
+* Returns a date formatted as a string according to the client's user preferences and
+* calendar using the time zone of the client. It returns the formatted date string to the
+* successCB callback with a properties object as a parameter. If there is an error
+* formatting the date, then the errorCB callback is invoked.
+*
+* The defaults are: formatLenght="short" and selector="date and time"
+*
+* @param {Date} date
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* formatLength {String}: 'short', 'medium', 'long', or 'full'
+* selector {String}: 'date', 'time', or 'date and time'
+*
+* @return Object.value {String}: The localized date string
+*
+* @error GlobalizationError.FORMATTING_ERROR
+*
+* Example
+* globalization.dateToString(new Date(),
+* function (date) {alert('date:' + date.value + '\n');},
+* function (errorCode) {alert(errorCode);},
+* {formatLength:'short'});
+*/
+dateToString:function(date, successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.dateToString Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.dateToString Error: failureCB is not a function");
+ return;
+ }
+
+
+ if (date instanceof Date){
+ var dateValue;
+ dateValue = date.valueOf();
+ exec(successCB, failureCB, "Globalization", "dateToString", [{"date": dateValue, "options": options}]);
+ }
+ else {
+ console.log("Globalization.dateToString Error: date is not a Date object");
+ }
+},
+
+
+/**
+* Parses a date formatted as a string according to the client's user
+* preferences and calendar using the time zone of the client and returns
+* the corresponding date object. It returns the date to the successCB
+* callback with a properties object as a parameter. If there is an error
+* parsing the date string, then the errorCB callback is invoked.
+*
+* The defaults are: formatLength="short" and selector="date and time"
+*
+* @param {String} dateString
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* formatLength {String}: 'short', 'medium', 'long', or 'full'
+* selector {String}: 'date', 'time', or 'date and time'
+*
+* @return Object.year {Number}: The four digit year
+* Object.month {Number}: The month from (0 - 11)
+* Object.day {Number}: The day from (1 - 31)
+* Object.hour {Number}: The hour from (0 - 23)
+* Object.minute {Number}: The minute from (0 - 59)
+* Object.second {Number}: The second from (0 - 59)
+* Object.millisecond {Number}: The milliseconds (from 0 - 999),
+* not available on all platforms
+*
+* @error GlobalizationError.PARSING_ERROR
+*
+* Example
+* globalization.stringToDate('4/11/2011',
+* function (date) { alert('Month:' + date.month + '\n' +
+* 'Day:' + date.day + '\n' +
+* 'Year:' + date.year + '\n');},
+* function (errorCode) {alert(errorCode);},
+* {selector:'date'});
+*/
+stringToDate:function(dateString, successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.stringToDate Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.stringToDate Error: failureCB is not a function");
+ return;
+ }
+ if (typeof dateString == "string"){
+ exec(successCB, failureCB, "Globalization", "stringToDate", [{"dateString": dateString, "options": options}]);
+ }
+ else {
+ console.log("Globalization.stringToDate Error: dateString is not a string");
+ }
+},
+
+
+/**
+* Returns a pattern string for formatting and parsing dates according to the client's
+* user preferences. It returns the pattern to the successCB callback with a
+* properties object as a parameter. If there is an error obtaining the pattern,
+* then the errorCB callback is invoked.
+*
+* The defaults are: formatLength="short" and selector="date and time"
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* formatLength {String}: 'short', 'medium', 'long', or 'full'
+* selector {String}: 'date', 'time', or 'date and time'
+*
+* @return Object.pattern {String}: The date and time pattern for formatting and parsing dates.
+* The patterns follow Unicode Technical Standard #35
+* http://unicode.org/reports/tr35/tr35-4.html
+* Object.timezone {String}: The abbreviated name of the time zone on the client
+* Object.utc_offset {Number}: The current difference in seconds between the client's
+* time zone and coordinated universal time.
+* Object.dst_offset {Number}: The current daylight saving time offset in seconds
+* between the client's non-daylight saving's time zone
+* and the client's daylight saving's time zone.
+*
+* @error GlobalizationError.PATTERN_ERROR
+*
+* Example
+* globalization.getDatePattern(
+* function (date) {alert('pattern:' + date.pattern + '\n');},
+* function () {},
+* {formatLength:'short'});
+*/
+getDatePattern:function(successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getDatePattern Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getDatePattern Error: failureCB is not a function");
+ return;
+ }
+
+ exec(successCB, failureCB, "Globalization", "getDatePattern", [{"options": options}]);
+},
+
+
+/**
+* Returns an array of either the names of the months or days of the week
+* according to the client's user preferences and calendar. It returns the array of names to the
+* successCB callback with a properties object as a parameter. If there is an error obtaining the
+* names, then the errorCB callback is invoked.
+*
+* The defaults are: type="wide" and item="months"
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* type {String}: 'narrow' or 'wide'
+* item {String}: 'months', or 'days'
+*
+* @return Object.value {Array{String}}: The array of names starting from either
+* the first month in the year or the
+* first day of the week.
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+* globalization.getDateNames(function (names) {
+* for(var i = 0; i < names.value.length; i++) {
+* alert('Month:' + names.value[i] + '\n');}},
+* function () {});
+*/
+getDateNames:function(successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getDateNames Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getDateNames Error: failureCB is not a function");
+ return;
+ }
+ exec(successCB, failureCB, "Globalization", "getDateNames", [{"options": options}]);
+},
+
+/**
+* Returns whether daylight savings time is in effect for a given date using the client's
+* time zone and calendar. It returns whether or not daylight savings time is in effect
+* to the successCB callback with a properties object as a parameter. If there is an error
+* reading the date, then the errorCB callback is invoked.
+*
+* @param {Date} date
+* @param {Function} successCB
+* @param {Function} errorCB
+*
+* @return Object.dst {Boolean}: The value "true" indicates that daylight savings time is
+* in effect for the given date and "false" indicate that it is not.
+*
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+* globalization.isDayLightSavingsTime(new Date(),
+* function (date) {alert('dst:' + date.dst + '\n');}
+* function () {});
+*/
+isDayLightSavingsTime:function(date, successCB, failureCB) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.isDayLightSavingsTime Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.isDayLightSavingsTime Error: failureCB is not a function");
+ return;
+ }
+
+
+ if (date instanceof Date){
+ var dateValue;
+ dateValue = date.valueOf();
+ exec(successCB, failureCB, "Globalization", "isDayLightSavingsTime", [{"date": dateValue}]);
+ }
+ else {
+ console.log("Globalization.isDayLightSavingsTime Error: date is not a Date object");
+ }
+
+},
+
+/**
+* Returns the first day of the week according to the client's user preferences and calendar.
+* The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
+* It returns the day to the successCB callback with a properties object as a parameter.
+* If there is an error obtaining the pattern, then the errorCB callback is invoked.
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+*
+* @return Object.value {Number}: The number of the first day of the week.
+*
+* @error GlobalizationError.UNKNOWN_ERROR
+*
+* Example
+* globalization.getFirstDayOfWeek(function (day)
+* { alert('Day:' + day.value + '\n');},
+* function () {});
+*/
+getFirstDayOfWeek:function(successCB, failureCB) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getFirstDayOfWeek Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getFirstDayOfWeek Error: failureCB is not a function");
+ return;
+ }
+
+ exec(successCB, failureCB, "Globalization", "getFirstDayOfWeek", []);
+},
+
+
+/**
+* Returns a number formatted as a string according to the client's user preferences.
+* It returns the formatted number string to the successCB callback with a properties object as a
+* parameter. If there is an error formatting the number, then the errorCB callback is invoked.
+*
+* The defaults are: type="decimal"
+*
+* @param {Number} number
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* type {String}: 'decimal', "percent", or 'currency'
+*
+* @return Object.value {String}: The formatted number string.
+*
+* @error GlobalizationError.FORMATTING_ERROR
+*
+* Example
+* globalization.numberToString(3.25,
+* function (number) {alert('number:' + number.value + '\n');},
+* function () {},
+* {type:'decimal'});
+*/
+numberToString:function(number, successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.numberToString Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.numberToString Error: failureCB is not a function");
+ return;
+ }
+
+ if(typeof number == "number") {
+ exec(successCB, failureCB, "Globalization", "numberToString", [{"number": number, "options": options}]);
+ }
+ else {
+ console.log("Globalization.numberToString Error: number is not a number");
+ }
+},
+
+/**
+* Parses a number formatted as a string according to the client's user preferences and
+* returns the corresponding number. It returns the number to the successCB callback with a
+* properties object as a parameter. If there is an error parsing the number string, then
+* the errorCB callback is invoked.
+*
+* The defaults are: type="decimal"
+*
+* @param {String} numberString
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* type {String}: 'decimal', "percent", or 'currency'
+*
+* @return Object.value {Number}: The parsed number.
+*
+* @error GlobalizationError.PARSING_ERROR
+*
+* Example
+* globalization.stringToNumber('1234.56',
+* function (number) {alert('Number:' + number.value + '\n');},
+* function () { alert('Error parsing number');});
+*/
+stringToNumber:function(numberString, successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.stringToNumber Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.stringToNumber Error: failureCB is not a function");
+ return;
+ }
+
+ if(typeof numberString == "string") {
+ exec(successCB, failureCB, "Globalization", "stringToNumber", [{"numberString": numberString, "options": options}]);
+ }
+ else {
+ console.log("Globalization.stringToNumber Error: numberString is not a string");
+ }
+},
+
+/**
+* Returns a pattern string for formatting and parsing numbers according to the client's user
+* preferences. It returns the pattern to the successCB callback with a properties object as a
+* parameter. If there is an error obtaining the pattern, then the errorCB callback is invoked.
+*
+* The defaults are: type="decimal"
+*
+* @param {Function} successCB
+* @param {Function} errorCB
+* @param {Object} options {optional}
+* type {String}: 'decimal', "percent", or 'currency'
+*
+* @return Object.pattern {String}: The number pattern for formatting and parsing numbers.
+* The patterns follow Unicode Technical Standard #35.
+* http://unicode.org/reports/tr35/tr35-4.html
+* Object.symbol {String}: The symbol to be used when formatting and parsing
+* e.g., percent or currency symbol.
+* Object.fraction {Number}: The number of fractional digits to use when parsing and
+* formatting numbers.
+* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
+* Object.positive {String}: The symbol to use for positive numbers when parsing and formatting.
+* Object.negative: {String}: The symbol to use for negative numbers when parsing and formatting.
+* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
+* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
+*
+* @error GlobalizationError.PATTERN_ERROR
+*
+* Example
+* globalization.getNumberPattern(
+* function (pattern) {alert('Pattern:' + pattern.pattern + '\n');},
+* function () {});
+*/
+getNumberPattern:function(successCB, failureCB, options) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getNumberPattern Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getNumberPattern Error: failureCB is not a function");
+ return;
+ }
+
+ exec(successCB, failureCB, "Globalization", "getNumberPattern", [{"options": options}]);
+},
+
+/**
+* Returns a pattern string for formatting and parsing currency values according to the client's
+* user preferences and ISO 4217 currency code. It returns the pattern to the successCB callback with a
+* properties object as a parameter. If there is an error obtaining the pattern, then the errorCB
+* callback is invoked.
+*
+* @param {String} currencyCode
+* @param {Function} successCB
+* @param {Function} errorCB
+*
+* @return Object.pattern {String}: The currency pattern for formatting and parsing currency values.
+* The patterns follow Unicode Technical Standard #35
+* http://unicode.org/reports/tr35/tr35-4.html
+* Object.code {String}: The ISO 4217 currency code for the pattern.
+* Object.fraction {Number}: The number of fractional digits to use when parsing and
+* formatting currency.
+* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
+* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
+* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
+*
+* @error GlobalizationError.FORMATTING_ERROR
+*
+* Example
+* globalization.getCurrencyPattern('EUR',
+* function (currency) {alert('Pattern:' + currency.pattern + '\n');}
+* function () {});
+*/
+getCurrencyPattern:function(currencyCode, successCB, failureCB) {
+ // successCallback required
+ if (typeof successCB != "function") {
+ console.log("Globalization.getCurrencyPattern Error: successCB is not a function");
+ return;
+ }
+
+ // errorCallback required
+ if (typeof failureCB != "function") {
+ console.log("Globalization.getCurrencyPattern Error: failureCB is not a function");
+ return;
+ }
+
+ if(typeof currencyCode == "string") {
+ exec(successCB, failureCB, "Globalization", "getCurrencyPattern", [{"currencyCode": currencyCode}]);
+ }
+ else {
+ console.log("Globalization.getCurrencyPattern Error: currencyCode is not a currency code");
+ }
+}
+
+};
+
+module.exports = globalization;
+
+});
+
// file: lib/common/plugin/logger.js
define("cordova/plugin/logger", function(require, exports, module) {
@@ -5747,7 +6242,7 @@ utils.clone = function(obj) {
};
/**
- * Returns a wrappered version of the function
+ * Returns a wrapped version of the function
*/
utils.close = function(context, func, params) {
if (typeof params == 'undefined') {
@@ -5918,7 +6413,7 @@ window.cordova = require('cordova');
platform = require('cordova/platform');
// Drop the common globals into the window object, but be nice and don't overwrite anything.
- builder.build(base.objects).intoButDontClobber(window);
+ builder.build(base.objects).intoButDoNotClobber(window);
// Drop the platform-specific globals into the window object
// and clobber any existing object.
diff --git a/framework/build.xml b/framework/build.xml
index e8910c35..9e7b1278 100644
--- a/framework/build.xml
+++ b/framework/build.xml
@@ -26,9 +26,37 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
diff --git a/framework/res/xml/config.xml b/framework/res/xml/config.xml
index 4a6fffcc..1117f8d1 100644
--- a/framework/res/xml/config.xml
+++ b/framework/res/xml/config.xml
@@ -51,6 +51,7 @@
+
diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java
index 4fd87181..df7181b5 100755
--- a/framework/src/org/apache/cordova/AccelListener.java
+++ b/framework/src/org/apache/cordova/AccelListener.java
@@ -19,6 +19,7 @@
package org.apache.cordova;
import java.util.List;
+
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
@@ -26,11 +27,11 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.content.Context;
/**
* This class listens to the accelerometer sensor and stores the latest
@@ -224,6 +225,16 @@ public class AccelListener extends Plugin implements SensorEventListener {
}
}
+ /**
+ * Called when the view navigates.
+ */
+ @Override
+ public void onReset() {
+ if (this.status == AccelListener.RUNNING) {
+ this.stop();
+ }
+ }
+
// Sends an error back to JS
private void fail(int code, String message) {
// Error object
diff --git a/framework/src/org/apache/cordova/AudioHandler.java b/framework/src/org/apache/cordova/AudioHandler.java
index ccf9f6cf..9ee226ab 100644
--- a/framework/src/org/apache/cordova/AudioHandler.java
+++ b/framework/src/org/apache/cordova/AudioHandler.java
@@ -139,6 +139,14 @@ public class AudioHandler extends Plugin {
this.players.clear();
}
+ /**
+ * Stop all audio players and recorders on navigate.
+ */
+ @Override
+ public void onReset() {
+ onDestroy();
+ }
+
/**
* Called when a message is sent to plugin.
*
diff --git a/framework/src/org/apache/cordova/BatteryListener.java b/framework/src/org/apache/cordova/BatteryListener.java
index eb0ea98a..4476cc3c 100755
--- a/framework/src/org/apache/cordova/BatteryListener.java
+++ b/framework/src/org/apache/cordova/BatteryListener.java
@@ -99,6 +99,13 @@ public class BatteryListener extends Plugin {
removeBatteryListener();
}
+ /**
+ * Stop battery receiver.
+ */
+ public void onReset() {
+ removeBatteryListener();
+ }
+
/**
* Stop the battery receiver and set it to null.
*/
diff --git a/framework/src/org/apache/cordova/CallbackServer.java b/framework/src/org/apache/cordova/CallbackServer.java
deleted file mode 100755
index dcf3b6e4..00000000
--- a/framework/src/org/apache/cordova/CallbackServer.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- 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;
-
-import java.io.BufferedReader;
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.UnsupportedEncodingException;
-import java.net.ServerSocket;
-import java.net.Socket;
-import java.util.LinkedList;
-
-/**
- * This class provides a way for Java to run JavaScript in the web page that has loaded Cordova.
- * The CallbackServer class implements an XHR server and a polling server with a list of JavaScript
- * statements that are to be executed on the web page.
- *
- * The process flow for XHR is:
- * 1. JavaScript makes an async XHR call.
- * 2. The server holds the connection open until data is available.
- * 3. The server writes the data to the client and closes the connection.
- * 4. The server immediately starts listening for the next XHR call.
- * 5. The client receives this XHR response, processes it.
- * 6. The client sends a new async XHR request.
- *
- * The CallbackServer class requires the following permission in Android manifest file
- *
- *
- * If the device has a proxy set, then XHR cannot be used, so polling must be used instead.
- * This can be determined by the client by calling CallbackServer.usePolling().
- *
- * The process flow for polling is:
- * 1. The client calls CallbackServer.getJavascript() to retrieve next statement.
- * 2. If statement available, then client processes it.
- * 3. The client repeats #1 in loop.
- */
-public class CallbackServer implements Runnable {
-
- @SuppressWarnings("unused")
- private static final String LOG_TAG = "CallbackServer";
-
- private ServerSocket waitSocket;
- /**
- * The list of JavaScript statements to be sent to JavaScript.
- * This can be null when there are no messages available.
- */
- private NativeToJsMessageQueue jsMessageQueue;
-
- /**
- * The port to listen on.
- */
- private int port;
-
- /**
- * The server thread.
- */
- private Thread serverThread;
-
- /**
- * Indicates the server is running.
- */
- private boolean active;
-
-
- /**
- * Indicates that polling should be used instead of XHR.
- */
- private boolean usePolling = true;
-
- /**
- * Security token to prevent other apps from accessing this callback server via XHR
- */
- private String token;
-
- /**
- * Constructor.
- */
- public CallbackServer() {
- //Log.d(LOG_TAG, "CallbackServer()");
- this.active = false;
- this.port = 0;
- }
-
- /**
- * Init callback server and start XHR if running local app.
- *
- * If Cordova app is loaded from file://, then we can use XHR
- * otherwise we have to use polling due to cross-domain security restrictions.
- *
- * @param url The URL of the Cordova app being loaded
- */
- public void init(String url) {
- //System.out.println("CallbackServer.start("+url+")");
- this.stopServer();
- this.port = 0;
-
- // Determine if XHR or polling is to be used
- if ((url != null) && !url.startsWith("file://")) {
- this.usePolling = true;
- this.stopServer();
- }
- else if (android.net.Proxy.getDefaultHost() != null) {
- this.usePolling = true;
- this.stopServer();
- }
- else {
- this.usePolling = false;
- this.startServer();
- }
- }
-
- /**
- * Return if polling is being used instead of XHR.
- * @return
- */
- public boolean usePolling() {
- return this.usePolling;
- }
-
- /**
- * Get the port that this server is running on.
- * @return
- */
- public int getPort() {
- return this.port;
- }
-
- /**
- * Get the security token that this server requires when calling getJavascript().
- * @return
- */
- public String getToken() {
- return this.token;
- }
-
- /**
- * Start the server on a new thread.
- */
- public void startServer() {
- //Log.d(LOG_TAG, "CallbackServer.startServer()");
- this.active = false;
-
- // Start server on new thread
- this.serverThread = new Thread(this);
- this.serverThread.start();
- }
-
- /**
- * Restart the server on a new thread.
- */
- public void restartServer() {
-
- // Stop server
- this.stopServer();
-
- // Start server again
- this.startServer();
- }
-
- /**
- * Start running the server.
- * This is called automatically when the server thread is started.
- */
- public void run() {
-
- // Start server
- try {
- this.active = true;
- String request;
- waitSocket = new ServerSocket(0);
- this.port = waitSocket.getLocalPort();
- //Log.d(LOG_TAG, "CallbackServer -- using port " +this.port);
- this.token = java.util.UUID.randomUUID().toString();
- //Log.d(LOG_TAG, "CallbackServer -- using token "+this.token);
-
- while (this.active) {
- //Log.d(LOG_TAG, "CallbackServer: Waiting for data on socket");
- Socket connection = waitSocket.accept();
- BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()), 40);
- DataOutputStream output = new DataOutputStream(connection.getOutputStream());
- request = xhrReader.readLine();
- String response = "";
- //Log.d(LOG_TAG, "CallbackServerRequest="+request);
- if (this.active && (request != null)) {
- if (request.contains("GET")) {
-
- // Get requested file
- String[] requestParts = request.split(" ");
-
- // Must have security token
- if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
- //Log.d(LOG_TAG, "CallbackServer -- Processing GET request");
- String payload = null;
-
- // Wait until there is some data to send, or send empty data every 10 sec
- // to prevent XHR timeout on the client
- while (this.active) {
- if (jsMessageQueue != null) {
- payload = jsMessageQueue.popAndEncode();
- if (payload != null) {
- break;
- }
- }
- synchronized (this) {
- try {
- this.wait(10000); // prevent timeout from happening
- //Log.d(LOG_TAG, "CallbackServer>>> break <<<");
- break;
- } catch (Exception e) {
- }
- }
- }
-
- // If server is still running
- if (this.active) {
-
- // If no data, then send 404 back to client before it times out
- if (payload == null) {
- //Log.d(LOG_TAG, "CallbackServer -- sending data 0");
- response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
- }
- else {
- //Log.d(LOG_TAG, "CallbackServer -- sending item");
- response = "HTTP/1.1 200 OK\r\n\r\n";
- response += encode(payload, "UTF-8");
- }
- }
- else {
- response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";
- }
- }
- else {
- response = "HTTP/1.1 403 Forbidden\r\n\r\n ";
- }
- }
- else {
- response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
- }
- //Log.d(LOG_TAG, "CallbackServer: response="+response);
- //Log.d(LOG_TAG, "CallbackServer: closing output");
- output.writeBytes(response);
- output.flush();
- }
- output.close();
- xhrReader.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- this.active = false;
- //Log.d(LOG_TAG, "CallbackServer.startServer() - EXIT");
- }
-
- /**
- * Stop server.
- * This stops the thread that the server is running on.
- */
- public void stopServer() {
- //Log.d(LOG_TAG, "CallbackServer.stopServer()");
- if (this.active) {
- this.active = false;
-
- try { waitSocket.close(); } catch (IOException ignore) {}
-
- // Break out of server wait
- synchronized (this) {
- this.notify();
- }
- }
- }
-
- /**
- * Destroy
- */
- public void destroy() {
- this.stopServer();
- }
-
- public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
- synchronized (this) {
- this.jsMessageQueue = queue;
- this.notify();
- }
- }
-
- /* The Following code has been modified from original implementation of URLEncoder */
-
- /* start */
-
- /*
- * 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.
- */
- static final String digits = "0123456789ABCDEF";
-
- /**
- * This will encode the return value to JavaScript. We revert the encoding for
- * common characters that don't require encoding to reduce the size of the string
- * being passed to JavaScript.
- *
- * @param s to be encoded
- * @param enc encoding type
- * @return encoded string
- */
- public static String encode(String s, String enc) throws UnsupportedEncodingException {
- if (s == null || enc == null) {
- throw new NullPointerException();
- }
- // check for UnsupportedEncodingException
- "".getBytes(enc);
-
- // Guess a bit bigger for encoded form
- StringBuilder buf = new StringBuilder(s.length() + 16);
- int start = -1;
- for (int i = 0; i < s.length(); i++) {
- char ch = s.charAt(i);
- if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
- || (ch >= '0' && ch <= '9')
- || " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) {
- if (start >= 0) {
- convert(s.substring(start, i), buf, enc);
- start = -1;
- }
- if (ch != ' ') {
- buf.append(ch);
- } else {
- buf.append(' ');
- }
- } else {
- if (start < 0) {
- start = i;
- }
- }
- }
- if (start >= 0) {
- convert(s.substring(start, s.length()), buf, enc);
- }
- return buf.toString();
- }
-
- private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException {
- byte[] bytes = s.getBytes(enc);
- for (int j = 0; j < bytes.length; j++) {
- buf.append('%');
- buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
- buf.append(digits.charAt(bytes[j] & 0xf));
- }
- }
-
- /* end */
-}
diff --git a/framework/src/org/apache/cordova/CompassListener.java b/framework/src/org/apache/cordova/CompassListener.java
index 83fcdd0a..458afaf4 100755
--- a/framework/src/org/apache/cordova/CompassListener.java
+++ b/framework/src/org/apache/cordova/CompassListener.java
@@ -33,6 +33,8 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.content.Context;
+import android.util.Log;
+
/**
* This class listens to the compass sensor and stores the latest heading value.
*/
@@ -166,6 +168,13 @@ public class CompassListener extends Plugin implements SensorEventListener {
this.stop();
}
+ /**
+ * Called when app has navigated and JS listeners have been destroyed.
+ */
+ public void onReset() {
+ this.stop();
+ }
+
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java
index 398286d2..2f4cdff9 100755
--- a/framework/src/org/apache/cordova/CordovaChromeClient.java
+++ b/framework/src/org/apache/cordova/CordovaChromeClient.java
@@ -226,24 +226,6 @@ public class CordovaChromeClient extends WebChromeClient {
result.confirm("OK");
}
- // Calling into CallbackServer
- else if (reqOk && defaultValue != null && defaultValue.equals("gap_callbackServer:")) {
- String r = "";
- if (message.equals("usePolling")) {
- r = "" + this.appView.callbackServer.usePolling();
- }
- else if (message.equals("restartServer")) {
- this.appView.callbackServer.restartServer();
- }
- else if (message.equals("getPort")) {
- r = Integer.toString(this.appView.callbackServer.getPort());
- }
- else if (message.equals("getToken")) {
- r = this.appView.callbackServer.getToken();
- }
- result.confirm(r);
- }
-
// Show dialog
else {
final JsPromptResult res = result;
diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java
index ddae1dda..106f60d9 100755
--- a/framework/src/org/apache/cordova/CordovaWebView.java
+++ b/framework/src/org/apache/cordova/CordovaWebView.java
@@ -50,6 +50,8 @@ import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.WindowManager;
+import android.webkit.WebBackForwardList;
+import android.webkit.WebHistoryItem;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebSettings.LayoutAlgorithm;
@@ -65,7 +67,6 @@ public class CordovaWebView extends WebView {
private ArrayList keyUpCodes = new ArrayList();
public PluginManager pluginManager;
- public CallbackServer callbackServer;
private boolean paused;
private BroadcastReceiver receiver;
@@ -572,12 +573,13 @@ public class CordovaWebView extends WebView {
// Check webview first to see if there is a history
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
if (super.canGoBack()) {
+ printBackForwardList();
super.goBack();
return true;
}
// If our managed history has prev url
- if (this.urls.size() > 1) {
+ if (this.urls.size() > 1 && !this.useBrowserHistory) {
this.urls.pop(); // Pop current url
String url = this.urls.pop(); // Pop prev url that we want to load, since it will be added back by loadUrl()
this.loadUrl(url);
@@ -937,4 +939,17 @@ public class CordovaWebView extends WebView {
settings.setAllowUniversalAccessFromFileURLs(true);
}
}
+
+
+
+ public void printBackForwardList() {
+ WebBackForwardList currentList = this.copyBackForwardList();
+ int currentSize = currentList.getSize();
+ for(int i = 0; i < currentSize; ++i)
+ {
+ WebHistoryItem item = currentList.getItemAtIndex(i);
+ String url = item.getUrl();
+ LOG.d(TAG, "The URL at index: " + Integer.toString(i) + "is " + url );
+ }
+ }
}
diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java
index fe0e9e9f..b0c318cd 100755
--- a/framework/src/org/apache/cordova/CordovaWebViewClient.java
+++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java
@@ -23,20 +23,15 @@ import java.util.Hashtable;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.PluginResult;
-import java.io.IOException;
-import java.io.InputStream;
-
import org.apache.cordova.api.LOG;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.TargetApi;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
@@ -44,7 +39,6 @@ import android.util.Log;
import android.view.View;
import android.webkit.HttpAuthHandler;
import android.webkit.SslErrorHandler;
-import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
@@ -107,7 +101,7 @@ public class CordovaWebViewClient extends WebViewClient {
String action = url.substring(idx2 + 1, idx3);
String callbackId = url.substring(idx3 + 1, idx4);
String jsonArgs = url.substring(idx4 + 1);
- appView.pluginManager.exec(service, action, callbackId, jsonArgs, true /* async */);
+ appView.pluginManager.exec(service, action, callbackId, jsonArgs);
}
/**
@@ -261,14 +255,13 @@ public class CordovaWebViewClient extends WebViewClient {
// Flush stale messages.
this.appView.jsMessageQueue.reset();
- // Create callback server
- if (this.appView.callbackServer == null) {
- this.appView.callbackServer = new CallbackServer();
- }
- this.appView.callbackServer.init(url);
-
// Broadcast message that page has loaded
this.appView.postMessage("onPageStarted", url);
+
+ // Notify all plugins of the navigation, so they can clean up if necessary.
+ if (this.appView.pluginManager != null) {
+ this.appView.pluginManager.onReset();
+ }
}
/**
@@ -329,9 +322,6 @@ public class CordovaWebViewClient extends WebViewClient {
// Shutdown if blank loaded
if (url.equals("about:blank")) {
- if (this.appView.callbackServer != null) {
- this.appView.callbackServer.destroy();
- }
appView.postMessage("exit", null);
}
}
diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java
index b36320fa..354a484c 100755
--- a/framework/src/org/apache/cordova/DroidGap.java
+++ b/framework/src/org/apache/cordova/DroidGap.java
@@ -19,6 +19,8 @@
package org.apache.cordova;
import java.util.HashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import org.apache.cordova.api.IPlugin;
import org.apache.cordova.api.LOG;
@@ -142,6 +144,8 @@ public class DroidGap extends Activity implements CordovaInterface {
protected LinearLayout root;
protected boolean cancelLoadUrl = false;
protected ProgressDialog spinnerDialog = null;
+ private final ExecutorService threadPool = Executors.newCachedThreadPool();
+
// The initial URL for our app
// ie http://server/path/index.html#abc?query
@@ -1051,4 +1055,9 @@ public class DroidGap extends Activity implements CordovaInterface {
}
return null;
}
+
+ @Override
+ public ExecutorService getThreadPool() {
+ return threadPool;
+ }
}
diff --git a/framework/src/org/apache/cordova/ExposedJsApi.java b/framework/src/org/apache/cordova/ExposedJsApi.java
index 710b2e0c..b386a402 100755
--- a/framework/src/org/apache/cordova/ExposedJsApi.java
+++ b/framework/src/org/apache/cordova/ExposedJsApi.java
@@ -40,7 +40,7 @@ import org.json.JSONException;
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
jsMessageQueue.setPaused(true);
try {
- boolean wasSync = pluginManager.exec(service, action, callbackId, arguments, true /* async */);
+ boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
String ret = "";
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
ret = jsMessageQueue.popAndEncode();
diff --git a/framework/src/org/apache/cordova/FileProgressResult.java b/framework/src/org/apache/cordova/FileProgressResult.java
new file mode 100644
index 00000000..d9811755
--- /dev/null
+++ b/framework/src/org/apache/cordova/FileProgressResult.java
@@ -0,0 +1,63 @@
+/*
+ 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;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Encapsulates in-progress status of uploading or downloading a file to a remote server.
+ */
+public class FileProgressResult {
+
+ private boolean lengthComputable = false; // declares whether total is known
+ private long loaded = 0; // bytes sent so far
+ private long total = 0; // bytes total, if known
+
+ public boolean getLengthComputable() {
+ return lengthComputable;
+ }
+
+ public void setLengthComputable(boolean computable) {
+ this.lengthComputable = computable;
+ }
+
+ public long getLoaded() {
+ return loaded;
+ }
+
+ public void setLoaded(long bytes) {
+ this.loaded = bytes;
+ }
+
+ public long getTotal() {
+ return total;
+ }
+
+ public void setTotal(long bytes) {
+ this.total = bytes;
+ }
+
+ public JSONObject toJSONObject() throws JSONException {
+ return new JSONObject(
+ "{loaded:" + loaded +
+ ",total:" + total +
+ ",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
+ }
+}
diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java
index 881caa54..1917d8a7 100644
--- a/framework/src/org/apache/cordova/FileTransfer.java
+++ b/framework/src/org/apache/cordova/FileTransfer.java
@@ -24,6 +24,7 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
+import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
@@ -32,6 +33,7 @@ import java.net.URL;
import java.net.URLDecoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
+import java.util.HashSet;
import java.util.Iterator;
import javax.net.ssl.HostnameVerifier;
@@ -58,34 +60,81 @@ public class FileTransfer extends Plugin {
private static final String LOG_TAG = "FileTransfer";
private static final String LINE_START = "--";
private static final String LINE_END = "\r\n";
- private static final String BOUNDARY = "*****";
+ private static final String BOUNDARY = "+++++";
public static int FILE_NOT_FOUND_ERR = 1;
public static int INVALID_URL_ERR = 2;
public static int CONNECTION_ERR = 3;
+ public static int ABORTED_ERR = 4;
+
+ private static HashSet abortTriggered = new HashSet();
private SSLSocketFactory defaultSSLSocketFactory = null;
private HostnameVerifier defaultHostnameVerifier = null;
+ private static final class AbortException extends Exception {
+ private static final long serialVersionUID = 1L;
+ public AbortException(String str) {
+ super(str);
+ }
+ }
+
+ /**
+ * Works around a bug on Android 2.3.
+ * http://code.google.com/p/android/issues/detail?id=14562
+ */
+ private static final class DoneHandlerInputStream extends FilterInputStream {
+ private boolean done;
+
+ public DoneHandlerInputStream(InputStream stream) {
+ super(stream);
+ }
+
+ @Override
+ public int read() throws IOException {
+ int result = done ? -1 : super.read();
+ done = (result == -1);
+ return result;
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ int result = done ? -1 : super.read(buffer);
+ done = (result == -1);
+ return result;
+ }
+
+ @Override
+ public int read(byte[] bytes, int offset, int count) throws IOException {
+ int result = done ? -1 : super.read(bytes, offset, count);
+ done = (result == -1);
+ return result;
+ }
+ }
+
/* (non-Javadoc)
* @see org.apache.cordova.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
*/
@Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
- String source = null;
- String target = null;
- try {
- source = args.getString(0);
- target = args.getString(1);
- } catch (JSONException e) {
- Log.d(LOG_TAG, "Missing source or target");
- return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
- }
+ if (action.equals("upload") || action.equals("download")) {
+ String source = null;
+ String target = null;
+ try {
+ source = args.getString(0);
+ target = args.getString(1);
+ } catch (JSONException e) {
+ Log.d(LOG_TAG, "Missing source or target");
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target");
+ }
- if (action.equals("upload")) {
- return upload(URLDecoder.decode(source), target, args);
- } else if (action.equals("download")) {
- return download(source, target, args.optBoolean(2));
+ if (action.equals("upload")) {
+ return upload(URLDecoder.decode(source), target, args, callbackId);
+ } else {
+ return download(source, target, args, callbackId);
+ }
+ } else if (action.equals("abort")) {
+ return abort(args);
} else {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
@@ -96,6 +145,7 @@ public class FileTransfer extends Plugin {
* @param source Full path of the file on the file system
* @param target URL of the server to receive the file
* @param args JSON Array of args
+ * @param callbackId callback id for optional progress reports
*
* args[2] fileKey Name of file request parameter
* args[3] fileName File name to be used on server
@@ -103,7 +153,7 @@ public class FileTransfer extends Plugin {
* args[5] params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
- private PluginResult upload(String source, String target, JSONArray args) {
+ private PluginResult upload(String source, String target, JSONArray args, String callbackId) {
Log.d(LOG_TAG, "upload " + source + " to " + target);
HttpURLConnection conn = null;
@@ -121,6 +171,7 @@ public class FileTransfer extends Plugin {
if (headers == null && params != null) {
headers = params.optJSONObject("headers");
}
+ String objectId = args.getString(9);
Log.d(LOG_TAG, "fileKey: " + fileKey);
Log.d(LOG_TAG, "fileName: " + fileName);
@@ -129,12 +180,14 @@ public class FileTransfer extends Plugin {
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
Log.d(LOG_TAG, "headers: " + headers);
+ Log.d(LOG_TAG, "objectId: " + objectId);
// Create return object
FileUploadResult result = new FileUploadResult();
+ FileProgressResult progress = new FileProgressResult();
// Get a input stream of the file on the phone
- FileInputStream fileInputStream = (FileInputStream) getPathFromUri(source);
+ InputStream inputStream = getPathFromUri(source);
DataOutputStream dos = null;
@@ -242,12 +295,18 @@ public class FileTransfer extends Plugin {
int stringLength = extraBytes.length + midParams.length() + tailParams.length() + fileNameBytes.length;
Log.d(LOG_TAG, "String Length: " + stringLength);
- int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
+ int fixedLength = -1;
+ if (inputStream instanceof FileInputStream) {
+ fixedLength = (int) ((FileInputStream)inputStream).getChannel().size() + stringLength;
+ progress.setLengthComputable(true);
+ progress.setTotal(fixedLength);
+ }
Log.d(LOG_TAG, "Content Length: " + fixedLength);
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
// http://code.google.com/p/android/issues/detail?id=3164
// It also causes OOM if HTTPS is used, even on newer devices.
chunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
+ chunkedMode = chunkedMode || (fixedLength == -1);
if (chunkedMode) {
conn.setChunkedStreamingMode(maxBufferSize);
@@ -265,12 +324,12 @@ public class FileTransfer extends Plugin {
dos.writeBytes(midParams);
// create a buffer of maximum size
- bytesAvailable = fileInputStream.available();
+ bytesAvailable = inputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
- bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+ bytesRead = inputStream.read(buffer, 0, bufferSize);
totalBytes = 0;
long prevBytesRead = 0;
@@ -282,28 +341,36 @@ public class FileTransfer extends Plugin {
prevBytesRead = totalBytes;
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
}
- bytesAvailable = fileInputStream.available();
+ bytesAvailable = inputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
- bytesRead = fileInputStream.read(buffer, 0, bufferSize);
+ bytesRead = inputStream.read(buffer, 0, bufferSize);
+ if (objectId != null) {
+ // Only send progress callbacks if the JS code sent us an object ID,
+ // so we don't spam old versions with unrecognized callbacks.
+ progress.setLoaded(totalBytes);
+ PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
+ progressResult.setKeepCallback(true);
+ success(progressResult, callbackId);
+ }
+ synchronized (abortTriggered) {
+ if (objectId != null && abortTriggered.contains(objectId)) {
+ abortTriggered.remove(objectId);
+ throw new AbortException("upload aborted");
+ }
+ }
}
// send multipart form data necessary after file data...
dos.writeBytes(tailParams);
// close streams
- fileInputStream.close();
+ inputStream.close();
dos.flush();
dos.close();
//------------------ read the SERVER RESPONSE
StringBuffer responseString = new StringBuffer("");
- DataInputStream inStream;
- try {
- inStream = new DataInputStream ( conn.getInputStream() );
- } catch(FileNotFoundException e) {
- Log.e(LOG_TAG, e.toString(), e);
- throw new IOException("Received error from server");
- }
+ DataInputStream inStream = new DataInputStream(getInputStream(conn));
String line;
while (( line = inStream.readLine()) != null) {
@@ -342,6 +409,9 @@ public class FileTransfer extends Plugin {
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+ } catch (AbortException e) {
+ JSONObject error = createFileTransferError(ABORTED_ERR, source, target, conn);
+ return new PluginResult(PluginResult.Status.ERROR, error);
} catch (Throwable t) {
// Shouldn't happen, but will
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
@@ -354,6 +424,13 @@ public class FileTransfer extends Plugin {
}
}
+ private InputStream getInputStream(HttpURLConnection conn) throws IOException {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
+ return new DoneHandlerInputStream(conn.getInputStream());
+ }
+ return conn.getInputStream();
+ }
+
// always verify the host - don't check for certificate
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
@@ -459,11 +536,13 @@ public class FileTransfer extends Plugin {
* @param target Full path of the file on the file system
* @return JSONObject the downloaded file
*/
- private PluginResult download(String source, String target, boolean trustEveryone) {
+ private PluginResult download(String source, String target, JSONArray args, String callbackId) {
Log.d(LOG_TAG, "download " + source + " to " + target);
HttpURLConnection connection = null;
try {
+ boolean trustEveryone = args.optBoolean(2);
+ String objectId = args.getString(3);
File file = getFileFromPath(target);
// create needed directories
@@ -513,22 +592,39 @@ public class FileTransfer extends Plugin {
connection.connect();
Log.d(LOG_TAG, "Download file:" + url);
- InputStream inputStream;
- try {
- inputStream = connection.getInputStream();
- } catch(FileNotFoundException e) {
- Log.e(LOG_TAG, e.toString(), e);
- throw new IOException("Received error from server");
- }
+ InputStream inputStream = getInputStream(connection);
byte[] buffer = new byte[1024];
int bytesRead = 0;
+ long totalBytes = 0;
+ FileProgressResult progress = new FileProgressResult();
+
+ if (connection.getContentEncoding() == null) {
+ // Only trust content-length header if no gzip etc
+ progress.setLengthComputable(true);
+ progress.setTotal(connection.getContentLength());
+ }
FileOutputStream outputStream = new FileOutputStream(file);
// write bytes to file
while ((bytesRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, bytesRead);
+ totalBytes += bytesRead;
+ if (objectId != null) {
+ // Only send progress callbacks if the JS code sent us an object ID,
+ // so we don't spam old versions with unrecognized callbacks.
+ progress.setLoaded(totalBytes);
+ PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
+ progressResult.setKeepCallback(true);
+ success(progressResult, callbackId);
+ }
+ synchronized (abortTriggered) {
+ if (objectId != null && abortTriggered.contains(objectId)) {
+ abortTriggered.remove(objectId);
+ throw new AbortException("download aborted");
+ }
+ }
}
outputStream.close();
@@ -554,6 +650,9 @@ public class FileTransfer extends Plugin {
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
}
+ } catch (AbortException e) {
+ JSONObject error = createFileTransferError(ABORTED_ERR, source, target, connection);
+ return new PluginResult(PluginResult.Status.ERROR, error);
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
Log.d(LOG_TAG, "I got a file not found exception");
@@ -621,4 +720,23 @@ public class FileTransfer extends Plugin {
return file;
}
+
+ /**
+ * Abort an ongoing upload or download.
+ *
+ * @param args args
+ */
+ private PluginResult abort(JSONArray args) {
+ String objectId;
+ try {
+ objectId = args.getString(0);
+ } catch (JSONException e) {
+ Log.d(LOG_TAG, "Missing objectId");
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing objectId");
+ }
+ synchronized (abortTriggered) {
+ abortTriggered.add(objectId);
+ }
+ return new PluginResult(PluginResult.Status.OK);
+ }
}
diff --git a/framework/src/org/apache/cordova/FileUploadResult.java b/framework/src/org/apache/cordova/FileUploadResult.java
index 36fae93f..b556869e 100644
--- a/framework/src/org/apache/cordova/FileUploadResult.java
+++ b/framework/src/org/apache/cordova/FileUploadResult.java
@@ -29,6 +29,7 @@ public class FileUploadResult {
private long bytesSent = 0; // bytes sent
private int responseCode = -1; // HTTP response code
private String response = null; // HTTP response
+ private String objectId = null; // FileTransfer object id
public long getBytesSent() {
return bytesSent;
@@ -54,10 +55,19 @@ public class FileUploadResult {
this.response = response;
}
+ public String getObjectId() {
+ return objectId;
+ }
+
+ public void setObjectId(String objectId) {
+ this.objectId = objectId;
+ }
+
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{bytesSent:" + bytesSent +
",responseCode:" + responseCode +
- ",response:" + JSONObject.quote(response) + "}");
+ ",response:" + JSONObject.quote(response) +
+ ",objectId:" + JSONObject.quote(objectId) + "}");
}
}
diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java
index 1c8f0841..cbeff98a 100755
--- a/framework/src/org/apache/cordova/FileUtils.java
+++ b/framework/src/org/apache/cordova/FileUtils.java
@@ -1048,10 +1048,16 @@ public class FileUtils extends Plugin {
*/
@SuppressWarnings("deprecation")
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
- String[] proj = { _DATA };
- Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
- int column_index = cursor.getColumnIndexOrThrow(_DATA);
- cursor.moveToFirst();
- return cursor.getString(column_index);
+ String uri = contentUri.toString();
+ if (uri.startsWith("content:")) {
+ String[] proj = { _DATA };
+ Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
+ int column_index = cursor.getColumnIndexOrThrow(_DATA);
+ cursor.moveToFirst();
+ return cursor.getString(column_index);
+ } else {
+ return uri;
+ }
+
}
}
diff --git a/framework/src/org/apache/cordova/GeoBroker.java b/framework/src/org/apache/cordova/GeoBroker.java
index d3bf6b34..e6798a99 100755
--- a/framework/src/org/apache/cordova/GeoBroker.java
+++ b/framework/src/org/apache/cordova/GeoBroker.java
@@ -135,10 +135,22 @@ public class GeoBroker extends Plugin {
* Stop listener.
*/
public void onDestroy() {
- this.networkListener.destroy();
- this.gpsListener.destroy();
- this.networkListener = null;
- this.gpsListener = null;
+ if (this.networkListener != null) {
+ this.networkListener.destroy();
+ this.networkListener = null;
+ }
+ if (this.gpsListener != null) {
+ this.gpsListener.destroy();
+ this.gpsListener = null;
+ }
+ }
+
+ /**
+ * Called when the view navigates.
+ * Stop the listeners.
+ */
+ public void onReset() {
+ this.onDestroy();
}
public JSONObject returnLocationJSON(Location loc) {
diff --git a/framework/src/org/apache/cordova/Globalization.java b/framework/src/org/apache/cordova/Globalization.java
new file mode 100644
index 00000000..98bf9e5a
--- /dev/null
+++ b/framework/src/org/apache/cordova/Globalization.java
@@ -0,0 +1,579 @@
+/*
+ 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;
+
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Currency;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.cordova.api.Plugin;
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.text.format.Time;
+
+/**
+ *
+ */
+public class Globalization extends Plugin {
+ //GlobalizationCommand Plugin Actions
+ public static final String GETLOCALENAME = "getLocaleName";
+ public static final String DATETOSTRING = "dateToString";
+ public static final String STRINGTODATE = "stringToDate";
+ public static final String GETDATEPATTERN = "getDatePattern";
+ public static final String GETDATENAMES = "getDateNames";
+ public static final String ISDAYLIGHTSAVINGSTIME = "isDayLightSavingsTime";
+ public static final String GETFIRSTDAYOFWEEK = "getFirstDayOfWeek";
+ public static final String NUMBERTOSTRING = "numberToString";
+ public static final String STRINGTONUMBER = "stringToNumber";
+ public static final String GETNUMBERPATTERN = "getNumberPattern";
+ public static final String GETCURRENCYPATTERN = "getCurrencyPattern";
+ public static final String GETPREFERREDLANGUAGE = "getPreferredLanguage";
+
+ //GlobalizationCommand Option Parameters
+ public static final String OPTIONS = "options";
+ public static final String FORMATLENGTH = "formatLength";
+ //public static final String SHORT = "short"; //default for dateToString format
+ public static final String MEDIUM = "medium";
+ public static final String LONG = "long";
+ public static final String FULL = "full";
+ public static final String SELECTOR = "selector";
+ //public static final String DATEANDTIME = "date and time"; //default for dateToString
+ public static final String DATE = "date";
+ public static final String TIME = "time";
+ public static final String DATESTRING = "dateString";
+ public static final String TYPE = "type";
+ public static final String ITEM = "item";
+ public static final String NARROW = "narrow";
+ public static final String WIDE = "wide";
+ public static final String MONTHS = "months";
+ public static final String DAYS = "days";
+ //public static final String DECMIAL = "wide"; //default for numberToString
+ public static final String NUMBER = "number";
+ public static final String NUMBERSTRING = "numberString";
+ public static final String PERCENT = "percent";
+ public static final String CURRENCY = "currency";
+ public static final String CURRENCYCODE = "currencyCode";
+
+ @Override
+ public PluginResult execute(String action, JSONArray data, String callbackId) {
+ PluginResult.Status status = PluginResult.Status.OK;
+ JSONObject obj = new JSONObject();
+
+ try{
+ if (action.equals(GETLOCALENAME)){
+ obj = getLocaleName();
+ return new PluginResult(status, obj);
+ }else if (action.equals(GETPREFERREDLANGUAGE)){
+ obj = getPreferredLanguage();
+ return new PluginResult(status, obj);
+ } else if (action.equalsIgnoreCase(DATETOSTRING)) {
+ obj = getDateToString(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(STRINGTODATE)){
+ obj = getStringtoDate(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(GETDATEPATTERN)){
+ obj = getDatePattern(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(GETDATENAMES)){
+ obj = getDateNames(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(ISDAYLIGHTSAVINGSTIME)){
+ obj = getIsDayLightSavingsTime(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(GETFIRSTDAYOFWEEK)){
+ obj = getFirstDayOfWeek(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(NUMBERTOSTRING)){
+ obj = getNumberToString(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(STRINGTONUMBER)){
+ obj = getStringToNumber(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(GETNUMBERPATTERN)){
+ obj = getNumberPattern(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }else if(action.equalsIgnoreCase(GETCURRENCYPATTERN)){
+ obj = getCurrencyPattern(data);
+ return new PluginResult(PluginResult.Status.OK, obj);
+ }
+ }catch (GlobalizationError ge){
+ return new PluginResult(PluginResult.Status.ERROR, ge.toJson());
+ }catch (Exception e){
+ return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
+ }
+ return new PluginResult(PluginResult.Status.INVALID_ACTION);
+ }
+ /*
+ * @Description: Returns the string identifier for the client's current locale setting
+ *
+ * @Return: JSONObject
+ * Object.value {String}: The locale identifier
+ *
+ * @throws: GlobalizationError.UNKNOWN_ERROR
+ */
+ private JSONObject getLocaleName() throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ try{
+ obj.put("value",Locale.getDefault().toString());//get the locale from the Android Device
+ return obj;
+ }catch(Exception e){
+ throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
+ }
+ }
+ /*
+ * @Description: Returns the string identifier for the client's current language
+ *
+ * @Return: JSONObject
+ * Object.value {String}: The language identifier
+ *
+ * @throws: GlobalizationError.UNKNOWN_ERROR
+ */
+ private JSONObject getPreferredLanguage() throws GlobalizationError {
+ JSONObject obj = new JSONObject();
+ try {
+ obj.put("value", Locale.getDefault().getDisplayLanguage().toString());
+ return obj;
+ } catch (Exception e) {
+ throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
+ }
+ }
+ /*
+ * @Description: Returns a date formatted as a string according to the client's user preferences and
+ * calendar using the time zone of the client.
+ *
+ * @Return: JSONObject
+ * Object.value {String}: The localized date string
+ *
+ * @throws: GlobalizationError.FORMATTING_ERROR
+ */
+ private JSONObject getDateToString(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ try{
+ Date date = new Date((Long)options.getJSONObject(0).get(DATE));
+
+ //get formatting pattern from android device (Will only have device specific formatting for short form of date) or options supplied
+ JSONObject datePattern = getDatePattern(options);
+ SimpleDateFormat fmt = new SimpleDateFormat(datePattern.getString("pattern"));
+
+ //return formatted date
+ return obj.put("value",fmt.format(date));
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Parses a date formatted as a string according to the client's user
+ * preferences and calendar using the time zone of the client and returns
+ * the corresponding date object
+ * @Return: JSONObject
+ * Object.year {Number}: The four digit year
+ * Object.month {Number}: The month from (0 - 11)
+ * Object.day {Number}: The day from (1 - 31)
+ * Object.hour {Number}: The hour from (0 - 23)
+ * Object.minute {Number}: The minute from (0 - 59)
+ * Object.second {Number}: The second from (0 - 59)
+ * Object.millisecond {Number}: The milliseconds (from 0 - 999), not available on all platforms
+ *
+ * @throws: GlobalizationError.PARSING_ERROR
+ */
+ private JSONObject getStringtoDate(JSONArray options)throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ Date date;
+ try{
+ //get format pattern from android device (Will only have device specific formatting for short form of date) or options supplied
+ DateFormat fmt = new SimpleDateFormat(getDatePattern(options).getString("pattern"));
+
+ //attempt parsing string based on user preferences
+ date = fmt.parse(options.getJSONObject(0).get(DATESTRING).toString());
+
+ //set Android Time object
+ Time time = new Time();
+ time.set(date.getTime());
+
+ //return properties;
+ obj.put("year", time.year);
+ obj.put("month", time.month);
+ obj.put("day", time.monthDay);
+ obj.put("hour", time.hour);
+ obj.put("minute", time.minute);
+ obj.put("second", time.second);
+ obj.put("millisecond", new Long(0));
+ return obj;
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns a pattern string for formatting and parsing dates according to the client's
+ * user preferences.
+ * @Return: JSONObject
+ *
+ * Object.pattern {String}: The date and time pattern for formatting and parsing dates.
+ * The patterns follow Unicode Technical Standard #35
+ * http://unicode.org/reports/tr35/tr35-4.html
+ * Object.timezone {String}: The abbreviated name of the time zone on the client
+ * Object.utc_offset {Number}: The current difference in seconds between the client's
+ * time zone and coordinated universal time.
+ * Object.dst_offset {Number}: The current daylight saving time offset in seconds
+ * between the client's non-daylight saving's time zone
+ * and the client's daylight saving's time zone.
+ *
+ * @throws: GlobalizationError.PATTERN_ERROR
+ */
+ private JSONObject getDatePattern(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+
+ try{
+ SimpleDateFormat fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getDateFormat(this.cordova.getActivity()); //default user preference for date
+ SimpleDateFormat fmtTime = (SimpleDateFormat)android.text.format.DateFormat.getTimeFormat(this.cordova.getActivity()); //default user preference for time
+
+ String fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern(); //default SHORT date/time format. ex. dd/MM/yyyy h:mm a
+
+ //get Date value + options (if available)
+ if (options.getJSONObject(0).length() > 1){
+ //options were included
+
+ //get formatLength option
+ if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(FORMATLENGTH)){
+ String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(FORMATLENGTH);
+ if (fmtOpt.equalsIgnoreCase(MEDIUM)){//medium
+ fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getMediumDateFormat(this.cordova.getActivity());
+ }else if (fmtOpt.equalsIgnoreCase(LONG) || fmtOpt.equalsIgnoreCase(FULL)){ //long/full
+ fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getLongDateFormat(this.cordova.getActivity());
+ }
+ }
+
+ //return pattern type
+ fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern();
+ if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(SELECTOR)){
+ String selOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(SELECTOR);
+ if (selOpt.equalsIgnoreCase(DATE)){
+ fmt = fmtDate.toLocalizedPattern();
+ }else if (selOpt.equalsIgnoreCase(TIME)){
+ fmt = fmtTime.toLocalizedPattern();
+ }
+ }
+ }
+
+ //TimeZone from users device
+ //TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone(); //substitute method
+ TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
+
+ obj.put("pattern", fmt);
+ obj.put("timezone", tz.getDisplayName(tz.inDaylightTime(Calendar.getInstance().getTime()),TimeZone.SHORT));
+ obj.put("utc_offset", tz.getRawOffset()/1000);
+ obj.put("dst_offset", tz.getDSTSavings()/1000);
+ return obj;
+
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns an array of either the names of the months or days of the week
+ * according to the client's user preferences and calendar
+ * @Return: JSONObject
+ * Object.value {Array{String}}: The array of names starting from either
+ * the first month in the year or the
+ * first day of the week.
+ *
+ * @throws: GlobalizationError.UNKNOWN_ERROR
+ */
+ private JSONObject getDateNames(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ //String[] value;
+ JSONArray value = new JSONArray();
+ List namesList = new ArrayList();
+ final Map namesMap; // final needed for sorting with anonymous comparator
+ try{
+ int type = 0; //default wide
+ int item = 0; //default months
+
+ //get options if available
+ if (options.getJSONObject(0).length() > 0){
+ //get type if available
+ if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
+ String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
+ if (t.equalsIgnoreCase(NARROW)){type++;} //DateUtils.LENGTH_MEDIUM
+ }
+ //get item if available
+ if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(ITEM)){
+ String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(ITEM);
+ if (t.equalsIgnoreCase(DAYS)){item += 10;} //Days of week start at 1
+ }
+ }
+ //determine return value
+ int method = item + type;
+ if (method == 1) { //months and narrow
+ namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
+ } else if (method == 10) { //days and wide
+ namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault());
+ } else if (method == 11) { //days and narrow
+ namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault());
+ } else { //default: months and wide
+ namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
+ }
+
+ // save names as a list
+ for(String name : namesMap.keySet()) {
+ namesList.add(name);
+ }
+
+ // sort the list according to values in namesMap
+ Collections.sort(namesList, new Comparator() {
+ public int compare(String arg0, String arg1) {
+ return namesMap.get(arg0).compareTo(namesMap.get(arg1));
+ }
+ });
+
+ // convert nameList into JSONArray of String objects
+ for (int i = 0; i < namesList.size(); i ++){
+ value.put(namesList.get(i));
+ }
+
+ //return array of names
+ return obj.put("value", value);
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns whether daylight savings time is in effect for a given date using the client's
+ * time zone and calendar.
+ * @Return: JSONObject
+ * Object.dst {Boolean}: The value "true" indicates that daylight savings time is
+ * in effect for the given date and "false" indicate that it is not. *
+ *
+ * @throws: GlobalizationError.UNKNOWN_ERROR
+ */
+ private JSONObject getIsDayLightSavingsTime(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ boolean dst = false;
+ try{
+ Date date = new Date((Long)options.getJSONObject(0).get(DATE));
+ //TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone();
+ TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
+ dst = tz.inDaylightTime(date); //get daylight savings data from date object and user timezone settings
+
+ return obj.put("dst",dst);
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns the first day of the week according to the client's user preferences and calendar.
+ * The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
+ * @Return: JSONObject
+ * Object.value {Number}: The number of the first day of the week.
+ *
+ * @throws: GlobalizationError.UNKNOWN_ERROR
+ */
+ private JSONObject getFirstDayOfWeek(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ try{
+ int value = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek(); //get first day of week based on user locale settings
+ return obj.put("value", value);
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns a number formatted as a string according to the client's user preferences.
+ * @Return: JSONObject
+ * Object.value {String}: The formatted number string.
+ *
+ * @throws: GlobalizationError.FORMATTING_ERROR
+ */
+ private JSONObject getNumberToString(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ String value = "";
+ try{
+ DecimalFormat fmt = getNumberFormatInstance(options);//returns Decimal/Currency/Percent instance
+ value = fmt.format(options.getJSONObject(0).get(NUMBER));
+ return obj.put("value", value);
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Parses a number formatted as a string according to the client's user preferences and
+ * returns the corresponding number.
+ * @Return: JSONObject
+ * Object.value {Number}: The parsed number.
+ *
+ * @throws: GlobalizationError.PARSING_ERROR
+ */
+ private JSONObject getStringToNumber(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ Number value;
+ try{
+ DecimalFormat fmt = getNumberFormatInstance(options); //returns Decimal/Currency/Percent instance
+ value = fmt.parse((String)options.getJSONObject(0).get(NUMBERSTRING));
+ return obj.put("value", value);
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns a pattern string for formatting and parsing numbers according to the client's user
+ * preferences.
+ * @Return: JSONObject
+ * Object.pattern {String}: The number pattern for formatting and parsing numbers.
+ * The patterns follow Unicode Technical Standard #35.
+ * http://unicode.org/reports/tr35/tr35-4.html
+ * Object.symbol {String}: The symbol to be used when formatting and parsing
+ * e.g., percent or currency symbol.
+ * Object.fraction {Number}: The number of fractional digits to use when parsing and
+ * formatting numbers.
+ * Object.rounding {Number}: The rounding increment to use when parsing and formatting.
+ * Object.positive {String}: The symbol to use for positive numbers when parsing and formatting.
+ * Object.negative: {String}: The symbol to use for negative numbers when parsing and formatting.
+ * Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
+ * Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
+ *
+ * @throws: GlobalizationError.PATTERN_ERROR
+ */
+ private JSONObject getNumberPattern(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ try{
+ //uses java.text.DecimalFormat to format value
+ DecimalFormat fmt = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault()); //default format
+ String symbol = String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator());
+ //get Date value + options (if available)
+ if (options.getJSONObject(0).length() > 0){
+ //options were included
+ if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
+ String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
+ if (fmtOpt.equalsIgnoreCase(CURRENCY)){
+ fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
+ symbol = fmt.getDecimalFormatSymbols().getCurrencySymbol();
+ }else if(fmtOpt.equalsIgnoreCase(PERCENT)){
+ fmt = (DecimalFormat) DecimalFormat.getPercentInstance(Locale.getDefault());
+ symbol = String.valueOf(fmt.getDecimalFormatSymbols().getPercent());
+ }
+ }
+ }
+
+ //return properties
+ obj.put("pattern", fmt.toPattern());
+ obj.put("symbol", symbol);
+ obj.put("fraction", fmt.getMinimumFractionDigits());
+ obj.put("rounding", new Integer(0));
+ obj.put("positive", fmt.getPositivePrefix());
+ obj.put("negative", fmt.getNegativePrefix());
+ obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
+ obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
+
+ return obj;
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Returns a pattern string for formatting and parsing currency values according to the client's
+ * user preferences and ISO 4217 currency code.
+ * @Return: JSONObject
+ * Object.pattern {String}: The currency pattern for formatting and parsing currency values.
+ * The patterns follow Unicode Technical Standard #35
+ * http://unicode.org/reports/tr35/tr35-4.html
+ * Object.code {String}: The ISO 4217 currency code for the pattern.
+ * Object.fraction {Number}: The number of fractional digits to use when parsing and
+ * formatting currency.
+ * Object.rounding {Number}: The rounding increment to use when parsing and formatting.
+ * Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
+ * Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
+ *
+ * @throws: GlobalizationError.FORMATTING_ERROR
+ */
+ private JSONObject getCurrencyPattern(JSONArray options) throws GlobalizationError{
+ JSONObject obj = new JSONObject();
+ try{
+ //get ISO 4217 currency code
+ String code = options.getJSONObject(0).getString(CURRENCYCODE);
+
+ //uses java.text.DecimalFormat to format value
+ DecimalFormat fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
+
+ //set currency format
+ Currency currency = Currency.getInstance(code);
+ fmt.setCurrency(currency);
+
+ //return properties
+ obj.put("pattern", fmt.toPattern());
+ obj.put("code", currency.getCurrencyCode());
+ obj.put("fraction", fmt.getMinimumFractionDigits());
+ obj.put("rounding", new Integer(0));
+ obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
+ obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
+
+ return obj;
+ }catch(Exception ge){
+ throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
+ }
+ }
+
+ /*
+ * @Description: Parses a JSONArray from user options and returns the correct Instance of Decimal/Percent/Currency.
+ * @Return: DecimalFormat : The Instance to use.
+ *
+ * @throws: JSONException
+ */
+ private DecimalFormat getNumberFormatInstance(JSONArray options) throws JSONException{
+ DecimalFormat fmt = (DecimalFormat)DecimalFormat.getInstance(Locale.getDefault()); //default format
+ try{
+ if (options.getJSONObject(0).length() > 1){
+ //options were included
+ if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
+ String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
+ if (fmtOpt.equalsIgnoreCase(CURRENCY)){
+ fmt = (DecimalFormat)DecimalFormat.getCurrencyInstance(Locale.getDefault());
+ }else if(fmtOpt.equalsIgnoreCase(PERCENT)){
+ fmt = (DecimalFormat)DecimalFormat.getPercentInstance(Locale.getDefault());
+ }
+ }
+ }
+
+ }catch (JSONException je){}
+ return fmt;
+ }
+}
diff --git a/framework/src/org/apache/cordova/GlobalizationError.java b/framework/src/org/apache/cordova/GlobalizationError.java
new file mode 100644
index 00000000..8a171d42
--- /dev/null
+++ b/framework/src/org/apache/cordova/GlobalizationError.java
@@ -0,0 +1,108 @@
+/*
+ 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;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * @description Exception class representing defined Globalization error codes
+ * @Globalization error codes:
+ * GlobalizationError.UNKNOWN_ERROR = 0;
+ * GlobalizationError.FORMATTING_ERROR = 1;
+ * GlobalizationError.PARSING_ERROR = 2;
+ * GlobalizationError.PATTERN_ERROR = 3;
+ */
+public class GlobalizationError extends Exception{
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+ public static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";
+ public static final String FORMATTING_ERROR = "FORMATTING_ERROR";
+ public static final String PARSING_ERROR = "PARSING_ERROR";
+ public static final String PATTERN_ERROR = "PATTERN_ERROR";
+
+ int error = 0; //default unknown error thrown
+ /**
+ * Default constructor
+ */
+ public GlobalizationError() {}
+ /**
+ * Create an exception returning an error code
+ *
+ * @param s
+ */
+ public GlobalizationError(String s) {
+ if (s.equalsIgnoreCase(FORMATTING_ERROR)){
+ error = 1;
+ }else if (s.equalsIgnoreCase(PARSING_ERROR)){
+ error = 2;
+ }else if (s.equalsIgnoreCase(PATTERN_ERROR)){
+ error = 3;
+ }
+ }
+ /**
+ * get error string based on error code
+ *
+ * @param String msg
+ */
+ public String getErrorString(){
+ String msg = "";
+ switch (error){
+ case 0:
+ msg = UNKNOWN_ERROR;
+ break;
+ case 1:
+ msg = FORMATTING_ERROR;
+ break;
+ case 2:
+ msg = PARSING_ERROR;
+ break;
+ case 3:
+ msg = PATTERN_ERROR;
+ break;
+ }
+ return msg;
+ }
+ /**
+ * get error code
+ *
+ * @param String msg
+ */
+ public int getErrorCode(){
+ return error;
+ }
+
+ /**
+ * get the json version of this object to return to javascript
+ * @return
+ */
+ public JSONObject toJson() {
+ JSONObject obj = new JSONObject();
+ try {
+ obj.put("code", getErrorCode());
+ obj.put("message", getErrorString());
+ } catch (JSONException e) {
+ // never happens
+ }
+ return obj;
+ }
+}
diff --git a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
index d2732c46..e1291990 100755
--- a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
+++ b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java
@@ -36,7 +36,7 @@ public class NativeToJsMessageQueue {
private static final String LOG_TAG = "JsMessageQueue";
// This must match the default value in incubator-cordova-js/lib/android/exec.js
- private static final int DEFAULT_BRIDGE_MODE = 1;
+ private static final int DEFAULT_BRIDGE_MODE = 3;
// Set this to true to force plugin results to be encoding as
// JS instead of the custom format (useful for benchmarking).
@@ -80,12 +80,11 @@ public class NativeToJsMessageQueue {
public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
this.cordova = cordova;
this.webView = webView;
- registeredListeners = new BridgeMode[5];
+ registeredListeners = new BridgeMode[4];
registeredListeners[0] = null; // Polling. Requires no logic.
- registeredListeners[1] = new CallbackBridgeMode();
- registeredListeners[2] = new LoadUrlBridgeMode();
- registeredListeners[3] = new OnlineEventsBridgeMode();
- registeredListeners[4] = new PrivateApiBridgeMode();
+ registeredListeners[1] = new LoadUrlBridgeMode();
+ registeredListeners[2] = new OnlineEventsBridgeMode();
+ registeredListeners[3] = new PrivateApiBridgeMode();
reset();
}
@@ -270,15 +269,6 @@ public class NativeToJsMessageQueue {
void onNativeToJsMessageAvailable();
}
- /** Uses a local server to send messages to JS via an XHR */
- private class CallbackBridgeMode implements BridgeMode {
- public void onNativeToJsMessageAvailable() {
- if (webView.callbackServer != null) {
- webView.callbackServer.onNativeToJsMessageAvailable(NativeToJsMessageQueue.this);
- }
- }
- }
-
/** Uses webView.loadUrl("javascript:") to execute messages. */
private class LoadUrlBridgeMode implements BridgeMode {
final Runnable runnable = new Runnable() {
diff --git a/framework/src/org/apache/cordova/NetworkManager.java b/framework/src/org/apache/cordova/NetworkManager.java
index 37f0933e..8c8c4002 100755
--- a/framework/src/org/apache/cordova/NetworkManager.java
+++ b/framework/src/org/apache/cordova/NetworkManager.java
@@ -69,6 +69,7 @@ public class NetworkManager extends Plugin {
private static final String LOG_TAG = "NetworkManager";
private String connectionCallbackId;
+ private boolean registered = false;
ConnectivityManager sockMan;
BroadcastReceiver receiver;
@@ -99,10 +100,13 @@ public class NetworkManager extends Plugin {
@SuppressWarnings("deprecation")
@Override
public void onReceive(Context context, Intent intent) {
- updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
+ // (The null check is for the ARM Emulator, please use Intel Emulator for better results)
+ if(webView != null)
+ updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
}
};
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
+ this.registered = true;
}
}
@@ -144,15 +148,23 @@ public class NetworkManager extends Plugin {
* Stop network receiver.
*/
public void onDestroy() {
- if (this.receiver != null) {
+ if (this.receiver != null && this.registered) {
try {
this.cordova.getActivity().unregisterReceiver(this.receiver);
+ this.registered = false;
} catch (Exception e) {
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
}
}
}
+ /**
+ * Stop the network receiver on navigation.
+ */
+ public void onReset() {
+ this.onDestroy();
+ }
+
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
@@ -198,10 +210,9 @@ public class NetworkManager extends Plugin {
result.setKeepCallback(true);
this.success(result, this.connectionCallbackId);
- // Send to all plugins
webView.postMessage("networkconnection", type);
}
-
+
/**
* Determine the type of connection
*
diff --git a/framework/src/org/apache/cordova/Storage.java b/framework/src/org/apache/cordova/Storage.java
index 551e9159..ea385442 100755
--- a/framework/src/org/apache/cordova/Storage.java
+++ b/framework/src/org/apache/cordova/Storage.java
@@ -115,6 +115,13 @@ public class Storage extends Plugin {
}
}
+ /**
+ * Clean up on navigation/refresh.
+ */
+ public void onReset() {
+ this.onDestroy();
+ }
+
// --------------------------------------------------------------------------
// LOCAL METHODS
// --------------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/TempListener.java b/framework/src/org/apache/cordova/TempListener.java
index c65bc879..5b594045 100755
--- a/framework/src/org/apache/cordova/TempListener.java
+++ b/framework/src/org/apache/cordova/TempListener.java
@@ -82,6 +82,13 @@ public class TempListener extends Plugin implements SensorEventListener {
this.stop();
}
+ /**
+ * Called on navigation.
+ */
+ public void onReset() {
+ this.stop();
+ }
+
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/api/CordovaInterface.java b/framework/src/org/apache/cordova/api/CordovaInterface.java
index 93b31a02..5a052c41 100755
--- a/framework/src/org/apache/cordova/api/CordovaInterface.java
+++ b/framework/src/org/apache/cordova/api/CordovaInterface.java
@@ -22,6 +22,8 @@ import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import java.util.concurrent.ExecutorService;
+
/**
* The Cordova activity abstract class that is extended by DroidGap.
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
@@ -67,5 +69,9 @@ public interface CordovaInterface {
* @return Object or null
*/
public Object onMessage(String id, Object data);
-
+
+ /**
+ * Returns a shared thread pool that can be used for background tasks.
+ */
+ public ExecutorService getThreadPool();
}
diff --git a/framework/src/org/apache/cordova/api/IPlugin.java b/framework/src/org/apache/cordova/api/IPlugin.java
index 870bb9e1..a33a663f 100755
--- a/framework/src/org/apache/cordova/api/IPlugin.java
+++ b/framework/src/org/apache/cordova/api/IPlugin.java
@@ -116,4 +116,11 @@ public interface IPlugin {
* @return Return true to prevent the URL from loading. Default is false.
*/
boolean onOverrideUrlLoading(String url);
+
+ /**
+ * Called when the WebView does a top-level navigation or refreshes.
+ *
+ * Plugins should stop any long-running processes and clean up internal state.
+ */
+ void onReset();
}
diff --git a/framework/src/org/apache/cordova/api/LegacyContext.java b/framework/src/org/apache/cordova/api/LegacyContext.java
index 073ba941..0d222813 100644
--- a/framework/src/org/apache/cordova/api/LegacyContext.java
+++ b/framework/src/org/apache/cordova/api/LegacyContext.java
@@ -29,6 +29,8 @@ import android.content.res.AssetManager;
import android.content.res.Resources;
import android.util.Log;
+import java.util.concurrent.ExecutorService;
+
@Deprecated
public class LegacyContext implements CordovaInterface {
private static final String LOG_TAG = "Deprecation Notice";
@@ -145,4 +147,10 @@ public class LegacyContext implements CordovaInterface {
Log.i(LOG_TAG, "Replace ctx.unbindService() with cordova.getActivity().unbindService()");
this.cordova.getActivity().unbindService(conn);
}
+
+ @Override
+ public ExecutorService getThreadPool() {
+ Log.i(LOG_TAG, "Replace ctx.getThreadPool() with cordova.getThreadPool()");
+ return this.cordova.getThreadPool();
+ }
}
diff --git a/framework/src/org/apache/cordova/api/Plugin.java b/framework/src/org/apache/cordova/api/Plugin.java
index 84b67e00..c27b1e54 100755
--- a/framework/src/org/apache/cordova/api/Plugin.java
+++ b/framework/src/org/apache/cordova/api/Plugin.java
@@ -23,6 +23,8 @@ import org.json.JSONArray;
import org.json.JSONObject;
import android.content.Intent;
+import android.util.Log;
+
/**
* Plugin interface must be implemented by any plugin classes.
*
@@ -215,4 +217,14 @@ public abstract class Plugin implements IPlugin {
public void error(String message, String callbackId) {
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
}
+
+ /**
+ * Called when the WebView does a top-level navigation or refreshes.
+ *
+ * Plugins should stop any long-running processes and clean up internal state.
+ *
+ * Does nothing by default.
+ */
+ public void onReset() {
+ }
}
diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java
index aef63023..e51739b0 100755
--- a/framework/src/org/apache/cordova/api/PluginManager.java
+++ b/framework/src/org/apache/cordova/api/PluginManager.java
@@ -47,7 +47,6 @@ public class PluginManager {
private final CordovaInterface ctx;
private final CordovaWebView app;
- private final ExecutorService execThreadPool = Executors.newCachedThreadPool();
// Flag to track first time through
private boolean firstRun;
@@ -210,23 +209,19 @@ public class PluginManager {
* 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 Cordova.callbackSuccess(...) or
- * Cordova.callbackError(...) is called once the plugin code has executed.
* @return Whether the task completed synchronously.
*/
- public boolean exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
+ public boolean exec(final String service, final String action, final String callbackId, final String jsonArgs) {
PluginResult cr = null;
- boolean runAsync = async;
+ final IPlugin plugin = this.getPlugin(service);
+ boolean runAsync = !plugin.isSynch(action);
try {
final JSONArray args = new JSONArray(jsonArgs);
- final IPlugin plugin = this.getPlugin(service);
//final CordovaInterface ctx = this.ctx;
if (plugin != null) {
- runAsync = async && !plugin.isSynch(action);
if (runAsync) {
// Run this on a different thread so that this one can return back to JS
- execThreadPool.execute(new Runnable() {
+ ctx.getThreadPool().execute(new Runnable() {
public void run() {
try {
// Call execute on the plugin so that it can do it's thing
@@ -267,6 +262,11 @@ public class PluginManager {
return true;
}
+ @Deprecated
+ public boolean exec(String service, String action, String callbackId, String jsonArgs, boolean async) {
+ return exec(service, action, callbackId, jsonArgs);
+ }
+
/**
* Get the plugin object that implements the service.
* If the plugin object does not already exist, then create it.
@@ -397,6 +397,20 @@ public class PluginManager {
return false;
}
+ /**
+ * Called when the app navigates or refreshes.
+ */
+ public void onReset() {
+ Iterator it = this.entries.values().iterator();
+ while (it.hasNext()) {
+ IPlugin plugin = it.next().plugin;
+ if (plugin != null) {
+ plugin.onReset();
+ }
+ }
+ }
+
+
private void pluginConfigurationMissing() {
LOG.e(TAG, "=====================================================================================");
LOG.e(TAG, "ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");