This commit is contained in:
Anis Kadri 2012-09-18 14:45:20 -07:00
commit f71e664952
28 changed files with 1106 additions and 486 deletions

View File

@ -33,7 +33,7 @@ To create your cordova.jar, copy the commons codec:
then run in the framework directory:
android update project -p . -t android-15
android update project -p . -t android-16
ant jar
@ -61,7 +61,7 @@ Project Commands
These commands live in a generated Cordova Android project.
./cordovap/debug [path] ..................... install to first device
./cordova/debug [path] ..................... install to first device
./cordova/emulate .......................... start avd (emulator) named default
./cordova/log .............................. starts logcat

View File

@ -66,7 +66,7 @@ function createAppInfoJar {
}
function on_error {
echo "An error occured. Deleting project..."
echo "An error occurred. Deleting project..."
[ -d "$PROJECT_PATH" ] && rm -rf "$PROJECT_PATH"
}

View File

@ -73,7 +73,7 @@ function emulate {
function clean {
ant clean
}
# has to be used independently and not in conjuction with other commands
# has to be used independently and not in conjunction with other commands
function log {
adb logcat
}

View File

@ -47,8 +47,10 @@
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:hardwareAccelerated="true"
android:debuggable="true">
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
android:theme="@android:style/Theme.Black.NoTitleBar"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -31,7 +31,7 @@ var app = {
// deviceready Event Handler
//
// The scope of 'this' is the event. In order to call the 'receivedEvent'
// function, we must explicity call 'app.receivedEvent(...);'
// function, we must explicitly call 'app.receivedEvent(...);'
onDeviceReady: function() {
app.receivedEvent('deviceready');
},

View File

@ -75,7 +75,7 @@ create_project.on('exit', function(code) {
// make sure main Activity was added
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
assert(exists, 'Activity did not get created');
// TODO check that package name and activity name were substitued properly
// TODO check that package name and activity name were substituted properly
});
// make sure plugins.xml was added

View File

@ -85,7 +85,7 @@ create_project.on('exit', function(code) {
// make sure main Activity was added
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
assert(exists, 'Activity did not get created');
// TODO check that package name and activity name were substitued properly
// TODO check that package name and activity name were substituted properly
});
// make sure plugins.xml was added

File diff suppressed because it is too large Load Diff

View File

@ -235,7 +235,7 @@ public class AudioHandler extends Plugin {
/**
* Seek to a location.
* @param id The id of the audio player
* @param miliseconds int: number of milliseconds to skip 1000 = 1 second
* @param milliseconds int: number of milliseconds to skip 1000 = 1 second
*/
public void seekToAudio(String id, int milliseconds) {
AudioPlayer audio = this.players.get(id);

View File

@ -410,7 +410,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
*/
private void setMode(MODE mode) {
if (this.mode != mode) {
//mode is not part of the expected behaviour, so no notification
//mode is not part of the expected behavior, so no notification
//this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + mode + ");");
}
this.mode = mode;

View File

@ -207,19 +207,18 @@ public class CallbackServer implements Runnable {
// Must have security token
if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
//Log.d(LOG_TAG, "CallbackServer -- Processing GET request");
String js = null;
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
synchronized (this) {
while (this.active) {
if (jsMessageQueue != null) {
// TODO(agrieve): Should this use popAll() instead?
js = jsMessageQueue.pop();
if (js != null) {
break;
}
}
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 <<<");
@ -233,14 +232,14 @@ public class CallbackServer implements Runnable {
if (this.active) {
// If no data, then send 404 back to client before it times out
if (js == null) {
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(js, "UTF-8");
response += encode(payload, "UTF-8");
}
}
else {

View File

@ -143,7 +143,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
this.saveToPhotoAlbum = args.getBoolean(9);
// If the user specifies a 0 or smaller width/height
// make it -1 so later comparrisions succeed
// make it -1 so later comparisons succeed
if (this.targetWidth < 1) {
this.targetWidth = -1;
}
@ -392,7 +392,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
// If we don't have a valid image so quit.
if (imagePath == null) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
this.failPicture("Unable to retreive path to picture!");
this.failPicture("Unable to retrieve path to picture!");
return;
}
Bitmap bitmap = getScaledBitmap(imagePath);
@ -762,7 +762,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
try{
this.conn.scanFile(this.scanMe.toString(), "image/*");
} catch (java.lang.IllegalStateException e){
LOG.e(LOG_TAG, "Can't scan file in MediaScanner aftering taking picture");
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
}
}

View File

@ -28,7 +28,6 @@ import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Debug;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.util.Log;
@ -331,8 +330,6 @@ public class ContactAccessorSdk5 extends ContactAccessor {
JSONArray websites = new JSONArray();
JSONArray photos = new JSONArray();
ArrayList<String> names = new ArrayList<String>();
// Column indices
int colContactId = c.getColumnIndex(ContactsContract.Data.CONTACT_ID);
int colRawContactId = c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID);
@ -795,10 +792,10 @@ public class ContactAccessorSdk5 extends ContactAccessor {
formatted.append(middleName + " ");
}
if (familyName != null) {
formatted.append(familyName + " ");
formatted.append(familyName);
}
if (honorificSuffix != null) {
formatted.append(honorificSuffix + " ");
formatted.append(" " + honorificSuffix);
}
contactName.put("familyName", familyName);
@ -862,7 +859,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
im.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im._ID)));
im.put("pref", false); // Android does not store pref attribute
im.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
im.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.TYPE))));
String type = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.PROTOCOL));
im.put("type", getImType(new Integer(type).intValue()));
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
@ -1248,7 +1246,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")));
contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getImType(getJsonString(im, "type")));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
@ -1412,7 +1410,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
retVal = false;
}
// if the save was a succes return the contact ID
// if the save was a success return the contact ID
if (retVal) {
return id;
} else {
@ -1447,7 +1445,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"))
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")))
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, getImType(getJsonString(im, "type")))
.build());
}
@ -2091,5 +2089,86 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
return stringType;
}
/**
* Converts a string from the W3C Contact API to it's Android int value.
* @param string
* @return Android int value
*/
private int getImType(String string) {
int type = ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM;
if (string != null) {
if ("aim".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM;
}
else if ("google talk".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK;
}
else if ("icq".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ;
}
else if ("jabber".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER;
}
else if ("msn".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN;
}
else if ("netmeeting".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_NETMEETING;
}
else if ("qq".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ;
}
else if ("skype".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE;
}
else if ("yahoo".equals(string.toLowerCase())) {
return ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO;
}
}
return type;
}
/**
* getPhoneType converts an Android phone type into a string
* @param type
* @return phone type as string.
*/
private String getImType(int type) {
String stringType;
switch (type) {
case ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM:
stringType = "AIM";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK:
stringType = "Google Talk";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ:
stringType = "ICQ";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER:
stringType = "Jabber";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN:
stringType = "MSN";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_NETMEETING:
stringType = "NetMeeting";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ:
stringType = "QQ";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE:
stringType = "Skype";
break;
case ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO:
stringType = "Yahoo";
break;
default:
stringType = "custom";
break;
}
return stringType;
}
}

View File

@ -202,9 +202,8 @@ public class CordovaChromeClient extends WebChromeClient {
String service = array.getString(0);
String action = array.getString(1);
String callbackId = array.getString(2);
boolean async = array.getBoolean(3);
PluginResult r = this.appView.pluginManager.exec(service, action, callbackId, message, async);
result.confirm(r == null ? "" : r.getJSONString());
String r = this.appView.exposedJsApi.exec(service, action, callbackId, message);
result.confirm(r == null ? "" : r);
} catch (JSONException e) {
e.printStackTrace();
}
@ -212,15 +211,14 @@ public class CordovaChromeClient extends WebChromeClient {
// Sets the native->JS bridge mode.
else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) {
this.appView.jsMessageQueue.setBridgeMode(Integer.parseInt(message));
this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
result.confirm("");
}
// Polling for JavaScript messages
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
// TODO(agrieve): Use popAll() here.
String r = this.appView.jsMessageQueue.pop();
result.confirm(r);
String r = this.appView.exposedJsApi.retrieveJsMessages();
result.confirm(r == null ? "" : r);
}
// Do NO-OP so older code doesn't display dialog

View File

@ -55,6 +55,11 @@ public class CordovaLocationListener implements LocationListener {
{
this.owner.fail(code, message, callbackId);
}
if(this.owner.isGlobalListener(this))
{
Log.d(TAG, "Stopping global listener");
this.stop();
}
this.callbacks.clear();
Iterator it = this.watches.entrySet().iterator();
@ -69,6 +74,11 @@ public class CordovaLocationListener implements LocationListener {
{
this.owner.win(loc, callbackId);
}
if(this.owner.isGlobalListener(this))
{
Log.d(TAG, "Stopping global listener");
this.stop();
}
this.callbacks.clear();
Iterator it = this.watches.entrySet().iterator();

View File

@ -36,9 +36,13 @@ import org.xmlpull.v1.XmlPullParserException;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.XmlResourceParser;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -63,15 +67,17 @@ public class CordovaWebView extends WebView {
public PluginManager pluginManager;
public CallbackServer callbackServer;
private boolean paused;
private BroadcastReceiver receiver;
/** Actvities and other important classes **/
/** Activities and other important classes **/
private CordovaInterface cordova;
CordovaWebViewClient viewClient;
@SuppressWarnings("unused")
private CordovaChromeClient chromeClient;
//This is for the polyfil history
//This is for the polyfill history
private String url;
String baseUrl;
private Stack<String> urls = new Stack<String>();
@ -90,6 +96,7 @@ public class CordovaWebView extends WebView {
private boolean handleButton = false;
NativeToJsMessageQueue jsMessageQueue;
ExposedJsApi exposedJsApi;
/**
* Constructor.
@ -199,8 +206,6 @@ public class CordovaWebView extends WebView {
@SuppressWarnings("deprecation")
@SuppressLint("NewApi")
private void setup() {
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
this.setInitialScale(0);
this.setVerticalScrollBarEnabled(false);
this.requestFocusFromTouch();
@ -229,16 +234,33 @@ public class CordovaWebView extends WebView {
// Enable built-in geolocation
settings.setGeolocationEnabled(true);
//Start up the plugin manager
try {
this.pluginManager = new PluginManager(this, this.cordova);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
// Fix for CB-1405
// Google issue 4641
this.updateUserAgentString();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
if (this.receiver == null) {
this.receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateUserAgentString();
}
};
this.cordova.getActivity().registerReceiver(this.receiver, intentFilter);
}
// end CB-1405
pluginManager = new PluginManager(this, this.cordova);
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue);
exposeJsInterface();
}
private void updateUserAgentString() {
this.getSettings().getUserAgentString();
}
private void exposeJsInterface() {
// addJavascriptInterface crashes on the 2.3 emulator.
@ -246,13 +268,7 @@ public class CordovaWebView extends WebView {
Log.i(TAG, "Disabled addJavascriptInterface() bridge callback due to a bug on the 2.3 emulator");
return;
}
this.addJavascriptInterface(new Object() {
@SuppressWarnings("unused")
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
PluginResult r = pluginManager.exec(service, action, callbackId, arguments, true /* async */);
return r == null ? "" : r.getJSONString();
}
}, "_cordovaExec");
this.addJavascriptInterface(exposedJsApi, "_cordovaNative");
}
/**
@ -461,7 +477,9 @@ public class CordovaWebView extends WebView {
* @param url
*/
void loadUrlNow(String url) {
LOG.d(TAG, ">>> loadUrlNow()");
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
LOG.d(TAG, ">>> loadUrlNow()");
}
super.loadUrl(url);
}
@ -499,7 +517,17 @@ public class CordovaWebView extends WebView {
* @param message
*/
public void sendJavascript(String statement) {
this.jsMessageQueue.add(statement);
this.jsMessageQueue.addJavaScript(statement);
}
/**
* Send a plugin result back to JavaScript.
* (This is a convenience method)
*
* @param message
*/
public void sendPluginResult(PluginResult result, String callbackId) {
this.jsMessageQueue.addPluginResult(result, callbackId);
}
/**
@ -731,7 +759,7 @@ public class CordovaWebView extends WebView {
if(keyDownCodes.contains(keyCode))
{
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
// only override default behaviour is event bound
// only override default behavior is event bound
LOG.d(TAG, "Down Key Hit");
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
return true;
@ -766,7 +794,7 @@ public class CordovaWebView extends WebView {
if (this.backHistory()) {
return true;
}
// If not, then invoke default behaviour
// If not, then invoke default behavior
else {
//this.activityState = ACTIVITY_EXITING;
return false;
@ -789,7 +817,7 @@ public class CordovaWebView extends WebView {
return super.onKeyUp(keyCode, event);
}
//Does webkit change this behaviour?
//Does webkit change this behavior?
return super.onKeyUp(keyCode, event);
}
@ -873,6 +901,15 @@ public class CordovaWebView extends WebView {
if (this.pluginManager != null) {
this.pluginManager.onDestroy();
}
// unregister the receiver
if (this.receiver != null) {
try {
this.cordova.getActivity().unregisterReceiver(this.receiver);
} catch (Exception e) {
Log.e(TAG, "Error unregistering configuration receiver: " + e.getMessage(), e);
}
}
}
public void onNewIntent(Intent intent)

View File

@ -54,9 +54,6 @@ import android.webkit.WebViewClient;
public class CordovaWebViewClient extends WebViewClient {
private static final String TAG = "Cordova";
// Disable URL-based exec() bridge by default since it's a bit of a
// security concern.
private static boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
CordovaInterface cordova;
CordovaWebView appView;
@ -110,11 +107,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);
PluginResult r = appView.pluginManager.exec(service, action, callbackId, jsonArgs, true /* async */);
String callbackString = r.toCallbackString(callbackId);
if (r != null) {
appView.sendJavascript(callbackString);
}
appView.pluginManager.exec(service, action, callbackId, jsonArgs, true /* async */);
}
/**
@ -128,7 +121,7 @@ public class CordovaWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Check if it's an exec() bridge command message.
if (ENABLE_LOCATION_CHANGE_EXEC_MODE && url.startsWith(CORDOVA_EXEC_URL_PREFIX)) {
if (NativeToJsMessageQueue.ENABLE_LOCATION_CHANGE_EXEC_MODE && url.startsWith(CORDOVA_EXEC_URL_PREFIX)) {
handleExecUrl(url);
}

View File

@ -406,6 +406,8 @@ public class DroidGap extends Activity implements CordovaInterface {
}
this.splashscreenTime = time;
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
this.showSplashScreen(this.splashscreenTime);
this.appView.loadUrl(url, time);
}
@ -716,7 +718,7 @@ public class DroidGap extends Activity implements CordovaInterface {
*/
public void sendJavascript(String statement) {
if (this.appView != null) {
this.appView.jsMessageQueue.add(statement);
this.appView.jsMessageQueue.addJavaScript(statement);
}
}
@ -996,7 +998,6 @@ public class DroidGap extends Activity implements CordovaInterface {
this.runOnUiThread(runnable);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
{
@ -1024,8 +1025,11 @@ public class DroidGap extends Activity implements CordovaInterface {
this.removeSplashScreen();
}
else {
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
this.showSplashScreen(this.splashscreenTime);
// If the splash dialog is showing don't try to show it again
if (this.splashDialog != null && !this.splashDialog.isShowing()) {
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
this.showSplashScreen(this.splashscreenTime);
}
}
}
else if ("spinner".equals(id)) {

View File

@ -23,7 +23,7 @@ import java.io.IOException;
import android.media.ExifInterface;
public class ExifHelper {
private String aperature = null;
private String aperture = null;
private String datetime = null;
private String exposureTime = null;
private String flash = null;
@ -70,7 +70,7 @@ public class ExifHelper {
* Reads all the EXIF data from the input file.
*/
public void readExifData() {
this.aperature = inFile.getAttribute(ExifInterface.TAG_APERTURE);
this.aperture = inFile.getAttribute(ExifInterface.TAG_APERTURE);
this.datetime = inFile.getAttribute(ExifInterface.TAG_DATETIME);
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
@ -102,8 +102,8 @@ public class ExifHelper {
return;
}
if (this.aperature != null) {
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperature);
if (this.aperture != null) {
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperture);
}
if (this.datetime != null) {
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime);

View File

@ -0,0 +1,61 @@
/*
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.apache.cordova.api.PluginManager;
import org.apache.cordova.api.PluginResult;
import org.json.JSONException;
/**
* Contains APIs that the JS can call. All functions in here should also have
* an equivalent entry in CordovaChromeClient.java, and be added to
* cordova-js/lib/android/plugin/android/promptbasednativeapi.js
*/
/* package */ class ExposedJsApi {
private PluginManager pluginManager;
private NativeToJsMessageQueue jsMessageQueue;
public ExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
this.pluginManager = pluginManager;
this.jsMessageQueue = jsMessageQueue;
}
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 */);
String ret = "";
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
ret = jsMessageQueue.popAndEncode();
}
return ret;
} finally {
jsMessageQueue.setPaused(false);
}
}
public void setNativeToJsBridgeMode(int value) {
jsMessageQueue.setBridgeMode(value);
}
public String retrieveJsMessages() {
return jsMessageQueue.popAndEncode();
}
}

View File

@ -85,7 +85,7 @@ public class FileTransfer extends Plugin {
if (action.equals("upload")) {
return upload(URLDecoder.decode(source), target, args);
} else if (action.equals("download")) {
return download(source, target);
return download(source, target, args.optBoolean(2));
} else {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
@ -287,7 +287,7 @@ public class FileTransfer extends Plugin {
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
// send multipart form data necessary after file data...
dos.writeBytes(tailParams);
// close streams
@ -459,7 +459,7 @@ 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) {
private PluginResult download(String source, String target, boolean trustEveryone) {
Log.d(LOG_TAG, "download " + source + " to " + target);
HttpURLConnection connection = null;
@ -473,7 +473,30 @@ public class FileTransfer extends Plugin {
if (webView.isUrlWhiteListed(source))
{
URL url = new URL(source);
connection = (HttpURLConnection) url.openConnection();
boolean useHttps = url.getProtocol().toLowerCase().equals("https");
// Open a HTTP connection to the URL based on protocol
if (useHttps) {
// Using standard HTTPS connection. Will not allow self signed certificate
if (!trustEveryone) {
connection = (HttpsURLConnection) url.openConnection();
}
// Use our HTTPS connection that blindly trusts everyone.
// This should only be used in debug environments
else {
// Setup the HTTPS connection class to trust everyone
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
// Save the current hostnameVerifier
defaultHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
connection = https;
}
}
// Return a standard HTTP connection
else {
connection = (HttpURLConnection) url.openConnection();
}
connection.setRequestMethod("GET");
//Add cookie support
@ -516,6 +539,12 @@ public class FileTransfer extends Plugin {
FileUtils fileUtil = new FileUtils();
JSONObject fileEntry = fileUtil.getEntry(file);
// Revert back to the proper verifier and socket factories
if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
((HttpsURLConnection) connection).setHostnameVerifier(defaultHostnameVerifier);
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
}
return new PluginResult(PluginResult.Status.OK, fileEntry);
}
else

View File

@ -409,19 +409,19 @@ public class FileUtils extends Plugin {
throw new InvalidModificationException("Can't rename a file to a directory");
}
FileChannel input = new FileInputStream(srcFile).getChannel();
FileChannel output = new FileOutputStream(destFile).getChannel();
FileInputStream istream = new FileInputStream(srcFile);
FileOutputStream ostream = new FileOutputStream(destFile);
FileChannel input = istream.getChannel();
FileChannel output = ostream.getChannel();
input.transferTo(0, input.size(), output);
input.close();
output.close();
/*
if (srcFile.length() != destFile.length()) {
return false;
try {
input.transferTo(0, input.size(), output);
} finally {
istream.close();
ostream.close();
input.close();
output.close();
}
*/
return getEntry(destFile);
}
@ -480,7 +480,7 @@ public class FileUtils extends Plugin {
// This weird test is to determine if we are copying or moving a directory into itself.
// Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
// Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
// Copy /sdcard/myDir to /sdcard/myDir/backup should throw an INVALID_MODIFICATION_ERR
if (dest.startsWith(src) && dest.indexOf(File.separator, src.length() - 1) != -1) {
return true;
}
@ -1008,14 +1008,17 @@ public class FileUtils extends Plugin {
filename = stripFileProtocol(filename);
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
if (raf.length() >= size) {
FileChannel channel = raf.getChannel();
channel.truncate(size);
return size;
try {
if (raf.length() >= size) {
FileChannel channel = raf.getChannel();
channel.truncate(size);
return size;
}
return raf.length();
} finally {
raf.close();
}
return raf.length();
}
/**
@ -1040,7 +1043,7 @@ public class FileUtils extends Plugin {
* Queries the media store to find out what the file path is for the Uri we supply
*
* @param contentUri the Uri of the audio/image/video
* @param cordova) the current applicaiton context
* @param cordova the current application context
* @return the full path to the file
*/
@SuppressWarnings("deprecation")

View File

@ -191,4 +191,14 @@ public class GeoBroker extends Plugin {
this.error(result, callbackId);
}
public boolean isGlobalListener(CordovaLocationListener listener)
{
if (gpsListener != null && networkListener != null)
{
return gpsListener.equals(listener) || networkListener.equals(listener);
}
else
return false;
}
}

View File

@ -19,12 +19,11 @@
package org.apache.cordova;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.LinkedList;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.PluginResult;
import android.os.Message;
import android.util.Log;
@ -39,15 +38,36 @@ public class NativeToJsMessageQueue {
// This must match the default value in incubator-cordova-js/lib/android/exec.js
private static final int DEFAULT_BRIDGE_MODE = 1;
// Set this to true to force plugin results to be encoding as
// JS instead of the custom format (useful for benchmarking).
private static final boolean FORCE_ENCODE_USING_EVAL = false;
// Disable URL-based exec() bridge by default since it's a bit of a
// security concern.
static final boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
// Disable sending back native->JS messages during an exec() when the active
// exec() is asynchronous. Set this to true when running bridge benchmarks.
static final boolean DISABLE_EXEC_CHAINING = false;
// Arbitrarily chosen upper limit for how much data to send to JS in one shot.
private static final int MAX_PAYLOAD_SIZE = 50 * 1024;
/**
* The index into registeredListeners to treat as active.
*/
private int activeListenerIndex;
/**
* When true, the active listener is not fired upon enqueue. When set to false,
* the active listener will be fired if the queue is non-empty.
*/
private boolean paused;
/**
* The list of JavaScript statements to be sent to JavaScript.
*/
private final LinkedList<String> queue = new LinkedList<String>();
private final LinkedList<JsMessage> queue = new LinkedList<JsMessage>();
/**
* The array of listeners that can be used to send messages to JS.
@ -56,7 +76,7 @@ public class NativeToJsMessageQueue {
private final CordovaInterface cordova;
private final CordovaWebView webView;
public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
this.cordova = cordova;
this.webView = webView;
@ -81,7 +101,7 @@ public class NativeToJsMessageQueue {
synchronized (this) {
activeListenerIndex = value;
BridgeMode activeListener = registeredListeners[value];
if (!queue.isEmpty() && activeListener != null) {
if (!paused && !queue.isEmpty() && activeListener != null) {
activeListener.onNativeToJsMessageAvailable();
}
}
@ -99,60 +119,151 @@ public class NativeToJsMessageQueue {
}
}
private int calculatePackedMessageLength(JsMessage message) {
int messageLen = message.calculateEncodedLength();
String messageLenStr = String.valueOf(messageLen);
return messageLenStr.length() + messageLen + 1;
}
private void packMessage(JsMessage message, StringBuilder sb) {
sb.append(message.calculateEncodedLength())
.append(' ');
message.encodeAsMessage(sb);
}
/**
* Removes and returns the last statement in the queue.
* Combines and returns queued messages combined into a single string.
* Combines as many messages as possible, while staying under MAX_PAYLOAD_SIZE.
* Returns null if the queue is empty.
*/
public String pop() {
public String popAndEncode() {
synchronized (this) {
if (queue.isEmpty()) {
return null;
}
return queue.remove(0);
int totalPayloadLen = 0;
int numMessagesToSend = 0;
for (JsMessage message : queue) {
int messageSize = calculatePackedMessageLength(message);
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE) {
break;
}
totalPayloadLen += messageSize;
numMessagesToSend += 1;
}
StringBuilder sb = new StringBuilder(totalPayloadLen);
for (int i = 0; i < numMessagesToSend; ++i) {
JsMessage message = queue.removeFirst();
packMessage(message, sb);
}
if (!queue.isEmpty()) {
// Attach a char to indicate that there are more messages pending.
sb.append('*');
}
return sb.toString();
}
}
/**
* Combines and returns all statements. Clears the queue.
* Returns null if the queue is empty.
* Same as popAndEncode(), except encodes in a form that can be executed as JS.
*/
public String popAll() {
private String popAndEncodeAsJs() {
synchronized (this) {
int length = queue.size();
if (length == 0) {
return null;
}
StringBuffer sb = new StringBuffer();
int totalPayloadLen = 0;
int numMessagesToSend = 0;
for (JsMessage message : queue) {
int messageSize = message.calculateEncodedLength() + 50; // overestimate.
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE) {
break;
}
totalPayloadLen += messageSize;
numMessagesToSend += 1;
}
boolean willSendAllMessages = numMessagesToSend == queue.size();
StringBuilder sb = new StringBuilder(totalPayloadLen + (willSendAllMessages ? 0 : 100));
// Wrap each statement in a try/finally so that if one throws it does
// not affect the next.
int i = 0;
for (String message : queue) {
if (++i == length) {
sb.append(message);
for (int i = 0; i < numMessagesToSend; ++i) {
JsMessage message = queue.removeFirst();
if (willSendAllMessages && (i + 1 == numMessagesToSend)) {
message.encodeAsJsMessage(sb);
} else {
sb.append("try{")
.append(message)
.append("}finally{");
sb.append("try{");
message.encodeAsJsMessage(sb);
sb.append("}finally{");
}
}
for ( i = 1; i < length; ++i) {
if (!willSendAllMessages) {
sb.append("window.setTimeout(function(){cordova.require('cordova/plugin/android/polling').pollOnce();},0);");
}
for (int i = willSendAllMessages ? 1 : 0; i < numMessagesToSend; ++i) {
sb.append('}');
}
queue.clear();
return sb.toString();
}
}
}
/**
* Add a JavaScript statement to the list.
*/
public void add(String statement) {
public void addJavaScript(String statement) {
enqueueMessage(new JsMessage(statement));
}
/**
* Add a JavaScript statement to the list.
*/
public void addPluginResult(PluginResult result, String callbackId) {
// Don't send anything if there is no result and there is no need to
// clear the callbacks.
boolean noResult = result.getStatus() == PluginResult.Status.NO_RESULT.ordinal();
boolean keepCallback = result.getKeepCallback();
if (noResult && keepCallback) {
return;
}
JsMessage message = new JsMessage(result, callbackId);
if (FORCE_ENCODE_USING_EVAL) {
StringBuilder sb = new StringBuilder(message.calculateEncodedLength() + 50);
message.encodeAsJsMessage(sb);
message = new JsMessage(sb.toString());
}
enqueueMessage(message);
}
private void enqueueMessage(JsMessage message) {
synchronized (this) {
queue.add(statement);
if (registeredListeners[activeListenerIndex] != null) {
queue.add(message);
if (!paused && registeredListeners[activeListenerIndex] != null) {
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
}
}
}
public void setPaused(boolean value) {
if (paused && value) {
// This should never happen. If a use-case for it comes up, we should
// change pause to be a counter.
Log.e(LOG_TAG, "nested call to setPaused detected.", new Throwable());
}
paused = value;
if (!value) {
synchronized (this) {
if (!queue.isEmpty() && registeredListeners[activeListenerIndex] != null) {
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
}
}
}
}
public boolean getPaused() {
return paused;
}
private interface BridgeMode {
@ -170,8 +281,17 @@ public class NativeToJsMessageQueue {
/** Uses webView.loadUrl("javascript:") to execute messages. */
private class LoadUrlBridgeMode implements BridgeMode {
final Runnable runnable = new Runnable() {
public void run() {
String js = popAndEncodeAsJs();
if (js != null) {
webView.loadUrlNow("javascript:" + js);
}
}
};
public void onNativeToJsMessageAvailable() {
webView.loadUrlNow("javascript:" + popAll());
cordova.getActivity().runOnUiThread(runnable);
}
}
@ -187,7 +307,9 @@ public class NativeToJsMessageQueue {
}
}
};
OnlineEventsBridgeMode() {
webView.setNetworkAvailable(true);
}
public void onNativeToJsMessageAvailable() {
cordova.getActivity().runOnUiThread(runnable);
}
@ -241,7 +363,7 @@ public class NativeToJsMessageQueue {
}
// webViewCore is lazily initialized, and so may not be available right away.
if (sendMessageMethod != null) {
String js = popAll();
String js = popAndEncodeAsJs();
Message execJsMessage = Message.obtain(null, EXECUTE_JS, js);
try {
sendMessageMethod.invoke(webViewCore, execJsMessage);
@ -251,4 +373,94 @@ public class NativeToJsMessageQueue {
}
}
}
private static class JsMessage {
final String jsPayloadOrCallbackId;
final PluginResult pluginResult;
JsMessage(String js) {
jsPayloadOrCallbackId = js;
pluginResult = null;
}
JsMessage(PluginResult pluginResult, String callbackId) {
jsPayloadOrCallbackId = callbackId;
this.pluginResult = pluginResult;
}
int calculateEncodedLength() {
if (pluginResult == null) {
return jsPayloadOrCallbackId.length() + 1;
}
int statusLen = String.valueOf(pluginResult.getStatus()).length();
int ret = 2 + statusLen + 1 + jsPayloadOrCallbackId.length() + 1;
switch (pluginResult.getMessageType()) {
case PluginResult.MESSAGE_TYPE_BOOLEAN:
ret += 1;
break;
case PluginResult.MESSAGE_TYPE_NUMBER: // n
ret += 1 + pluginResult.getMessage().length();
break;
case PluginResult.MESSAGE_TYPE_STRING: // s
ret += 1 + pluginResult.getStrMessage().length();
break;
case PluginResult.MESSAGE_TYPE_JSON:
default:
ret += pluginResult.getMessage().length();
}
return ret;
}
void encodeAsMessage(StringBuilder sb) {
if (pluginResult == null) {
sb.append('J')
.append(jsPayloadOrCallbackId);
return;
}
int status = pluginResult.getStatus();
boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
boolean resultOk = status == PluginResult.Status.OK.ordinal();
boolean keepCallback = pluginResult.getKeepCallback();
sb.append((noResult || resultOk) ? 'S' : 'F')
.append(keepCallback ? '1' : '0')
.append(status)
.append(' ')
.append(jsPayloadOrCallbackId)
.append(' ');
switch (pluginResult.getMessageType()) {
case PluginResult.MESSAGE_TYPE_BOOLEAN:
sb.append(pluginResult.getMessage().charAt(0)); // t or f.
break;
case PluginResult.MESSAGE_TYPE_NUMBER: // n
sb.append('n')
.append(pluginResult.getMessage());
break;
case PluginResult.MESSAGE_TYPE_STRING: // s
sb.append('s')
.append(pluginResult.getStrMessage());
break;
case PluginResult.MESSAGE_TYPE_JSON:
default:
sb.append(pluginResult.getMessage()); // [ or {
}
}
void encodeAsJsMessage(StringBuilder sb) {
if (pluginResult == null) {
sb.append(jsPayloadOrCallbackId);
} else {
int status = pluginResult.getStatus();
boolean success = (status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal());
sb.append("cordova.callbackFromNative('")
.append(jsPayloadOrCallbackId)
.append("',")
.append(success)
.append(",")
.append(status)
.append(",")
.append(pluginResult.getMessage())
.append(",")
.append(pluginResult.getKeepCallback())
.append(");");
}
}
}
}

View File

@ -180,7 +180,7 @@ public class Storage extends Plugin {
}
/**
* Checks to see the the query is a Data Definintion command
* Checks to see the the query is a Data Definition command
*
* @param query to be executed
* @return true if it is a DDL command, false otherwise

View File

@ -139,14 +139,19 @@ public abstract class Plugin implements IPlugin {
/**
* Send generic JavaScript statement back to JavaScript.
* success(...) and error(...) should be used instead where possible.
*
* @param statement
* sendPluginResult() should be used instead where possible.
*/
public void sendJavascript(String statement) {
this.webView.sendJavascript(statement);
}
/**
* Send generic JavaScript statement back to JavaScript.
*/
public void sendPluginResult(PluginResult pluginResult, String callbackId) {
this.webView.sendPluginResult(pluginResult, callbackId);
}
/**
* Call the JavaScript success callback for this plugin.
*
@ -158,7 +163,7 @@ public abstract class Plugin implements IPlugin {
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void success(PluginResult pluginResult, String callbackId) {
this.webView.sendJavascript(pluginResult.toSuccessCallbackString(callbackId));
this.webView.sendPluginResult(pluginResult, callbackId);
}
/**
@ -168,7 +173,7 @@ public abstract class Plugin implements IPlugin {
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void success(JSONObject message, String callbackId) {
this.webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
}
/**
@ -178,7 +183,7 @@ public abstract class Plugin implements IPlugin {
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void success(String message, String callbackId) {
this.webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
}
/**
@ -188,7 +193,7 @@ public abstract class Plugin implements IPlugin {
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void error(PluginResult pluginResult, String callbackId) {
this.webView.sendJavascript(pluginResult.toErrorCallbackString(callbackId));
this.webView.sendPluginResult(pluginResult, callbackId);
}
/**
@ -198,7 +203,7 @@ public abstract class Plugin implements IPlugin {
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void error(JSONObject message, String callbackId) {
this.webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
}
/**
@ -208,6 +213,6 @@ public abstract class Plugin implements IPlugin {
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void error(String message, String callbackId) {
this.webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
}
}

View File

@ -22,6 +22,8 @@ import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.cordova.CordovaWebView;
import org.json.JSONArray;
@ -45,6 +47,7 @@ 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;
@ -200,7 +203,7 @@ public class PluginManager {
* or execute the class denoted by the clazz argument.
*
* @param service String containing the service to run
* @param action String containt the action that the class is supposed to perform. This is
* @param action String containing the action that the class is supposed to perform. This is
* passed to the plugin execute method and it is up to the plugin developer
* how to deal with it.
* @param callbackId String containing the id of the callback that is execute in JavaScript if
@ -210,10 +213,9 @@ public class PluginManager {
* @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 PluginResult to send to the page, or null if no response is ready yet.
* @return Whether the task completed synchronously.
*/
public PluginResult 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, final boolean async) {
PluginResult cr = null;
boolean runAsync = async;
try {
@ -224,30 +226,26 @@ public class PluginManager {
runAsync = async && !plugin.isSynch(action);
if (runAsync) {
// Run this on a different thread so that this one can return back to JS
Thread thread = new Thread(new Runnable() {
execThreadPool.execute(new Runnable() {
public void run() {
try {
// Call execute on the plugin so that it can do it's thing
PluginResult cr = plugin.execute(action, args, callbackId);
String callbackString = cr.toCallbackString(callbackId);
if (callbackString != null) {
app.sendJavascript(callbackString);
}
app.sendPluginResult(cr, callbackId);
} catch (Exception e) {
PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
app.sendJavascript(cr.toErrorCallbackString(callbackId));
app.sendPluginResult(cr, callbackId);
}
}
});
thread.start();
return null;
return false;
} else {
// Call execute on the plugin so that it can do it's thing
cr = plugin.execute(action, args, callbackId);
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
return null;
return true;
}
}
}
@ -260,12 +258,13 @@ public class PluginManager {
if (cr == null) {
cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
}
app.sendJavascript(cr.toErrorCallbackString(callbackId));
app.sendPluginResult(cr, callbackId);
}
if (cr == null) {
cr = new PluginResult(PluginResult.Status.NO_RESULT);
}
return cr;
app.sendPluginResult(cr, callbackId);
return true;
}
/**

View File

@ -23,43 +23,49 @@ import org.json.JSONObject;
public class PluginResult {
private final int status;
private final String message;
private final int messageType;
private boolean keepCallback = false;
private String strMessage;
private String encodedMessage;
public PluginResult(Status status) {
this.status = status.ordinal();
this.message = "\"" + PluginResult.StatusMessages[this.status] + "\"";
this(status, PluginResult.StatusMessages[status.ordinal()]);
}
public PluginResult(Status status, String message) {
this.status = status.ordinal();
this.message = JSONObject.quote(message);
this.messageType = MESSAGE_TYPE_STRING;
this.strMessage = message;
}
public PluginResult(Status status, JSONArray message) {
this.status = status.ordinal();
this.message = message.toString();
this.messageType = MESSAGE_TYPE_JSON;
encodedMessage = message.toString();
}
public PluginResult(Status status, JSONObject message) {
this.status = status.ordinal();
this.message = message.toString();
this.messageType = MESSAGE_TYPE_JSON;
encodedMessage = message.toString();
}
public PluginResult(Status status, int i) {
this.status = status.ordinal();
this.message = ""+i;
this.messageType = MESSAGE_TYPE_NUMBER;
this.encodedMessage = ""+i;
}
public PluginResult(Status status, float f) {
this.status = status.ordinal();
this.message = ""+f;
this.messageType = MESSAGE_TYPE_NUMBER;
this.encodedMessage = ""+f;
}
public PluginResult(Status status, boolean b) {
this.status = status.ordinal();
this.message = ""+b;
this.messageType = MESSAGE_TYPE_BOOLEAN;
this.encodedMessage = Boolean.toString(b);
}
public void setKeepCallback(boolean b) {
@ -70,18 +76,35 @@ public class PluginResult {
return status;
}
public int getMessageType() {
return messageType;
}
public String getMessage() {
return message;
if (encodedMessage == null) {
encodedMessage = JSONObject.quote(strMessage);
}
return encodedMessage;
}
/**
* If messageType == MESSAGE_TYPE_STRING, then returns the message string.
* Otherwise, returns null.
*/
public String getStrMessage() {
return strMessage;
}
public boolean getKeepCallback() {
return this.keepCallback;
}
@Deprecated // Use sendPluginResult instead of sendJavascript.
public String getJSONString() {
return "{\"status\":" + this.status + ",\"message\":" + this.message + ",\"keepCallback\":" + this.keepCallback + "}";
return "{\"status\":" + this.status + ",\"message\":" + this.getMessage() + ",\"keepCallback\":" + this.keepCallback + "}";
}
@Deprecated // Use sendPluginResult instead of sendJavascript.
public String toCallbackString(String callbackId) {
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
if ((status == PluginResult.Status.NO_RESULT.ordinal()) && keepCallback) {
@ -95,14 +118,22 @@ public class PluginResult {
return toErrorCallbackString(callbackId);
}
@Deprecated // Use sendPluginResult instead of sendJavascript.
public String toSuccessCallbackString(String callbackId) {
return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");";
}
@Deprecated // Use sendPluginResult instead of sendJavascript.
public String toErrorCallbackString(String callbackId) {
return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");";
}
public static final int MESSAGE_TYPE_STRING = 1;
public static final int MESSAGE_TYPE_JSON = 2;
public static final int MESSAGE_TYPE_NUMBER = 3;
public static final int MESSAGE_TYPE_BOOLEAN = 4;
public static String[] StatusMessages = new String[] {
"No result",
"OK",