From c8fafa6bbd55ee06180d7326632ac681cdc669df Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 21:49:30 -0500 Subject: [PATCH 01/10] Updates. --- .../src/com/phonegap/api/PluginManager.java | 3 +- .../src/org/apache/cordova/AccelListener.java | 330 +- framework/src/org/apache/cordova/App.java | 207 +- .../src/org/apache/cordova/AudioHandler.java | 395 +- .../src/org/apache/cordova/AudioPlayer.java | 722 ++-- .../org/apache/cordova/BatteryListener.java | 49 +- .../org/apache/cordova/CameraLauncher.java | 222 +- framework/src/org/apache/cordova/Capture.java | 114 +- .../org/apache/cordova/CompassListener.java | 43 +- .../org/apache/cordova/ContactAccessor.java | 211 +- .../apache/cordova/ContactAccessorSdk5.java | 3205 ++++++++--------- .../apache/cordova/CordovaChromeClient.java | 190 +- .../org/apache/cordova/CordovaException.java | 1 - .../org/apache/cordova/CordovaWebView.java | 1050 +++--- .../apache/cordova/CordovaWebViewClient.java | 257 +- framework/src/org/apache/cordova/Device.java | 59 +- .../src/org/apache/cordova/DroidGap.java | 1035 +++--- .../src/org/apache/cordova/FileTransfer.java | 132 +- .../src/org/apache/cordova/FileUtils.java | 65 +- .../src/org/apache/cordova/GeoListener.java | 203 +- .../src/org/apache/cordova/GpsListener.java | 244 +- .../src/org/apache/cordova/HttpHandler.java | 105 +- .../LinearLayoutSoftKeyboardDetect.java | 27 +- .../org/apache/cordova/NetworkListener.java | 236 +- .../org/apache/cordova/NetworkManager.java | 51 +- .../src/org/apache/cordova/Notification.java | 615 ++-- .../src/org/apache/cordova/SplashScreen.java | 3 +- .../src/org/apache/cordova/StandAlone.java | 18 +- framework/src/org/apache/cordova/Storage.java | 355 +- .../src/org/apache/cordova/TempListener.java | 124 +- .../apache/cordova/api/CordovaInterface.java | 187 +- .../src/org/apache/cordova/api/IPlugin.java | 71 +- .../src/org/apache/cordova/api/Plugin.java | 111 +- .../org/apache/cordova/api/PluginEntry.java | 12 +- .../org/apache/cordova/api/PluginManager.java | 49 +- .../org/apache/cordova/api/PluginResult.java | 174 +- 36 files changed, 5650 insertions(+), 5225 deletions(-) mode change 100644 => 100755 framework/src/org/apache/cordova/CordovaException.java diff --git a/framework/src/com/phonegap/api/PluginManager.java b/framework/src/com/phonegap/api/PluginManager.java index b0c0f0bb..f3813596 100755 --- a/framework/src/com/phonegap/api/PluginManager.java +++ b/framework/src/com/phonegap/api/PluginManager.java @@ -18,6 +18,7 @@ */ package com.phonegap.api; +import org.apache.cordova.CordovaWebView; import org.apache.cordova.api.CordovaInterface; import android.webkit.WebView; @@ -31,6 +32,6 @@ import android.webkit.WebView; public class PluginManager extends org.apache.cordova.api.PluginManager { public PluginManager(WebView app, CordovaInterface ctx) throws Exception { - super(app, ctx); + super((CordovaWebView) app, ctx); } } diff --git a/framework/src/org/apache/cordova/AccelListener.java b/framework/src/org/apache/cordova/AccelListener.java index 9e62a5be..dbda3fe6 100755 --- a/framework/src/org/apache/cordova/AccelListener.java +++ b/framework/src/org/apache/cordova/AccelListener.java @@ -27,7 +27,6 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -40,14 +39,14 @@ import android.content.Context; */ public class AccelListener extends Plugin implements SensorEventListener { - public static int STOPPED = 0; - public static int STARTING = 1; + public static int STOPPED = 0; + public static int STARTING = 1; public static int RUNNING = 2; public static int ERROR_FAILED_TO_START = 3; - + public float TIMEOUT = 30000; // Timeout in msec to shut off listener - - float x,y,z; // most recent acceleration values + + float x, y, z; // most recent acceleration values long timestamp; // time of most recent value int status; // status of listener long lastAccessTime; // time the value was last retrieved @@ -64,128 +63,129 @@ public class AccelListener extends Plugin implements SensorEventListener { this.z = 0; this.timestamp = 0; this.setStatus(AccelListener.STOPPED); - } - - /** - * Sets the context of the Command. This can then be used to do things like - * get file paths associated with the Activity. - * - * @param ctx The context of the main Activity. - */ - - public void setContext(Context ctx) { - this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE); } - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public PluginResult execute(String action, JSONArray args, String callbackId) { - PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - - try { - if (action.equals("getStatus")) { - int i = this.getStatus(); - return new PluginResult(status, i); - } - else if (action.equals("start")) { - int i = this.start(); - return new PluginResult(status, i); - } - else if (action.equals("stop")) { - this.stop(); - return new PluginResult(status, 0); - } - else if (action.equals("getAcceleration")) { - // If not running, then this is an async call, so don't worry about waiting - if (this.status != AccelListener.RUNNING) { - int r = this.start(); - if (r == AccelListener.ERROR_FAILED_TO_START) { - return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START); - } - // Wait until running - long timeout = 2000; - while ((this.status == STARTING) && (timeout > 0)) { - timeout = timeout - 100; - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - if (timeout == 0) { - return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START); - } - } - this.lastAccessTime = System.currentTimeMillis(); - JSONObject r = new JSONObject(); - r.put("x", this.x); - r.put("y", this.y); - r.put("z", this.z); - // TODO: Should timestamp be sent? - r.put("timestamp", this.timestamp); - return new PluginResult(status, r); - } - else if (action.equals("setTimeout")) { - try { - float timeout = Float.parseFloat(args.getString(0)); - this.setTimeout(timeout); - return new PluginResult(status, 0); - } catch (NumberFormatException e) { - status = PluginResult.Status.INVALID_ACTION; - e.printStackTrace(); - } catch (JSONException e) { - status = PluginResult.Status.JSON_EXCEPTION; - e.printStackTrace(); - } - } - else if (action.equals("getTimeout")) { - float f = this.getTimeout(); - return new PluginResult(status, f); - } else { - // Unsupported action - return new PluginResult(PluginResult.Status.INVALID_ACTION); - } - return new PluginResult(status, result); - } catch (JSONException e) { - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); - } - } + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param ctx The context of the main Activity. + */ + + public void setContext(CordovaInterface ctx) { + super.setContext(ctx); + this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("getStatus")) { + int i = this.getStatus(); + return new PluginResult(status, i); + } + else if (action.equals("start")) { + int i = this.start(); + return new PluginResult(status, i); + } + else if (action.equals("stop")) { + this.stop(); + return new PluginResult(status, 0); + } + else if (action.equals("getAcceleration")) { + // If not running, then this is an async call, so don't worry about waiting + if (this.status != AccelListener.RUNNING) { + int r = this.start(); + if (r == AccelListener.ERROR_FAILED_TO_START) { + return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START); + } + // Wait until running + long timeout = 2000; + while ((this.status == STARTING) && (timeout > 0)) { + timeout = timeout - 100; + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + if (timeout == 0) { + return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.ERROR_FAILED_TO_START); + } + } + this.lastAccessTime = System.currentTimeMillis(); + JSONObject r = new JSONObject(); + r.put("x", this.x); + r.put("y", this.y); + r.put("z", this.z); + // TODO: Should timestamp be sent? + r.put("timestamp", this.timestamp); + return new PluginResult(status, r); + } + else if (action.equals("setTimeout")) { + try { + float timeout = Float.parseFloat(args.getString(0)); + this.setTimeout(timeout); + return new PluginResult(status, 0); + } catch (NumberFormatException e) { + status = PluginResult.Status.INVALID_ACTION; + e.printStackTrace(); + } catch (JSONException e) { + status = PluginResult.Status.JSON_EXCEPTION; + e.printStackTrace(); + } + } + else if (action.equals("getTimeout")) { + float f = this.getTimeout(); + return new PluginResult(status, f); + } else { + // Unsupported action + return new PluginResult(PluginResult.Status.INVALID_ACTION); + } + return new PluginResult(status, result); + } catch (JSONException e) { + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + /** + * Identifies if action to be executed returns a value and should be run synchronously. + * + * @param action The action to execute + * @return T=returns value + */ + public boolean isSynch(String action) { + if (action.equals("getStatus")) { + return true; + } + else if (action.equals("getAcceleration")) { + // Can only return value if RUNNING + if (this.status == AccelListener.RUNNING) { + return true; + } + } + else if (action.equals("getTimeout")) { + return true; + } + return false; + } - /** - * Identifies if action to be executed returns a value and should be run synchronously. - * - * @param action The action to execute - * @return T=returns value - */ - public boolean isSynch(String action) { - if (action.equals("getStatus")) { - return true; - } - else if (action.equals("getAcceleration")) { - // Can only return value if RUNNING - if (this.status == AccelListener.RUNNING) { - return true; - } - } - else if (action.equals("getTimeout")) { - return true; - } - return false; - } - /** * Called by AccelBroker when listener is to be shut down. * Stop listener. */ public void onDestroy() { - this.stop(); + this.stop(); } //-------------------------------------------------------------------------- @@ -199,14 +199,14 @@ public class AccelListener extends Plugin implements SensorEventListener { */ public int start() { - // If already starting or running, then just return + // If already starting or running, then just return if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) { - return this.status; + return this.status; } // Get accelerometer from sensor manager List list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER); - + // If found, then register as listener if ((list != null) && (list.size() > 0)) { this.mSensor = list.get(0); @@ -214,12 +214,12 @@ public class AccelListener extends Plugin implements SensorEventListener { this.setStatus(AccelListener.STARTING); this.lastAccessTime = System.currentTimeMillis(); } - + // If error, then set status to error else { this.setStatus(AccelListener.ERROR_FAILED_TO_START); } - + return this.status; } @@ -228,7 +228,7 @@ public class AccelListener extends Plugin implements SensorEventListener { */ public void stop() { if (this.status != AccelListener.STOPPED) { - this.sensorManager.unregisterListener(this); + this.sensorManager.unregisterListener(this); } this.setStatus(AccelListener.STOPPED); } @@ -248,29 +248,29 @@ public class AccelListener extends Plugin implements SensorEventListener { * @param SensorEvent event */ public void onSensorChanged(SensorEvent event) { - - // Only look at accelerometer events + + // Only look at accelerometer events if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) { return; } - + // If not running, then just return if (this.status == AccelListener.STOPPED) { - return; + return; } - + // Save time that event was received this.timestamp = System.currentTimeMillis(); this.x = event.values[0]; this.y = event.values[1]; - this.z = event.values[2]; + this.z = event.values[2]; this.setStatus(AccelListener.RUNNING); // If values haven't been read for TIMEOUT time, then turn off accelerometer sensor to save power - if ((this.timestamp - this.lastAccessTime) > this.TIMEOUT) { - this.stop(); - } + if ((this.timestamp - this.lastAccessTime) > this.TIMEOUT) { + this.stop(); + } } /** @@ -278,34 +278,34 @@ public class AccelListener extends Plugin implements SensorEventListener { * * @return status */ - public int getStatus() { - return this.status; - } - - /** - * Set the timeout to turn off accelerometer sensor if getX() hasn't been called. - * - * @param timeout Timeout in msec. - */ - public void setTimeout(float timeout) { - this.TIMEOUT = timeout; - } - - /** - * Get the timeout to turn off accelerometer sensor if getX() hasn't been called. - * - * @return timeout in msec - */ - public float getTimeout() { - return this.TIMEOUT; - } - - /** - * Set the status and send it to JavaScript. - * @param status - */ - private void setStatus(int status) { - this.status = status; - } - + public int getStatus() { + return this.status; + } + + /** + * Set the timeout to turn off accelerometer sensor if getX() hasn't been called. + * + * @param timeout Timeout in msec. + */ + public void setTimeout(float timeout) { + this.TIMEOUT = timeout; + } + + /** + * Get the timeout to turn off accelerometer sensor if getX() hasn't been called. + * + * @return timeout in msec + */ + public float getTimeout() { + return this.TIMEOUT; + } + + /** + * Set the status and send it to JavaScript. + * @param status + */ + private void setStatus(int status) { + this.status = status; + } + } diff --git a/framework/src/org/apache/cordova/App.java b/framework/src/org/apache/cordova/App.java index 146140d1..75460d52 100755 --- a/framework/src/org/apache/cordova/App.java +++ b/framework/src/org/apache/cordova/App.java @@ -26,7 +26,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import android.webkit.WebView; +//import android.webkit.WebView; import android.app.Activity; import android.view.View; @@ -50,38 +50,43 @@ public class App extends Plugin { String result = ""; try { - if (action.equals("clearCache")) { - this.clearCache(); - } - else if (action.equals("show")) { - final CordovaWebView wv = this.webView; - ((Activity)this.ctx).runOnUiThread(new Runnable() { - public void run() { - wv.setVisibility(View.VISIBLE); - } - }); - } - else if (action.equals("loadUrl")) { - this.loadUrl(args.getString(0), args.optJSONObject(1)); + if (action.equals("clearCache")) { + this.clearCache(); } - else if (action.equals("cancelLoadUrl")) { - this.cancelLoadUrl(); + else if (action.equals("show")) { // TODO @bc - Not in master branch. When should this be called? + ctx.getActivity().runOnUiThread(new Runnable() { + public void run() { + webView.postMessage("spinner", "stop"); + } + }); +// final CordovaWebView wv = this.webView; +// ((Activity) this.ctx).runOnUiThread(new Runnable() { +// public void run() { +// wv.setVisibility(View.VISIBLE); +// } +// }); } - else if (action.equals("clearHistory")) { - this.clearHistory(); + else if (action.equals("loadUrl")) { + this.loadUrl(args.getString(0), args.optJSONObject(1)); + } + else if (action.equals("cancelLoadUrl")) { + this.cancelLoadUrl(); + } + else if (action.equals("clearHistory")) { + this.clearHistory(); } else if (action.equals("backHistory")) { this.backHistory(); } - else if (action.equals("overrideBackbutton")) { - this.overrideBackbutton(args.getBoolean(0)); + else if (action.equals("overrideBackbutton")) { + this.overrideBackbutton(args.getBoolean(0)); } - else if (action.equals("isBackbuttonOverridden")) { - boolean b = this.isBackbuttonOverridden(); - return new PluginResult(status, b); + else if (action.equals("isBackbuttonOverridden")) { + boolean b = this.isBackbuttonOverridden(); + return new PluginResult(status, b); } - else if (action.equals("exitApp")) { - this.exitApp(); + else if (action.equals("exitApp")) { + this.exitApp(); } return new PluginResult(status, result); } catch (JSONException e) { @@ -93,93 +98,93 @@ public class App extends Plugin { // LOCAL METHODS //-------------------------------------------------------------------------- - /** - * Clear the resource cache. - */ - public void clearCache() { - webView.clearCache(true); - } - - /** - * Load the url into the webview. - * - * @param url - * @param props Properties that can be passed in to the DroidGap activity (i.e. loadingDialog, wait, ...) - * @throws JSONException - */ - public void loadUrl(String url, JSONObject props) throws JSONException { - LOG.d("App", "App.loadUrl("+url+","+props+")"); - int wait = 0; - boolean openExternal = false; - boolean clearHistory = false; + /** + * Clear the resource cache. + */ + public void clearCache() { + this.webView.clearCache(true); + } - // If there are properties, then set them on the Activity - HashMap params = new HashMap(); - if (props != null) { - JSONArray keys = props.names(); - for (int i=0; i params = new HashMap(); + if (props != null) { + JSONArray keys = props.names(); + for (int i = 0; i < keys.length(); i++) { + String key = keys.getString(i); + if (key.equals("wait")) { + wait = props.getInt(key); + } + else if (key.equalsIgnoreCase("openexternal")) { + openExternal = props.getBoolean(key); + } + else if (key.equalsIgnoreCase("clearhistory")) { + clearHistory = props.getBoolean(key); + } + else { + Object value = props.get(key); + if (value == null) { - // If wait property, then delay loading + } + else if (value.getClass().equals(String.class)) { + params.put(key, (String) value); + } + else if (value.getClass().equals(Boolean.class)) { + params.put(key, (Boolean) value); + } + else if (value.getClass().equals(Integer.class)) { + params.put(key, (Integer) value); + } + } + } + } - if (wait > 0) { - try { - synchronized(this) { - this.wait(wait); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - webView.showWebPage(url, openExternal, clearHistory, params); - } + // If wait property, then delay loading + + if (wait > 0) { + try { + synchronized (this) { + this.wait(wait); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + this.webView.showWebPage(url, openExternal, clearHistory, params); + } + + /** + * Cancel loadUrl before it has been loaded (Only works on a CordovaInterface class) + */ + public void cancelLoadUrl() { + this.ctx.cancelLoadUrl(); + } - /** - * Cancel loadUrl before it has been loaded (Only works on a CordovaInterface class) - */ - public void cancelLoadUrl() { - ((DroidGap)this.ctx).cancelLoadUrl(); - } - /** * Clear page history for the app. */ public void clearHistory() { - webView.clearHistory(); + this.webView.clearHistory(); } - + /** * Go to previous page displayed. * This is the same as pressing the backbutton on Android device. */ public void backHistory() { - webView.backHistory(); + this.webView.backHistory(); } /** @@ -189,8 +194,8 @@ public class App extends Plugin { * @param override T=override, F=cancel override */ public void overrideBackbutton(boolean override) { - LOG.i("DroidGap", "WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!"); - ((DroidGap)this.ctx).bound = override; + LOG.i("App", "WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!"); + this.ctx.bindBackButton(override); } /** @@ -199,14 +204,14 @@ public class App extends Plugin { * @return boolean */ public boolean isBackbuttonOverridden() { - return ((DroidGap)this.ctx).bound; + return this.ctx.isBackButtonBound(); } /** * Exit the Android application. */ public void exitApp() { - ((DroidGap)this.ctx).endActivity(); + this.ctx.getActivity().finish(); } } diff --git a/framework/src/org/apache/cordova/AudioHandler.java b/framework/src/org/apache/cordova/AudioHandler.java index d7df63e9..644640c9 100755 --- a/framework/src/org/apache/cordova/AudioHandler.java +++ b/framework/src/org/apache/cordova/AudioHandler.java @@ -29,7 +29,8 @@ import org.json.JSONArray; import org.json.JSONException; import java.util.HashMap; -import java.util.Map.Entry; + +//import java.util.Map.Entry; /** * This class called by CordovaActivity to play and record audio. @@ -45,98 +46,98 @@ import java.util.Map.Entry; public class AudioHandler extends Plugin { public static String TAG = "AudioHandler"; - HashMap players; // Audio player object - ArrayList pausedForPhone; // Audio players that were paused when phone call came in - - /** - * Constructor. - */ - public AudioHandler() { - this.players = new HashMap(); - this.pausedForPhone = new ArrayList(); - } + HashMap players; // Audio player object + ArrayList pausedForPhone; // Audio players that were paused when phone call came in - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public PluginResult execute(String action, JSONArray args, String callbackId) { - PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - - try { - if (action.equals("startRecordingAudio")) { - this.startRecordingAudio(args.getString(0), args.getString(1)); - } - else if (action.equals("stopRecordingAudio")) { - this.stopRecordingAudio(args.getString(0)); - } - else if (action.equals("startPlayingAudio")) { - this.startPlayingAudio(args.getString(0), args.getString(1)); - } - else if (action.equals("seekToAudio")) { - this.seekToAudio(args.getString(0), args.getInt(1)); - } - else if (action.equals("pausePlayingAudio")) { - this.pausePlayingAudio(args.getString(0)); - } - else if (action.equals("stopPlayingAudio")) { - this.stopPlayingAudio(args.getString(0)); - } else if (action.equals("setVolume")) { - try { - this.setVolume(args.getString(0), Float.parseFloat(args.getString(1))); - } catch (NumberFormatException nfe) { - //no-op - } - } else if (action.equals("getCurrentPositionAudio")) { - float f = this.getCurrentPositionAudio(args.getString(0)); - return new PluginResult(status, f); - } - else if (action.equals("getDurationAudio")) { - float f = this.getDurationAudio(args.getString(0), args.getString(1)); - return new PluginResult(status, f); - } - else if (action.equals("release")) { - boolean b = this.release(args.getString(0)); - return new PluginResult(status, b); - } - return new PluginResult(status, result); - } catch (JSONException e) { - e.printStackTrace(); - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); - } - } + /** + * Constructor. + */ + public AudioHandler() { + this.players = new HashMap(); + this.pausedForPhone = new ArrayList(); + } - /** - * Identifies if action to be executed returns a value and should be run synchronously. - * - * @param action The action to execute - * @return T=returns value - */ - public boolean isSynch(String action) { - if (action.equals("getCurrentPositionAudio")) { - return true; - } - else if (action.equals("getDurationAudio")) { - return true; - } - return false; - } + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; - /** - * Stop all audio players and recorders. - */ - public void onDestroy() { + try { + if (action.equals("startRecordingAudio")) { + this.startRecordingAudio(args.getString(0), args.getString(1)); + } + else if (action.equals("stopRecordingAudio")) { + this.stopRecordingAudio(args.getString(0)); + } + else if (action.equals("startPlayingAudio")) { + this.startPlayingAudio(args.getString(0), args.getString(1)); + } + else if (action.equals("seekToAudio")) { + this.seekToAudio(args.getString(0), args.getInt(1)); + } + else if (action.equals("pausePlayingAudio")) { + this.pausePlayingAudio(args.getString(0)); + } + else if (action.equals("stopPlayingAudio")) { + this.stopPlayingAudio(args.getString(0)); + } else if (action.equals("setVolume")) { + try { + this.setVolume(args.getString(0), Float.parseFloat(args.getString(1))); + } catch (NumberFormatException nfe) { + //no-op + } + } else if (action.equals("getCurrentPositionAudio")) { + float f = this.getCurrentPositionAudio(args.getString(0)); + return new PluginResult(status, f); + } + else if (action.equals("getDurationAudio")) { + float f = this.getDurationAudio(args.getString(0), args.getString(1)); + return new PluginResult(status, f); + } + else if (action.equals("release")) { + boolean b = this.release(args.getString(0)); + return new PluginResult(status, b); + } + return new PluginResult(status, result); + } catch (JSONException e) { + e.printStackTrace(); + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } + + /** + * Identifies if action to be executed returns a value and should be run synchronously. + * + * @param action The action to execute + * @return T=returns value + */ + public boolean isSynch(String action) { + if (action.equals("getCurrentPositionAudio")) { + return true; + } + else if (action.equals("getDurationAudio")) { + return true; + } + return false; + } + + /** + * Stop all audio players and recorders. + */ + public void onDestroy() { for (AudioPlayer audio : this.players.values()) { audio.destroy(); } this.players.clear(); - } - + } + /** * Called when a message is sent to plugin. * @@ -144,13 +145,13 @@ public class AudioHandler extends Plugin { * @param data The message data */ public void onMessage(String id, Object data) { - + // If phone message if (id.equals("telephone")) { - + // If phone ringing, then pause playing if ("ringing".equals(data) || "offhook".equals(data)) { - + // Get all audio players and pause them for (AudioPlayer audio : this.players.values()) { if (audio.getState() == AudioPlayer.MEDIA_RUNNING) { @@ -160,7 +161,7 @@ public class AudioHandler extends Plugin { } } - + // If phone idle, then resume playing those players we paused else if ("idle".equals(data)) { for (AudioPlayer audio : this.pausedForPhone) { @@ -174,177 +175,179 @@ public class AudioHandler extends Plugin { //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- - - /** - * Release the audio player instance to save memory. - * - * @param id The id of the audio player - */ - private boolean release(String id) { - if (!this.players.containsKey(id)) { - return false; - } - AudioPlayer audio = this.players.get(id); - this.players.remove(id); - audio.destroy(); - return true; - } - /** - * Start recording and save the specified file. - * - * @param id The id of the audio player - * @param file The name of the file - */ + /** + * Release the audio player instance to save memory. + * + * @param id The id of the audio player + */ + private boolean release(String id) { + if (!this.players.containsKey(id)) { + return false; + } + AudioPlayer audio = this.players.get(id); + this.players.remove(id); + audio.destroy(); + return true; + } + + /** + * Start recording and save the specified file. + * + * @param id The id of the audio player + * @param file The name of the file + */ public void startRecordingAudio(String id, String file) { - // If already recording, then just return; - if (this.players.containsKey(id)) { - return; - } - AudioPlayer audio = new AudioPlayer(this, id); - this.players.put(id, audio); - audio.startRecording(file); + // If already recording, then just return; + if (this.players.containsKey(id)) { + return; + } + AudioPlayer audio = new AudioPlayer(this, id); + this.players.put(id, audio); + audio.startRecording(file); } /** * Stop recording and save to the file specified when recording started. * - * @param id The id of the audio player + * @param id The id of the audio player */ public void stopRecordingAudio(String id) { - AudioPlayer audio = this.players.get(id); - if (audio != null) { - audio.stopRecording(); - this.players.remove(id); - } + AudioPlayer audio = this.players.get(id); + if (audio != null) { + audio.stopRecording(); + this.players.remove(id); + } } - + /** * Start or resume playing audio file. * - * @param id The id of the audio player + * @param id The id of the audio player * @param file The name of the audio file. */ public void startPlayingAudio(String id, String file) { - AudioPlayer audio = this.players.get(id); - if (audio == null) { - audio = new AudioPlayer(this, id); - this.players.put(id, audio); - } - audio.startPlaying(file); + AudioPlayer audio = this.players.get(id); + if (audio == null) { + audio = new AudioPlayer(this, id); + this.players.put(id, audio); + } + audio.startPlaying(file); } /** * Seek to a location. * * - * @param id The id of the audio player - * @param miliseconds int: number of milliseconds to skip 1000 = 1 second + * @param id The id of the audio player + * @param miliseconds int: number of milliseconds to skip 1000 = 1 second */ public void seekToAudio(String id, int milliseconds) { - AudioPlayer audio = this.players.get(id); - if (audio != null) { - audio.seekToPlaying(milliseconds); - } + AudioPlayer audio = this.players.get(id); + if (audio != null) { + audio.seekToPlaying(milliseconds); + } } - + /** * Pause playing. * - * @param id The id of the audio player + * @param id The id of the audio player */ public void pausePlayingAudio(String id) { - AudioPlayer audio = this.players.get(id); - if (audio != null) { - audio.pausePlaying(); - } + AudioPlayer audio = this.players.get(id); + if (audio != null) { + audio.pausePlaying(); + } } /** * Stop playing the audio file. * - * @param id The id of the audio player + * @param id The id of the audio player */ public void stopPlayingAudio(String id) { - AudioPlayer audio = this.players.get(id); - if (audio != null) { - audio.stopPlaying(); - //audio.destroy(); - //this.players.remove(id); - } + AudioPlayer audio = this.players.get(id); + if (audio != null) { + audio.stopPlaying(); + //audio.destroy(); + //this.players.remove(id); + } } - + /** * Get current position of playback. * - * @param id The id of the audio player + * @param id The id of the audio player * @return position in msec */ public float getCurrentPositionAudio(String id) { - AudioPlayer audio = this.players.get(id); - if (audio != null) { - return(audio.getCurrentPosition()/1000.0f); - } - return -1; + AudioPlayer audio = this.players.get(id); + if (audio != null) { + return (audio.getCurrentPosition() / 1000.0f); + } + return -1; } - + /** * Get the duration of the audio file. * - * @param id The id of the audio player + * @param id The id of the audio player * @param file The name of the audio file. * @return The duration in msec. */ public float getDurationAudio(String id, String file) { - - // Get audio file - AudioPlayer audio = this.players.get(id); - if (audio != null) { - return(audio.getDuration(file)); - } - - // If not already open, then open the file - else { - audio = new AudioPlayer(this, id); - this.players.put(id, audio); - return(audio.getDuration(file)); - } - } - + + // Get audio file + AudioPlayer audio = this.players.get(id); + if (audio != null) { + return (audio.getDuration(file)); + } + + // If not already open, then open the file + else { + audio = new AudioPlayer(this, id); + this.players.put(id, audio); + return (audio.getDuration(file)); + } + } + /** * Set the audio device to be used for playback. * * @param output 1=earpiece, 2=speaker */ + @SuppressWarnings("deprecation") public void setAudioOutputDevice(int output) { - AudioManager audiMgr = (AudioManager) this.ctx.getSystemService(Context.AUDIO_SERVICE); - if (output == 2) { - audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL); - } - else if (output == 1) { - audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL); - } - else { - System.out.println("AudioHandler.setAudioOutputDevice() Error: Unknown output device."); - } + AudioManager audiMgr = (AudioManager) this.ctx.getActivity().getSystemService(Context.AUDIO_SERVICE); + if (output == 2) { + audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL); + } + else if (output == 1) { + audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL); + } + else { + System.out.println("AudioHandler.setAudioOutputDevice() Error: Unknown output device."); + } } - + /** * Get the audio device to be used for playback. * * @return 1=earpiece, 2=speaker */ + @SuppressWarnings("deprecation") public int getAudioOutputDevice() { - AudioManager audiMgr = (AudioManager) this.ctx.getSystemService(Context.AUDIO_SERVICE); - if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) { - return 1; - } - else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) { - return 2; - } - else { - return -1; - } + AudioManager audiMgr = (AudioManager) this.ctx.getActivity().getSystemService(Context.AUDIO_SERVICE); + if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) { + return 1; + } + else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) { + return 2; + } + else { + return -1; + } } /** diff --git a/framework/src/org/apache/cordova/AudioPlayer.java b/framework/src/org/apache/cordova/AudioPlayer.java index cb97453a..72efcdae 100755 --- a/framework/src/org/apache/cordova/AudioPlayer.java +++ b/framework/src/org/apache/cordova/AudioPlayer.java @@ -45,413 +45,411 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On private static final String LOG_TAG = "AudioPlayer"; // AudioPlayer states - public static int MEDIA_NONE = 0; - public static int MEDIA_STARTING = 1; - public static int MEDIA_RUNNING = 2; - public static int MEDIA_PAUSED = 3; - public static int MEDIA_STOPPED = 4; - - // AudioPlayer message ids - private static int MEDIA_STATE = 1; - private static int MEDIA_DURATION = 2; + public static int MEDIA_NONE = 0; + public static int MEDIA_STARTING = 1; + public static int MEDIA_RUNNING = 2; + public static int MEDIA_PAUSED = 3; + public static int MEDIA_STOPPED = 4; + + // AudioPlayer message ids + private static int MEDIA_STATE = 1; + private static int MEDIA_DURATION = 2; private static int MEDIA_POSITION = 3; - private static int MEDIA_ERROR = 9; - - // Media error codes - private static int MEDIA_ERR_NONE_ACTIVE = 0; - private static int MEDIA_ERR_ABORTED = 1; - private static int MEDIA_ERR_NETWORK = 2; - private static int MEDIA_ERR_DECODE = 3; - private static int MEDIA_ERR_NONE_SUPPORTED = 4; - - private AudioHandler handler; // The AudioHandler object - private String id; // The id of this player (used to identify Media object in JavaScript) - private int state = MEDIA_NONE; // State of recording or playback - private String audioFile = null; // File name to play or record to - private float duration = -1; // Duration of audio + private static int MEDIA_ERROR = 9; - private MediaRecorder recorder = null; // Audio recording object - private String tempFile = null; // Temporary recording file name - - private MediaPlayer mPlayer = null; // Audio player object - private boolean prepareOnly = false; + // Media error codes + private static int MEDIA_ERR_NONE_ACTIVE = 0; + private static int MEDIA_ERR_ABORTED = 1; +// private static int MEDIA_ERR_NETWORK = 2; +// private static int MEDIA_ERR_DECODE = 3; +// private static int MEDIA_ERR_NONE_SUPPORTED = 4; - /** - * Constructor. - * - * @param handler The audio handler object - * @param id The id of this audio player - */ - public AudioPlayer(AudioHandler handler, String id) { - this.handler = handler; - this.id = id; + private AudioHandler handler; // The AudioHandler object + private String id; // The id of this player (used to identify Media object in JavaScript) + private int state = MEDIA_NONE; // State of recording or playback + private String audioFile = null; // File name to play or record to + private float duration = -1; // Duration of audio + + private MediaRecorder recorder = null; // Audio recording object + private String tempFile = null; // Temporary recording file name + + private MediaPlayer mPlayer = null; // Audio player object + private boolean prepareOnly = false; + + /** + * Constructor. + * + * @param handler The audio handler object + * @param id The id of this audio player + */ + public AudioPlayer(AudioHandler handler, String id) { + this.handler = handler; + this.id = id; this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3"; - } + } - /** - * Destroy player and stop audio playing or recording. - */ - public void destroy() { - - // Stop any play or record - if (this.mPlayer != null) { - if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { - this.mPlayer.stop(); - this.setState(MEDIA_STOPPED); - } - this.mPlayer.release(); - this.mPlayer = null; - } - if (this.recorder != null) { - this.stopRecording(); - this.recorder.release(); - this.recorder = null; - } - } + /** + * Destroy player and stop audio playing or recording. + */ + public void destroy() { + + // Stop any play or record + if (this.mPlayer != null) { + if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { + this.mPlayer.stop(); + this.setState(MEDIA_STOPPED); + } + this.mPlayer.release(); + this.mPlayer = null; + } + if (this.recorder != null) { + this.stopRecording(); + this.recorder.release(); + this.recorder = null; + } + } + + /** + * Start recording the specified file. + * + * @param file The name of the file + */ + public void startRecording(String file) { + if (this.mPlayer != null) { + Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode."); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});"); + } + + // Make sure we're not already recording + else if (this.recorder == null) { + this.audioFile = file; + this.recorder = new MediaRecorder(); + this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC); + this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP); + this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB); + this.recorder.setOutputFile(this.tempFile); + try { + this.recorder.prepare(); + this.recorder.start(); + this.setState(MEDIA_RUNNING); + return; + } catch (IllegalStateException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});"); + } + else { + Log.d(LOG_TAG, "AudioPlayer Error: Already recording."); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});"); + } + } + + /** + * Save temporary recorded file to specified name + * + * @param file + */ + public void moveFile(String file) { + + /* this is a hack to save the file as the specified name */ + File f = new File(this.tempFile); + f.renameTo(new File("/sdcard/" + file)); + } - /** - * Start recording the specified file. - * - * @param file The name of the file - */ - public void startRecording(String file) { - if (this.mPlayer != null) { - Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode."); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});"); - } - - // Make sure we're not already recording - else if (this.recorder == null) { - this.audioFile = file; - this.recorder = new MediaRecorder(); - this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC); - this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP); - this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB); - this.recorder.setOutputFile(this.tempFile); - try { - this.recorder.prepare(); - this.recorder.start(); - this.setState(MEDIA_RUNNING); - return; - } catch (IllegalStateException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});"); - } - else { - Log.d(LOG_TAG, "AudioPlayer Error: Already recording."); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});"); - } - } - - /** - * Save temporary recorded file to specified name - * - * @param file - */ - public void moveFile(String file) { - - /* this is a hack to save the file as the specified name */ - File f = new File(this.tempFile); - f.renameTo(new File("/sdcard/" + file)); - } - /** * Stop recording and save to the file specified when recording started. */ - public void stopRecording() { - if (this.recorder != null) { - try{ - if (this.state == MEDIA_RUNNING) { - this.recorder.stop(); - this.setState(MEDIA_STOPPED); - } - this.moveFile(this.audioFile); - } - catch (Exception e) { - e.printStackTrace(); - } - } - } - + public void stopRecording() { + if (this.recorder != null) { + try { + if (this.state == MEDIA_RUNNING) { + this.recorder.stop(); + this.setState(MEDIA_STOPPED); + } + this.moveFile(this.audioFile); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + /** * Start or resume playing audio file. * * @param file The name of the audio file. */ - public void startPlaying(String file) { - if (this.recorder != null) { - Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode."); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});"); - } - - // If this is a new request to play audio, or stopped - else if ((this.mPlayer == null) || (this.state == MEDIA_STOPPED)) { - try { - // If stopped, then reset player - if (this.mPlayer != null) { - this.mPlayer.reset(); - } - // Otherwise, create a new one - else { - this.mPlayer = new MediaPlayer(); - } - this.audioFile = file; - - // If streaming file - if (this.isStreaming(file)) { - this.mPlayer.setDataSource(file); - this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - this.setState(MEDIA_STARTING); - this.mPlayer.setOnPreparedListener(this); - this.mPlayer.prepareAsync(); - } - - // If local file - else { - if (file.startsWith("/android_asset/")) { - String f = file.substring(15); - android.content.res.AssetFileDescriptor fd = this.handler.ctx.getAssets().openFd(f); - this.mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); - } + public void startPlaying(String file) { + if (this.recorder != null) { + Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode."); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});"); + } + + // If this is a new request to play audio, or stopped + else if ((this.mPlayer == null) || (this.state == MEDIA_STOPPED)) { + try { + // If stopped, then reset player + if (this.mPlayer != null) { + this.mPlayer.reset(); + } + // Otherwise, create a new one + else { + this.mPlayer = new MediaPlayer(); + } + this.audioFile = file; + + // If streaming file + if (this.isStreaming(file)) { + this.mPlayer.setDataSource(file); + this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + this.setState(MEDIA_STARTING); + this.mPlayer.setOnPreparedListener(this); + this.mPlayer.prepareAsync(); + } + + // If local file + else { + if (file.startsWith("/android_asset/")) { + String f = file.substring(15); + android.content.res.AssetFileDescriptor fd = this.handler.ctx.getActivity().getAssets().openFd(f); + this.mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength()); + } else { File fp = new File(file); if (fp.exists()) { FileInputStream fileInputStream = new FileInputStream(file); this.mPlayer.setDataSource(fileInputStream.getFD()); - } + } else { this.mPlayer.setDataSource("/sdcard/" + file); } } - this.setState(MEDIA_STARTING); - this.mPlayer.setOnPreparedListener(this); - this.mPlayer.prepare(); + this.setState(MEDIA_STARTING); + this.mPlayer.setOnPreparedListener(this); + this.mPlayer.prepare(); - // Get duration - this.duration = getDurationInSeconds(); - } - } - catch (Exception e) { - e.printStackTrace(); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});"); - } - } + // Get duration + this.duration = getDurationInSeconds(); + } + } catch (Exception e) { + e.printStackTrace(); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});"); + } + } - // If we have already have created an audio player - else { - - // If player has been paused, then resume playback - if ((this.state == MEDIA_PAUSED) || (this.state == MEDIA_STARTING)) { - this.mPlayer.start(); - this.setState(MEDIA_RUNNING); - } - else { - Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: "+this.state); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});"); - } - } - } + // If we have already have created an audio player + else { - /** - * Seek or jump to a new time in the track. - */ - public void seekToPlaying(int milliseconds) { - if (this.mPlayer != null) { - this.mPlayer.seekTo(milliseconds); - Log.d(LOG_TAG, "Send a onStatus update for the new seek"); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_POSITION+", "+milliseconds/1000.0f+");"); - } - } - - /** - * Pause playing. - */ - public void pausePlaying() { - - // If playing, then pause - if (this.state == MEDIA_RUNNING) { - this.mPlayer.pause(); - this.setState(MEDIA_PAUSED); - } - else { - Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: "+this.state); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_NONE_ACTIVE+"});"); - } - } + // If player has been paused, then resume playback + if ((this.state == MEDIA_PAUSED) || (this.state == MEDIA_STARTING)) { + this.mPlayer.start(); + this.setState(MEDIA_RUNNING); + } + else { + Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: " + this.state); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});"); + } + } + } + + /** + * Seek or jump to a new time in the track. + */ + public void seekToPlaying(int milliseconds) { + if (this.mPlayer != null) { + this.mPlayer.seekTo(milliseconds); + Log.d(LOG_TAG, "Send a onStatus update for the new seek"); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");"); + } + } + + /** + * Pause playing. + */ + public void pausePlaying() { + + // If playing, then pause + if (this.state == MEDIA_RUNNING) { + this.mPlayer.pause(); + this.setState(MEDIA_PAUSED); + } + else { + Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});"); + } + } /** * Stop playing the audio file. */ - public void stopPlaying() { - if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { - this.mPlayer.stop(); - this.setState(MEDIA_STOPPED); - } - else { - Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: "+this.state); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_NONE_ACTIVE+"});"); - } - } - - /** - * Callback to be invoked when playback of a media source has completed. - * - * @param mPlayer The MediaPlayer that reached the end of the file - */ - public void onCompletion(MediaPlayer mPlayer) { - this.setState(MEDIA_STOPPED); - } - + public void stopPlaying() { + if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { + this.mPlayer.stop(); + this.setState(MEDIA_STOPPED); + } + else { + Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});"); + } + } + + /** + * Callback to be invoked when playback of a media source has completed. + * + * @param mPlayer The MediaPlayer that reached the end of the file + */ + public void onCompletion(MediaPlayer mPlayer) { + this.setState(MEDIA_STOPPED); + } + /** * Get current position of playback. * * @return position in msec or -1 if not playing */ - public long getCurrentPosition() { - if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { - int curPos = this.mPlayer.getCurrentPosition(); - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_POSITION+", "+curPos/1000.0f+");"); - return curPos; - } - else { - return -1; - } - } - - /** - * Determine if playback file is streaming or local. - * It is streaming if file name starts with "http://" - * - * @param file The file name - * @return T=streaming, F=local - */ - public boolean isStreaming(String file) { - if (file.contains("http://") || file.contains("https://")) { - return true; - } - else { - return false; - } - } - - /** - * Get the duration of the audio file. + public long getCurrentPosition() { + if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) { + int curPos = this.mPlayer.getCurrentPosition(); + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");"); + return curPos; + } + else { + return -1; + } + } + + /** + * Determine if playback file is streaming or local. + * It is streaming if file name starts with "http://" * - * @param file The name of the audio file. - * @return The duration in msec. - * -1=can't be determined - * -2=not allowed + * @param file The file name + * @return T=streaming, F=local */ - public float getDuration(String file) { - - // Can't get duration of recording - if (this.recorder != null) { - return(-2); // not allowed - } - - // If audio file already loaded and started, then return duration - if (this.mPlayer != null) { - return this.duration; - } - - // If no player yet, then create one - else { - this.prepareOnly = true; - this.startPlaying(file); - - // This will only return value for local, since streaming - // file hasn't been read yet. - return this.duration; - } - } + public boolean isStreaming(String file) { + if (file.contains("http://") || file.contains("https://")) { + return true; + } + else { + return false; + } + } - /** - * Callback to be invoked when the media source is ready for playback. - * - * @param mPlayer The MediaPlayer that is ready for playback - */ - public void onPrepared(MediaPlayer mPlayer) { - // Listen for playback completion - this.mPlayer.setOnCompletionListener(this); + /** + * Get the duration of the audio file. + * + * @param file The name of the audio file. + * @return The duration in msec. + * -1=can't be determined + * -2=not allowed + */ + public float getDuration(String file) { - // If start playing after prepared - if (!this.prepareOnly) { - - // Start playing - this.mPlayer.start(); + // Can't get duration of recording + if (this.recorder != null) { + return (-2); // not allowed + } - // Set player init flag - this.setState(MEDIA_RUNNING); - } - - // Save off duration - this.duration = getDurationInSeconds(); - this.prepareOnly = false; + // If audio file already loaded and started, then return duration + if (this.mPlayer != null) { + return this.duration; + } - // Send status notification to JavaScript - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_DURATION+","+this.duration+");"); - - } + // If no player yet, then create one + else { + this.prepareOnly = true; + this.startPlaying(file); - /** - * By default Android returns the length of audio in mills but we want seconds - * - * @return length of clip in seconds - */ - private float getDurationInSeconds() { - return (this.mPlayer.getDuration() / 1000.0f); - } + // This will only return value for local, since streaming + // file hasn't been read yet. + return this.duration; + } + } - /** - * Callback to be invoked when there has been an error during an asynchronous operation - * (other errors will throw exceptions at method call time). - * - * @param mPlayer the MediaPlayer the error pertains to - * @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED) - * @param arg2 an extra code, specific to the error. - */ - public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) { - Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2+")"); + /** + * Callback to be invoked when the media source is ready for playback. + * + * @param mPlayer The MediaPlayer that is ready for playback + */ + public void onPrepared(MediaPlayer mPlayer) { + // Listen for playback completion + this.mPlayer.setOnCompletionListener(this); - // TODO: Not sure if this needs to be sent? - this.mPlayer.stop(); - this.mPlayer.release(); - - // Send error notification to JavaScript - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":"+arg1+"});"); - return false; - } - - /** - * Set the state and send it to JavaScript. - * - * @param state - */ - private void setState(int state) { - if (this.state != state) { - this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_STATE+", "+state+");"); - } - - this.state = state; - } - - /** - * Get the audio state. - * - * @return int - */ - public int getState() { - return this.state; - } + // If start playing after prepared + if (!this.prepareOnly) { - /** - * Set the volume for audio player - * - * @param volume - */ + // Start playing + this.mPlayer.start(); + + // Set player init flag + this.setState(MEDIA_RUNNING); + } + + // Save off duration + this.duration = getDurationInSeconds(); + this.prepareOnly = false; + + // Send status notification to JavaScript + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");"); + + } + + /** + * By default Android returns the length of audio in mills but we want seconds + * + * @return length of clip in seconds + */ + private float getDurationInSeconds() { + return (this.mPlayer.getDuration() / 1000.0f); + } + + /** + * Callback to be invoked when there has been an error during an asynchronous operation + * (other errors will throw exceptions at method call time). + * + * @param mPlayer the MediaPlayer the error pertains to + * @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED) + * @param arg2 an extra code, specific to the error. + */ + public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) { + Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2 + ")"); + + // TODO: Not sure if this needs to be sent? + this.mPlayer.stop(); + this.mPlayer.release(); + + // Send error notification to JavaScript + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":" + arg1 + "});"); + return false; + } + + /** + * Set the state and send it to JavaScript. + * + * @param state + */ + private void setState(int state) { + if (this.state != state) { + this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state + ");"); + } + + this.state = state; + } + + /** + * Get the audio state. + * + * @return int + */ + public int getState() { + return this.state; + } + + /** + * Set the volume for audio player + * + * @param volume + */ public void setVolume(float volume) { this.mPlayer.setVolume(volume, volume); } diff --git a/framework/src/org/apache/cordova/BatteryListener.java b/framework/src/org/apache/cordova/BatteryListener.java index 84024b2c..43d820b1 100755 --- a/framework/src/org/apache/cordova/BatteryListener.java +++ b/framework/src/org/apache/cordova/BatteryListener.java @@ -24,7 +24,6 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -32,20 +31,20 @@ import android.content.IntentFilter; import android.util.Log; public class BatteryListener extends Plugin { - + private static final String LOG_TAG = "BatteryManager"; BroadcastReceiver receiver; private String batteryCallbackId = null; - + /** * Constructor. */ public BatteryListener() { this.receiver = null; } - + /** * Executes the request and returns PluginResult. * @@ -56,43 +55,43 @@ public class BatteryListener extends Plugin { */ public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.INVALID_ACTION; - String result = "Unsupported Operation: " + action; - + String result = "Unsupported Operation: " + action; + if (action.equals("start")) { - if (this.batteryCallbackId != null) { - return new PluginResult(PluginResult.Status.ERROR, "Battery listener already running."); - } + if (this.batteryCallbackId != null) { + return new PluginResult(PluginResult.Status.ERROR, "Battery listener already running."); + } this.batteryCallbackId = callbackId; // We need to listen to power events to update battery status - IntentFilter intentFilter = new IntentFilter() ; + IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); if (this.receiver == null) { this.receiver = new BroadcastReceiver() { @Override - public void onReceive(Context context, Intent intent) { - updateBatteryInfo(intent); + public void onReceive(Context context, Intent intent) { + updateBatteryInfo(intent); } }; - ctx.registerReceiver(this.receiver, intentFilter); + ctx.getActivity().registerReceiver(this.receiver, intentFilter); } // Don't return any result now, since status results will be sent when events come in from broadcast receiver PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT); pluginResult.setKeepCallback(true); return pluginResult; - } - + } + else if (action.equals("stop")) { removeBatteryListener(); this.sendUpdate(new JSONObject(), false); // release status callback in JS side this.batteryCallbackId = null; return new PluginResult(PluginResult.Status.OK); } - + return new PluginResult(status, result); } - + /** * Stop battery receiver. */ @@ -106,7 +105,7 @@ public class BatteryListener extends Plugin { private void removeBatteryListener() { if (this.receiver != null) { try { - this.ctx.unregisterReceiver(this.receiver); + this.ctx.getActivity().unregisterReceiver(this.receiver); this.receiver = null; } catch (Exception e) { Log.e(LOG_TAG, "Error unregistering battery receiver: " + e.getMessage(), e); @@ -137,20 +136,20 @@ public class BatteryListener extends Plugin { * @param batteryIntent the current battery information * @return */ - private void updateBatteryInfo(Intent batteryIntent) { + private void updateBatteryInfo(Intent batteryIntent) { sendUpdate(this.getBatteryInfo(batteryIntent), true); } - + /** * Create a new plugin result and send it back to JavaScript * * @param connection the network info to set as navigator.connection */ private void sendUpdate(JSONObject info, boolean keepCallback) { - if (this.batteryCallbackId != null) { - PluginResult result = new PluginResult(PluginResult.Status.OK, info); - result.setKeepCallback(keepCallback); - this.success(result, this.batteryCallbackId); - } + if (this.batteryCallbackId != null) { + PluginResult result = new PluginResult(PluginResult.Status.OK, info); + result.setKeepCallback(keepCallback); + this.success(result, this.batteryCallbackId); + } } } diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java index 57514640..94d7e9c7 100755 --- a/framework/src/org/apache/cordova/CameraLauncher.java +++ b/framework/src/org/apache/cordova/CameraLauncher.java @@ -26,7 +26,7 @@ import java.io.IOException; import java.io.OutputStream; import org.apache.commons.codec.binary.Base64; -import org.apache.cordova.api.CordovaInterface; +//import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; @@ -35,7 +35,7 @@ import org.json.JSONException; import android.app.Activity; import android.content.ContentValues; -import android.content.Context; +//import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; @@ -53,50 +53,50 @@ public class CameraLauncher extends Plugin { private static final int DATA_URL = 0; // Return base64 encoded string private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android) - + private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android) private static final int CAMERA = 1; // Take picture from camera private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android) - + private static final int PICTURE = 0; // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType private static final int VIDEO = 1; // allow selection of video only, ONLY RETURNS URL private static final int ALLMEDIA = 2; // allow selection from all media types - + private static final int JPEG = 0; // Take a picture of type JPEG private static final int PNG = 1; // Take a picture of type PNG private static final String GET_PICTURE = "Get Picture"; private static final String GET_VIDEO = "Get Video"; private static final String GET_All = "Get All"; - + private static final String LOG_TAG = "CameraLauncher"; - + private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality) private int targetWidth; // desired width of the image private int targetHeight; // desired height of the image private Uri imageUri; // Uri of captured image private int encodingType; // Type of encoding to use private int mediaType; // What type of media to retrieve - + public String callbackId; private int numPics; - + //This should never be null! - private CordovaInterface cordova; - + //private CordovaInterface cordova; + /** * Constructor. */ public CameraLauncher() { } - public void setContext(Context mCtx) { - super.setContext(mCtx); - if(CordovaInterface.class.isInstance(mCtx)) - cordova = (CordovaInterface) mCtx; - else - LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); - } - +// public void setContext(CordovaInterface mCtx) { +// super.setContext(mCtx); +// if (CordovaInterface.class.isInstance(mCtx)) +// cordova = (CordovaInterface) mCtx; +// else +// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); +// } + /** * Executes the request and returns PluginResult. * @@ -107,9 +107,9 @@ public class CameraLauncher extends Plugin { */ public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.OK; - String result = ""; + String result = ""; this.callbackId = callbackId; - + try { if (action.equals("takePicture")) { int srcType = CAMERA; @@ -127,7 +127,7 @@ public class CameraLauncher extends Plugin { this.targetHeight = args.getInt(4); this.encodingType = args.getInt(5); this.mediaType = args.getInt(6); - + if (srcType == CAMERA) { this.takePicture(destType, encodingType); } @@ -144,11 +144,11 @@ public class CameraLauncher extends Plugin { return new PluginResult(PluginResult.Status.JSON_EXCEPTION); } } - + //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- - + /** * Take a picture with the camera. * When an image is captured or the camera view is cancelled, the result is returned @@ -166,20 +166,21 @@ public class CameraLauncher extends Plugin { public void takePicture(int returnType, int encodingType) { // Save the number of images currently on disk for later this.numPics = queryImgDB().getCount(); - + // Display camera Intent intent = new Intent("android.media.action.IMAGE_CAPTURE"); - + // Specify file so that large image is captured and returned // TODO: What if there isn't any external storage? File photo = createCaptureFile(encodingType); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); this.imageUri = Uri.fromFile(photo); - - if(cordova != null) - cordova.startActivityForResult((Plugin) this, intent, (CAMERA+1)*16 + returnType+1); - else - LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); + + if (this.ctx != null) { + this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA + 1) * 16 + returnType + 1); + } +// else +// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); } /** @@ -191,9 +192,9 @@ public class CameraLauncher extends Plugin { private File createCaptureFile(int encodingType) { File photo = null; if (encodingType == JPEG) { - photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.jpg"); + photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()), "Pic.jpg"); } else if (encodingType == PNG) { - photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.png"); + photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()), "Pic.png"); } else { throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType); } @@ -212,23 +213,25 @@ public class CameraLauncher extends Plugin { Intent intent = new Intent(); String title = GET_PICTURE; if (this.mediaType == PICTURE) { - intent.setType("image/*"); + intent.setType("image/*"); } else if (this.mediaType == VIDEO) { - intent.setType("video/*"); + intent.setType("video/*"); title = GET_VIDEO; } else if (this.mediaType == ALLMEDIA) { // I wanted to make the type 'image/*, video/*' but this does not work on all versions // of android so I had to go with the wildcard search. - intent.setType("*/*"); + intent.setType("*/*"); title = GET_All; } - + intent.setAction(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - cordova.startActivityForResult((Plugin) this, Intent.createChooser(intent, - new String(title)), (srcType+1)*16 + returnType + 1); + if (this.ctx != null) { + this.ctx.startActivityForResult((Plugin) this, Intent.createChooser(intent, + new String(title)), (srcType + 1) * 16 + returnType + 1); + } } /** @@ -262,8 +265,8 @@ public class CameraLauncher extends Plugin { // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this // would result in whitespace in the new image. else { - double newRatio = newWidth / (double)newHeight; - double origRatio = origWidth / (double)origHeight; + double newRatio = newWidth / (double) newHeight; + double origRatio = origWidth / (double) origHeight; if (origRatio > newRatio) { newHeight = (newWidth * origHeight) / origWidth; @@ -271,10 +274,10 @@ public class CameraLauncher extends Plugin { newWidth = (newHeight * origWidth) / origHeight; } } - + return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true); } - + /** * Called when the camera view exits. * @@ -284,22 +287,22 @@ public class CameraLauncher extends Plugin { * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). */ public void onActivityResult(int requestCode, int resultCode, Intent intent) { - + // Get src and dest types from request code - int srcType = (requestCode/16) - 1; + int srcType = (requestCode / 16) - 1; int destType = (requestCode % 16) - 1; - int rotate = 0; - - // Create an ExifHelper to save the exif data that is lost during compression - ExifHelper exif = new ExifHelper(); - try { - if (this.encodingType == JPEG) { - exif.createInFile(DirectoryManager.getTempDirectoryPath(ctx) + "/Pic.jpg"); - exif.readExifData(); - } - } catch (IOException e) { - e.printStackTrace(); - } + int rotate = 0; + + // Create an ExifHelper to save the exif data that is lost during compression + ExifHelper exif = new ExifHelper(); + try { + if (this.encodingType == JPEG) { + exif.createInFile(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()) + "/Pic.jpg"); + exif.readExifData(); + } + } catch (IOException e) { + e.printStackTrace(); + } // If CAMERA if (srcType == CAMERA) { @@ -309,15 +312,15 @@ public class CameraLauncher extends Plugin { // Read in bitmap of captured image Bitmap bitmap; try { - bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri); + bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getActivity().getContentResolver(), imageUri); } catch (FileNotFoundException e) { Uri uri = intent.getData(); - android.content.ContentResolver resolver = this.ctx.getContentResolver(); + android.content.ContentResolver resolver = this.ctx.getActivity().getContentResolver(); bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); } bitmap = scaleBitmap(bitmap); - + // If sending base64 image back if (destType == DATA_URL) { this.processPicture(bitmap); @@ -325,33 +328,33 @@ public class CameraLauncher extends Plugin { } // If sending filename back - else if (destType == FILE_URI){ + else if (destType == FILE_URI) { // Create entry in media store for image // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) ContentValues values = new ContentValues(); values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); Uri uri = null; try { - uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException e) { LOG.d(LOG_TAG, "Can't write to external media storage."); try { - uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException ex) { - LOG.d(LOG_TAG, "Can't write to internal media storage."); + LOG.d(LOG_TAG, "Can't write to internal media storage."); this.failPicture("Error capturing image - no media storage found."); return; } } // Add compressed version of captured image to returned media store Uri - OutputStream os = this.ctx.getContentResolver().openOutputStream(uri); + OutputStream os = this.ctx.getActivity().getContentResolver().openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); - + // Restore exif data to file if (this.encodingType == JPEG) { - exif.createOutFile(FileUtils.getRealPathFromURI(uri, ((Activity) this.ctx))); + exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx)); exif.writeExifData(); } @@ -361,12 +364,12 @@ public class CameraLauncher extends Plugin { bitmap.recycle(); bitmap = null; System.gc(); - + checkForDuplicateImage(FILE_URI); } catch (IOException e) { e.printStackTrace(); this.failPicture("Error capturing image."); - } + } } // If cancelled @@ -379,37 +382,37 @@ public class CameraLauncher extends Plugin { this.failPicture("Did not complete!"); } } - + // If retrieving photo from library else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) { if (resultCode == Activity.RESULT_OK) { Uri uri = intent.getData(); - android.content.ContentResolver resolver = this.ctx.getContentResolver(); - + android.content.ContentResolver resolver = this.ctx.getActivity().getContentResolver(); + // If you ask for video or all media type you will automatically get back a file URI // and there will be no attempt to resize any returned data if (this.mediaType != PICTURE) { - this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } else { // If sending base64 image back if (destType == DATA_URL) { try { Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); - String[] cols = { MediaStore.Images.Media.ORIENTATION }; - Cursor cursor = this.ctx.getContentResolver().query(intent.getData(), - cols, - null, null, null); - if (cursor != null) { - cursor.moveToPosition(0); - rotate = cursor.getInt(0); - cursor.close(); - } - if (rotate != 0) { - Matrix matrix = new Matrix(); - matrix.setRotate(rotate); - bitmap = bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); - } + String[] cols = { MediaStore.Images.Media.ORIENTATION }; + Cursor cursor = this.ctx.getActivity().getContentResolver().query(intent.getData(), + cols, + null, null, null); + if (cursor != null) { + cursor.moveToPosition(0); + rotate = cursor.getInt(0); + cursor.close(); + } + if (rotate != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(rotate); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); + } bitmap = scaleBitmap(bitmap); this.processPicture(bitmap); bitmap.recycle(); @@ -420,7 +423,7 @@ public class CameraLauncher extends Plugin { this.failPicture("Error retrieving image."); } } - + // If sending filename back else if (destType == FILE_URI) { // Do we need to scale the returned file @@ -428,21 +431,21 @@ public class CameraLauncher extends Plugin { try { Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri)); bitmap = scaleBitmap(bitmap); - - String fileName = DirectoryManager.getTempDirectoryPath(ctx) + "/resize.jpg"; - OutputStream os = new FileOutputStream(fileName); + + String fileName = DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()) + "/resize.jpg"; + OutputStream os = new FileOutputStream(fileName); bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os); os.close(); - + // Restore exif data to file if (this.encodingType == JPEG) { - exif.createOutFile(FileUtils.getRealPathFromURI(uri, ((Activity) ctx))); + exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx)); exif.writeExifData(); } bitmap.recycle(); bitmap = null; - + // The resized image is cached by the app in order to get around this and not have to delete you // application cache I'm adding the current system time to the end of the file url. this.success(new PluginResult(PluginResult.Status.OK, ("file://" + fileName + "?" + System.currentTimeMillis())), this.callbackId); @@ -453,16 +456,16 @@ public class CameraLauncher extends Plugin { } } else { - this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId); } } } } else if (resultCode == Activity.RESULT_CANCELED) { - this.failPicture("Selection cancelled."); + this.failPicture("Selection cancelled."); } else { - this.failPicture("Selection did not complete!"); + this.failPicture("Selection did not complete!"); } } } @@ -473,14 +476,14 @@ public class CameraLauncher extends Plugin { * @return a cursor */ private Cursor queryImgDB() { - return this.ctx.getContentResolver().query( + return this.ctx.getActivity().getContentResolver().query( android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.Media._ID }, null, null, null); } - + /** * Used to find out if we are in a situation where the Camera Intent adds to images * to the content store. If we are using a FILE_URI and the number of images in the DB @@ -492,17 +495,17 @@ public class CameraLauncher extends Plugin { int diff = 1; Cursor cursor = queryImgDB(); int currentNumOfImages = cursor.getCount(); - + if (type == FILE_URI) { diff = 2; } - + // delete the duplicate file if the difference is 2 for file URI or 1 for Data URL if ((currentNumOfImages - numPics) == diff) { cursor.moveToLast(); - int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1; + int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1; Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + id); - this.ctx.getContentResolver().delete(uri, null, null); + this.ctx.getActivity().getContentResolver().delete(uri, null, null); } } @@ -511,25 +514,24 @@ public class CameraLauncher extends Plugin { * * @param bitmap */ - public void processPicture(Bitmap bitmap) { + public void processPicture(Bitmap bitmap) { ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream(); try { if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) { - byte[] code = jpeg_data.toByteArray(); + byte[] code = jpeg_data.toByteArray(); byte[] output = Base64.encodeBase64(code); String js_out = new String(output); this.success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId); js_out = null; output = null; code = null; - } - } - catch(Exception e) { + } + } catch (Exception e) { this.failPicture("Error compressing image."); - } + } jpeg_data = null; } - + /** * Send error message to JavaScript. * diff --git a/framework/src/org/apache/cordova/Capture.java b/framework/src/org/apache/cordova/Capture.java index c1ee74ff..bfb5307d 100644 --- a/framework/src/org/apache/cordova/Capture.java +++ b/framework/src/org/apache/cordova/Capture.java @@ -32,7 +32,7 @@ import org.json.JSONObject; import android.app.Activity; import android.content.ContentValues; -import android.content.Context; +//import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -40,47 +40,47 @@ import android.media.MediaPlayer; import android.net.Uri; import android.util.Log; - public class Capture extends Plugin { private static final String VIDEO_3GPP = "video/3gpp"; - private static final String VIDEO_MP4 = "video/mp4"; + private static final String VIDEO_MP4 = "video/mp4"; private static final String AUDIO_3GPP = "audio/3gpp"; private static final String IMAGE_JPEG = "image/jpeg"; - + private static final int CAPTURE_AUDIO = 0; // Constant for capture audio private static final int CAPTURE_IMAGE = 1; // Constant for capture image private static final int CAPTURE_VIDEO = 2; // Constant for capture video private static final String LOG_TAG = "Capture"; - + private static final int CAPTURE_INTERNAL_ERR = 0; - private static final int CAPTURE_APPLICATION_BUSY = 1; - private static final int CAPTURE_INVALID_ARGUMENT = 2; +// private static final int CAPTURE_APPLICATION_BUSY = 1; +// private static final int CAPTURE_INVALID_ARGUMENT = 2; private static final int CAPTURE_NO_MEDIA_FILES = 3; - private static final int CAPTURE_NOT_SUPPORTED = 20; - +// private static final int CAPTURE_NOT_SUPPORTED = 20; + private String callbackId; // The ID of the callback to be invoked with our result private long limit; // the number of pics/vids/clips to take private double duration; // optional duration parameter for video recording private JSONArray results; // The array of results to be returned to the user private Uri imageUri; // Uri of captured image - private CordovaInterface cordova; - public void setContext(Context mCtx) - { - if(CordovaInterface.class.isInstance(mCtx)) - cordova = (CordovaInterface) mCtx; - else - LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); - } - + //private CordovaInterface cordova; + +// public void setContext(Context mCtx) +// { +// if (CordovaInterface.class.isInstance(mCtx)) +// cordova = (CordovaInterface) mCtx; +// else +// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity"); +// } + @Override public PluginResult execute(String action, JSONArray args, String callbackId) { this.callbackId = callbackId; this.limit = 1; this.duration = 0.0f; this.results = new JSONArray(); - + JSONObject options = args.optJSONObject(0); if (options != null) { limit = options.optLong("limit", 1); @@ -102,9 +102,9 @@ public class Capture extends Plugin { this.captureImage(); } else if (action.equals("captureVideo")) { - this.captureVideo(duration); + this.captureVideo(duration); } - + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); r.setKeepCallback(true); return r; @@ -133,7 +133,7 @@ public class Capture extends Plugin { mimeType = FileUtils.getMimeType(filePath); } Log.d(LOG_TAG, "Mime type = " + mimeType); - + if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) { obj = getImageData(filePath, obj); } @@ -143,8 +143,7 @@ public class Capture extends Plugin { else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) { obj = getAudioVideoData(filePath, obj, true); } - } - catch (JSONException e) { + } catch (JSONException e) { Log.d(LOG_TAG, "Error: setting media file data object"); } return obj; @@ -179,15 +178,14 @@ public class Capture extends Plugin { try { player.setDataSource(filePath); player.prepare(); - obj.put("duration", player.getDuration()/1000); + obj.put("duration", player.getDuration() / 1000); if (video) { obj.put("height", player.getVideoHeight()); obj.put("width", player.getVideoWidth()); } - } - catch (IOException e) { + } catch (IOException e) { Log.d(LOG_TAG, "Error: loading video file"); - } + } return obj; } @@ -197,7 +195,7 @@ public class Capture extends Plugin { private void captureAudio() { Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION); - cordova.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO); + this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO); } /** @@ -207,11 +205,11 @@ public class Capture extends Plugin { Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); // Specify file so that large image is captured and returned - File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Capture.jpg"); + File photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()), "Capture.jpg"); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo)); this.imageUri = Uri.fromFile(photo); - cordova.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE); + this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE); } /** @@ -221,10 +219,10 @@ public class Capture extends Plugin { Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE); // Introduced in API 8 //intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration); - - cordova.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO); + + this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO); } - + /** * Called when the video view exits. * @@ -260,11 +258,11 @@ public class Capture extends Plugin { try { // Create an ExifHelper to save the exif data that is lost during compression ExifHelper exif = new ExifHelper(); - exif.createInFile(DirectoryManager.getTempDirectoryPath(ctx) + "/Capture.jpg"); + exif.createInFile(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()) + "/Capture.jpg"); exif.readExifData(); - + // Read in bitmap of captured image - Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri); + Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getActivity().getContentResolver(), imageUri); // Create entry in media store for image // (Don't use insertImage() because it uses default compression setting of 50 - no way to change it) @@ -272,34 +270,34 @@ public class Capture extends Plugin { values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, IMAGE_JPEG); Uri uri = null; try { - uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException e) { LOG.d(LOG_TAG, "Can't write to external media storage."); try { - uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); + uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values); } catch (UnsupportedOperationException ex) { - LOG.d(LOG_TAG, "Can't write to internal media storage."); + LOG.d(LOG_TAG, "Can't write to internal media storage."); this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found.")); return; } } // Add compressed version of captured image to returned media store Uri - OutputStream os = this.ctx.getContentResolver().openOutputStream(uri); + OutputStream os = this.ctx.getActivity().getContentResolver().openOutputStream(uri); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os); os.close(); bitmap.recycle(); bitmap = null; System.gc(); - + // Restore exif data to file - exif.createOutFile(FileUtils.getRealPathFromURI(uri, ((Activity) this.ctx))); + exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx)); exif.writeExifData(); - + // Add image to results results.put(createMediaFile(uri)); - + if (results.length() >= limit) { // Send Uri back to JavaScript for viewing image this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId); @@ -330,7 +328,7 @@ public class Capture extends Plugin { else if (resultCode == Activity.RESULT_CANCELED) { // If we have partial results send them back to the user if (results.length() > 0) { - this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId); } // user canceled the action else { @@ -341,7 +339,7 @@ public class Capture extends Plugin { else { // If we have partial results send them back to the user if (results.length() > 0) { - this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId); + this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId); } // something bad happened else { @@ -357,38 +355,38 @@ public class Capture extends Plugin { * @return a JSONObject that represents a File * @throws IOException */ - private JSONObject createMediaFile(Uri data){ - File fp = new File(FileUtils.getRealPathFromURI(data, ((Activity) this.ctx))); + private JSONObject createMediaFile(Uri data) { + File fp = new File(FileUtils.getRealPathFromURI(data, this.ctx)); JSONObject obj = new JSONObject(); - try { + try { // File properties obj.put("name", fp.getName()); obj.put("fullPath", fp.getAbsolutePath()); - + // Because of an issue with MimeTypeMap.getMimeTypeFromExtension() all .3gpp files // are reported as video/3gpp. I'm doing this hacky check of the URI to see if it // is stored in the audio or video content store. if (fp.getAbsoluteFile().toString().endsWith(".3gp") || fp.getAbsoluteFile().toString().endsWith(".3gpp")) { if (data.toString().contains("/audio/")) { - obj.put("type", AUDIO_3GPP); + obj.put("type", AUDIO_3GPP); } else { - obj.put("type", VIDEO_3GPP); - } + obj.put("type", VIDEO_3GPP); + } } else { - obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath())); + obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath())); } - + obj.put("lastModifiedDate", fp.lastModified()); obj.put("size", fp.length()); } catch (JSONException e) { // this will never happen e.printStackTrace(); } - + return obj; } - + private JSONObject createErrorObject(int code, String message) { JSONObject obj = new JSONObject(); try { diff --git a/framework/src/org/apache/cordova/CompassListener.java b/framework/src/org/apache/cordova/CompassListener.java index 65b33bbd..e9fbcaca 100755 --- a/framework/src/org/apache/cordova/CompassListener.java +++ b/framework/src/org/apache/cordova/CompassListener.java @@ -27,7 +27,6 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -43,18 +42,18 @@ public class CompassListener extends Plugin implements SensorEventListener { public static int STARTING = 1; public static int RUNNING = 2; public static int ERROR_FAILED_TO_START = 3; - + public long TIMEOUT = 30000; // Timeout in msec to shut off listener - + int status; // status of listener float heading; // most recent heading value long timeStamp; // time of most recent value long lastAccessTime; // time the value was last retrieved int accuracy; // accuracy of the sensor - + private SensorManager sensorManager;// Sensor manager Sensor mSensor; // Compass sensor returned by sensor manager - + /** * Constructor. */ @@ -70,9 +69,9 @@ public class CompassListener extends Plugin implements SensorEventListener { * * @param ctx The context of the main Activity. */ - public void setContext(Context ctx) { + public void setContext(CordovaInterface ctx) { super.setContext(ctx); - this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE); + this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE); } /** @@ -85,8 +84,8 @@ public class CompassListener extends Plugin implements SensorEventListener { */ public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - + String result = ""; + try { if (action.equals("start")) { this.start(); @@ -116,7 +115,7 @@ public class CompassListener extends Plugin implements SensorEventListener { } } if (timeout == 0) { - return new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START); + return new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START); } } return new PluginResult(status, getCompassHeading()); @@ -159,7 +158,7 @@ public class CompassListener extends Plugin implements SensorEventListener { } return false; } - + /** * Called when listener is to be shut down and object is being destroyed. */ @@ -177,13 +176,14 @@ public class CompassListener extends Plugin implements SensorEventListener { * @return status of listener */ public int start() { - + // If already starting or running, then just return if ((this.status == CompassListener.RUNNING) || (this.status == CompassListener.STARTING)) { return this.status; } // Get compass sensor from sensor manager + @SuppressWarnings("deprecation") List list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION); // If found, then register as listener @@ -198,10 +198,10 @@ public class CompassListener extends Plugin implements SensorEventListener { else { this.setStatus(CompassListener.ERROR_FAILED_TO_START); } - + return this.status; } - + /** * Stop listening to compass sensor. */ @@ -211,8 +211,7 @@ public class CompassListener extends Plugin implements SensorEventListener { } this.setStatus(CompassListener.STOPPED); } - - + public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub } @@ -237,7 +236,7 @@ public class CompassListener extends Plugin implements SensorEventListener { this.stop(); } } - + /** * Get status of compass sensor. * @@ -246,7 +245,7 @@ public class CompassListener extends Plugin implements SensorEventListener { public int getStatus() { return this.status; } - + /** * Get the most recent compass heading. * @@ -256,7 +255,7 @@ public class CompassListener extends Plugin implements SensorEventListener { this.lastAccessTime = System.currentTimeMillis(); return this.heading; } - + /** * Set the timeout to turn off compass sensor if getHeading() hasn't been called. * @@ -265,7 +264,7 @@ public class CompassListener extends Plugin implements SensorEventListener { public void setTimeout(long timeout) { this.TIMEOUT = timeout; } - + /** * Get the timeout to turn off compass sensor if getHeading() hasn't been called. * @@ -290,7 +289,7 @@ public class CompassListener extends Plugin implements SensorEventListener { */ private JSONObject getCompassHeading() { JSONObject obj = new JSONObject(); - + try { obj.put("magneticHeading", this.getHeading()); obj.put("trueHeading", this.getHeading()); @@ -301,7 +300,7 @@ public class CompassListener extends Plugin implements SensorEventListener { } catch (JSONException e) { // Should never happen } - + return obj; } diff --git a/framework/src/org/apache/cordova/ContactAccessor.java b/framework/src/org/apache/cordova/ContactAccessor.java index 12669ce4..99c375af 100644 --- a/framework/src/org/apache/cordova/ContactAccessor.java +++ b/framework/src/org/apache/cordova/ContactAccessor.java @@ -18,11 +18,12 @@ package org.apache.cordova; import java.util.HashMap; -import android.app.Activity; -import android.content.Context; +//import android.app.Activity; +//import android.content.Context; import android.util.Log; import android.webkit.WebView; +import org.apache.cordova.api.CordovaInterface; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -35,11 +36,11 @@ import org.json.JSONObject; * Eclair or higher, we want to use {@link ContactAccessorSdk5}. */ public abstract class ContactAccessor { - + protected final String LOG_TAG = "ContactsAccessor"; - protected Context mApp; + protected CordovaInterface mApp; protected WebView mView; - + /** * Check to see if the data associated with the key is required to * be populated in the Contact object. @@ -47,22 +48,22 @@ public abstract class ContactAccessor { * @param map created by running buildPopulationSet. * @return true if the key data is required */ - protected boolean isRequired(String key, HashMap map) { - Boolean retVal = map.get(key); - return (retVal == null) ? false : retVal.booleanValue(); - } - + protected boolean isRequired(String key, HashMap map) { + Boolean retVal = map.get(key); + return (retVal == null) ? false : retVal.booleanValue(); + } + /** * Create a hash map of what data needs to be populated in the Contact object * @param fields the list of fields to populate * @return the hash map of required data */ - protected HashMap buildPopulationSet(JSONArray fields) { - HashMap map = new HashMap(); - - String key; - try { - if (fields.length() == 1 && fields.getString(0).equals("*")) { + protected HashMap buildPopulationSet(JSONArray fields) { + HashMap map = new HashMap(); + + String key; + try { + if (fields.length() == 1 && fields.getString(0).equals("*")) { map.put("displayName", true); map.put("name", true); map.put("nickname", true); @@ -76,90 +77,88 @@ public abstract class ContactAccessor { map.put("urls", true); map.put("photos", true); map.put("categories", true); - } - else { - for (int i=0; i@.*/ + /** + * Keep the photo size under the 1 MB blog limit. + */ + private static final long MAX_PHOTO_SIZE = 1048576; - /** - * A static map that converts the JavaScript property name to Android database column name. - */ + private static final String EMAIL_REGEXP = ".+@.+\\.+.+"; /* @.*/ + + /** + * A static map that converts the JavaScript property name to Android database column name. + */ private static final Map dbMap = new HashMap(); static { - dbMap.put("id", ContactsContract.Data.CONTACT_ID); - dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME); - dbMap.put("name", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); - dbMap.put("name.formatted", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); - dbMap.put("name.familyName", ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME); - dbMap.put("name.givenName", ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME); - dbMap.put("name.middleName", ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME); - dbMap.put("name.honorificPrefix", ContactsContract.CommonDataKinds.StructuredName.PREFIX); - dbMap.put("name.honorificSuffix", ContactsContract.CommonDataKinds.StructuredName.SUFFIX); - dbMap.put("nickname", ContactsContract.CommonDataKinds.Nickname.NAME); - dbMap.put("phoneNumbers", ContactsContract.CommonDataKinds.Phone.NUMBER); - dbMap.put("phoneNumbers.value", ContactsContract.CommonDataKinds.Phone.NUMBER); - dbMap.put("emails", ContactsContract.CommonDataKinds.Email.DATA); - dbMap.put("emails.value", ContactsContract.CommonDataKinds.Email.DATA); - dbMap.put("addresses", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); - dbMap.put("addresses.formatted", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); - dbMap.put("addresses.streetAddress", ContactsContract.CommonDataKinds.StructuredPostal.STREET); - dbMap.put("addresses.locality", ContactsContract.CommonDataKinds.StructuredPostal.CITY); - dbMap.put("addresses.region", ContactsContract.CommonDataKinds.StructuredPostal.REGION); - dbMap.put("addresses.postalCode", ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE); - dbMap.put("addresses.country", ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY); - dbMap.put("ims", ContactsContract.CommonDataKinds.Im.DATA); - dbMap.put("ims.value", ContactsContract.CommonDataKinds.Im.DATA); - dbMap.put("organizations", ContactsContract.CommonDataKinds.Organization.COMPANY); - dbMap.put("organizations.name", ContactsContract.CommonDataKinds.Organization.COMPANY); - dbMap.put("organizations.department", ContactsContract.CommonDataKinds.Organization.DEPARTMENT); - dbMap.put("organizations.title", ContactsContract.CommonDataKinds.Organization.TITLE); - dbMap.put("birthday", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE); - dbMap.put("note", ContactsContract.CommonDataKinds.Note.NOTE); - dbMap.put("photos.value", ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); - //dbMap.put("categories.value", null); - dbMap.put("urls", ContactsContract.CommonDataKinds.Website.URL); - dbMap.put("urls.value", ContactsContract.CommonDataKinds.Website.URL); + dbMap.put("id", ContactsContract.Data.CONTACT_ID); + dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME); + dbMap.put("name", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); + dbMap.put("name.formatted", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME); + dbMap.put("name.familyName", ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME); + dbMap.put("name.givenName", ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME); + dbMap.put("name.middleName", ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME); + dbMap.put("name.honorificPrefix", ContactsContract.CommonDataKinds.StructuredName.PREFIX); + dbMap.put("name.honorificSuffix", ContactsContract.CommonDataKinds.StructuredName.SUFFIX); + dbMap.put("nickname", ContactsContract.CommonDataKinds.Nickname.NAME); + dbMap.put("phoneNumbers", ContactsContract.CommonDataKinds.Phone.NUMBER); + dbMap.put("phoneNumbers.value", ContactsContract.CommonDataKinds.Phone.NUMBER); + dbMap.put("emails", ContactsContract.CommonDataKinds.Email.DATA); + dbMap.put("emails.value", ContactsContract.CommonDataKinds.Email.DATA); + dbMap.put("addresses", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); + dbMap.put("addresses.formatted", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS); + dbMap.put("addresses.streetAddress", ContactsContract.CommonDataKinds.StructuredPostal.STREET); + dbMap.put("addresses.locality", ContactsContract.CommonDataKinds.StructuredPostal.CITY); + dbMap.put("addresses.region", ContactsContract.CommonDataKinds.StructuredPostal.REGION); + dbMap.put("addresses.postalCode", ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE); + dbMap.put("addresses.country", ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY); + dbMap.put("ims", ContactsContract.CommonDataKinds.Im.DATA); + dbMap.put("ims.value", ContactsContract.CommonDataKinds.Im.DATA); + dbMap.put("organizations", ContactsContract.CommonDataKinds.Organization.COMPANY); + dbMap.put("organizations.name", ContactsContract.CommonDataKinds.Organization.COMPANY); + dbMap.put("organizations.department", ContactsContract.CommonDataKinds.Organization.DEPARTMENT); + dbMap.put("organizations.title", ContactsContract.CommonDataKinds.Organization.TITLE); + dbMap.put("birthday", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE); + dbMap.put("note", ContactsContract.CommonDataKinds.Note.NOTE); + dbMap.put("photos.value", ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + //dbMap.put("categories.value", null); + dbMap.put("urls", ContactsContract.CommonDataKinds.Website.URL); + dbMap.put("urls.value", ContactsContract.CommonDataKinds.Website.URL); } /** * Create an contact accessor. */ - public ContactAccessorSdk5(WebView view, Context context) { - mApp = context; - mView = view; - } - - /** - * This method takes the fields required and search options in order to produce an - * array of contacts that matches the criteria provided. - * @param fields an array of items to be used as search criteria - * @param options that can be applied to contact searching - * @return an array of contacts - */ - @Override - public JSONArray search(JSONArray fields, JSONObject options) { - // Get the find options - String searchTerm = ""; - int limit = Integer.MAX_VALUE; - boolean multiple = true; - - if (options != null) { - searchTerm = options.optString("filter"); - if (searchTerm.length()==0) { - searchTerm = "%"; - } - else { - searchTerm = "%" + searchTerm + "%"; - } - try { - multiple = options.getBoolean("multiple"); - if (!multiple) { - limit = 1; + public ContactAccessorSdk5(WebView view, CordovaInterface context) { + mApp = context; + mView = view; + } + + /** + * This method takes the fields required and search options in order to produce an + * array of contacts that matches the criteria provided. + * @param fields an array of items to be used as search criteria + * @param options that can be applied to contact searching + * @return an array of contacts + */ + @Override + public JSONArray search(JSONArray fields, JSONObject options) { + // Get the find options + String searchTerm = ""; + int limit = Integer.MAX_VALUE; + boolean multiple = true; + + if (options != null) { + searchTerm = options.optString("filter"); + if (searchTerm.length() == 0) { + searchTerm = "%"; + } + else { + searchTerm = "%" + searchTerm + "%"; + } + try { + multiple = options.getBoolean("multiple"); + if (!multiple) { + limit = 1; + } + } catch (JSONException e) { + // Multiple was not specified so we assume the default is true. + } + } + else { + searchTerm = "%"; } - } catch (JSONException e) { - // Multiple was not specified so we assume the default is true. - } - } - else { - searchTerm = "%"; - } - - //Log.d(LOG_TAG, "Search Term = " + searchTerm); - //Log.d(LOG_TAG, "Field Length = " + fields.length()); - //Log.d(LOG_TAG, "Fields = " + fields.toString()); - // Loop through the fields the user provided to see what data should be returned. - HashMap populate = buildPopulationSet(fields); - - // Build the ugly where clause and where arguments for one big query. - WhereOptions whereOptions = buildWhereClause(fields, searchTerm); - - // Get all the id's where the search term matches the fields passed in. - Cursor idCursor = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - new String[] { ContactsContract.Data.CONTACT_ID }, - whereOptions.getWhere(), - whereOptions.getWhereArgs(), - ContactsContract.Data.CONTACT_ID + " ASC"); + //Log.d(LOG_TAG, "Search Term = " + searchTerm); + //Log.d(LOG_TAG, "Field Length = " + fields.length()); + //Log.d(LOG_TAG, "Fields = " + fields.toString()); + + // Loop through the fields the user provided to see what data should be returned. + HashMap populate = buildPopulationSet(fields); + + // Build the ugly where clause and where arguments for one big query. + WhereOptions whereOptions = buildWhereClause(fields, searchTerm); + + // Get all the id's where the search term matches the fields passed in. + Cursor idCursor = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, + new String[] { ContactsContract.Data.CONTACT_ID }, + whereOptions.getWhere(), + whereOptions.getWhereArgs(), + ContactsContract.Data.CONTACT_ID + " ASC"); + + // Create a set of unique ids + //Log.d(LOG_TAG, "ID cursor query returns = " + idCursor.getCount()); + Set contactIds = new HashSet(); + while (idCursor.moveToNext()) { + contactIds.add(idCursor.getString(idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))); + } + idCursor.close(); + + // Build a query that only looks at ids + WhereOptions idOptions = buildIdClause(contactIds, searchTerm); - // Create a set of unique ids - //Log.d(LOG_TAG, "ID cursor query returns = " + idCursor.getCount()); - Set contactIds = new HashSet(); - while (idCursor.moveToNext()) { - contactIds.add(idCursor.getString(idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID))); - } - idCursor.close(); - - // Build a query that only looks at ids - WhereOptions idOptions = buildIdClause(contactIds, searchTerm); - - // Do the id query - Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI, - null, - idOptions.getWhere(), - idOptions.getWhereArgs(), - ContactsContract.Data.CONTACT_ID + " ASC"); - - JSONArray contacts = populateContactArray(limit, populate, c); - return contacts; - } - - /** - * A special search that finds one contact by id - * - * @param id contact to find by id - * @return a JSONObject representing the contact - * @throws JSONException - */ - public JSONObject getContactById(String id) throws JSONException { // Do the id query - Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI, + Cursor c = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, + null, + idOptions.getWhere(), + idOptions.getWhereArgs(), + ContactsContract.Data.CONTACT_ID + " ASC"); + + JSONArray contacts = populateContactArray(limit, populate, c); + return contacts; + } + + /** + * A special search that finds one contact by id + * + * @param id contact to find by id + * @return a JSONObject representing the contact + * @throws JSONException + */ + public JSONObject getContactById(String id) throws JSONException { + // Do the id query + Cursor c = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI, null, ContactsContract.Data.CONTACT_ID + " = ? ", new String[] { id }, - ContactsContract.Data.CONTACT_ID + " ASC"); + ContactsContract.Data.CONTACT_ID + " ASC"); JSONArray fields = new JSONArray(); fields.put("*"); - - HashMap populate = buildPopulationSet(fields); - + + HashMap populate = buildPopulationSet(fields); + JSONArray contacts = populateContactArray(1, populate, c); - + if (contacts.length() == 1) { return contacts.getJSONObject(0); } else { return null; } - } + } - /** - * Creates an array of contacts from the cursor you pass in - * - * @param limit max number of contacts for the array - * @param populate whether or not you should populate a certain value - * @param c the cursor - * @return a JSONArray of contacts - */ + /** + * Creates an array of contacts from the cursor you pass in + * + * @param limit max number of contacts for the array + * @param populate whether or not you should populate a certain value + * @param c the cursor + * @return a JSONArray of contacts + */ private JSONArray populateContactArray(int limit, HashMap populate, Cursor c) { - - String contactId = ""; - String rawId = ""; - String oldContactId = ""; - boolean newContact = true; - String mimetype = ""; - JSONArray contacts = new JSONArray(); - JSONObject contact = new JSONObject(); - JSONArray organizations = new JSONArray(); - JSONArray addresses = new JSONArray(); - JSONArray phones = new JSONArray(); - JSONArray emails = new JSONArray(); - JSONArray ims = new JSONArray(); - JSONArray websites = new JSONArray(); - JSONArray photos = new JSONArray(); - - if (c.getCount() > 0) { - while (c.moveToNext() && (contacts.length() <= (limit-1))) { - try { - contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); - rawId = c.getString(c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID)); - - // If we are in the first row set the oldContactId - if (c.getPosition() == 0) { - oldContactId = contactId; - } - - // When the contact ID changes we need to push the Contact object - // to the array of contacts and create new objects. - if (!oldContactId.equals(contactId)) { - // Populate the Contact object with it's arrays - // and push the contact into the contacts array - contacts.put(populateContact(contact, organizations, addresses, phones, - emails, ims, websites, photos)); - - // Clean up the objects - contact = new JSONObject(); - organizations = new JSONArray(); - addresses = new JSONArray(); - phones = new JSONArray(); - emails = new JSONArray(); - ims = new JSONArray(); - websites = new JSONArray(); - photos = new JSONArray(); - - // Set newContact to true as we are starting to populate a new contact - newContact = true; - } - - // When we detect a new contact set the ID and display name. - // These fields are available in every row in the result set returned. - if (newContact) { - newContact = false; - contact.put("id", contactId); - contact.put("rawId", rawId); - } - - // Grab the mimetype of the current row as it will be used in a lot of comparisons - mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); - + String contactId = ""; + String rawId = ""; + String oldContactId = ""; + boolean newContact = true; + String mimetype = ""; + + JSONArray contacts = new JSONArray(); + JSONObject contact = new JSONObject(); + JSONArray organizations = new JSONArray(); + JSONArray addresses = new JSONArray(); + JSONArray phones = new JSONArray(); + JSONArray emails = new JSONArray(); + JSONArray ims = new JSONArray(); + JSONArray websites = new JSONArray(); + JSONArray photos = new JSONArray(); + + if (c.getCount() > 0) { + while (c.moveToNext() && (contacts.length() <= (limit - 1))) { + try { + contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); + rawId = c.getString(c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID)); + + // If we are in the first row set the oldContactId + if (c.getPosition() == 0) { + oldContactId = contactId; + } + + // When the contact ID changes we need to push the Contact object + // to the array of contacts and create new objects. + if (!oldContactId.equals(contactId)) { + // Populate the Contact object with it's arrays + // and push the contact into the contacts array + contacts.put(populateContact(contact, organizations, addresses, phones, + emails, ims, websites, photos)); + + // Clean up the objects + contact = new JSONObject(); + organizations = new JSONArray(); + addresses = new JSONArray(); + phones = new JSONArray(); + emails = new JSONArray(); + ims = new JSONArray(); + websites = new JSONArray(); + photos = new JSONArray(); + + // Set newContact to true as we are starting to populate a new contact + newContact = true; + } + + // When we detect a new contact set the ID and display name. + // These fields are available in every row in the result set returned. + if (newContact) { + newContact = false; + contact.put("id", contactId); + contact.put("rawId", rawId); + } + + // Grab the mimetype of the current row as it will be used in a lot of comparisons + mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); + if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) { contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME))); } - if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - && isRequired("name",populate)) { + if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + && isRequired("name", populate)) { contact.put("name", nameQuery(c)); } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - && isRequired("phoneNumbers",populate)) { - phones.put(phoneQuery(c)); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) - && isRequired("emails",populate)) { - emails.put(emailQuery(c)); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) - && isRequired("addresses",populate)) { - addresses.put(addressQuery(c)); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE) - && isRequired("organizations",populate)) { - organizations.put(organizationQuery(c)); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) - && isRequired("ims",populate)) { - ims.put(imQuery(c)); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) - && isRequired("note",populate)) { - contact.put("note",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE))); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) - && isRequired("nickname",populate)) { - contact.put("nickname",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME))); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE) - && isRequired("urls",populate)) { - websites.put(websiteQuery(c)); - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) { - if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE)) - && isRequired("birthday",populate)) { - contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE))); + else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + && isRequired("phoneNumbers", populate)) { + phones.put(phoneQuery(c)); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + && isRequired("emails", populate)) { + emails.put(emailQuery(c)); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) + && isRequired("addresses", populate)) { + addresses.put(addressQuery(c)); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE) + && isRequired("organizations", populate)) { + organizations.put(organizationQuery(c)); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE) + && isRequired("ims", populate)) { + ims.put(imQuery(c)); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) + && isRequired("note", populate)) { + contact.put("note", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE))); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) + && isRequired("nickname", populate)) { + contact.put("nickname", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME))); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE) + && isRequired("urls", populate)) { + websites.put(websiteQuery(c)); + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) { + if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE)) + && isRequired("birthday", populate)) { + contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE))); + } + } + else if (mimetype.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + && isRequired("photos", populate)) { + photos.put(photoQuery(c, contactId)); + } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + + // Set the old contact ID + oldContactId = contactId; + } + + // Push the last contact into the contacts array + if (contacts.length() < limit) { + contacts.put(populateContact(contact, organizations, addresses, phones, + emails, ims, websites, photos)); } - } - else if (mimetype.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - && isRequired("photos",populate)) { - photos.put(photoQuery(c, contactId)); - } } - catch (JSONException e) { - Log.e(LOG_TAG, e.getMessage(),e); - } - - // Set the old contact ID - oldContactId = contactId; - } - - // Push the last contact into the contacts array - if (contacts.length() < limit) { - contacts.put(populateContact(contact, organizations, addresses, phones, - emails, ims, websites, photos)); - } - } - c.close(); + c.close(); return contacts; } - /** - * Builds a where clause all all the ids passed into the method - * @param contactIds a set of unique contact ids - * @param searchTerm what to search for - * @return an object containing the selection and selection args - */ - private WhereOptions buildIdClause(Set contactIds, String searchTerm) { - WhereOptions options = new WhereOptions(); - - // If the user is searching for every contact then short circuit the method - // and return a shorter where clause to be searched. - if (searchTerm.equals("%")) { - options.setWhere("(" + ContactsContract.Data.CONTACT_ID + " LIKE ? )"); - options.setWhereArgs(new String[] {searchTerm}); - return options; + /** + * Builds a where clause all all the ids passed into the method + * @param contactIds a set of unique contact ids + * @param searchTerm what to search for + * @return an object containing the selection and selection args + */ + private WhereOptions buildIdClause(Set contactIds, String searchTerm) { + WhereOptions options = new WhereOptions(); + + // If the user is searching for every contact then short circuit the method + // and return a shorter where clause to be searched. + if (searchTerm.equals("%")) { + options.setWhere("(" + ContactsContract.Data.CONTACT_ID + " LIKE ? )"); + options.setWhereArgs(new String[] { searchTerm }); + return options; + } + + // This clause means that there are specific ID's to be populated + Iterator it = contactIds.iterator(); + StringBuffer buffer = new StringBuffer("("); + + while (it.hasNext()) { + buffer.append("'" + it.next() + "'"); + if (it.hasNext()) { + buffer.append(","); + } + } + buffer.append(")"); + + options.setWhere(ContactsContract.Data.CONTACT_ID + " IN " + buffer.toString()); + options.setWhereArgs(null); + + return options; } - // This clause means that there are specific ID's to be populated - Iterator it = contactIds.iterator(); - StringBuffer buffer = new StringBuffer("("); - - while (it.hasNext()) { - buffer.append("'" + it.next() + "'"); - if (it.hasNext()) { - buffer.append(","); - } - } - buffer.append(")"); - - options.setWhere(ContactsContract.Data.CONTACT_ID + " IN " + buffer.toString()); - options.setWhereArgs(null); - - return options; - } - - /** - * Create a new contact using a JSONObject to hold all the data. - * @param contact - * @param organizations array of organizations - * @param addresses array of addresses - * @param phones array of phones - * @param emails array of emails - * @param ims array of instant messenger addresses - * @param websites array of websites - * @param photos - * @return - */ - private JSONObject populateContact(JSONObject contact, JSONArray organizations, - JSONArray addresses, JSONArray phones, JSONArray emails, - JSONArray ims, JSONArray websites, JSONArray photos) { - try { - // Only return the array if it has at least one entry + /** + * Create a new contact using a JSONObject to hold all the data. + * @param contact + * @param organizations array of organizations + * @param addresses array of addresses + * @param phones array of phones + * @param emails array of emails + * @param ims array of instant messenger addresses + * @param websites array of websites + * @param photos + * @return + */ + private JSONObject populateContact(JSONObject contact, JSONArray organizations, + JSONArray addresses, JSONArray phones, JSONArray emails, + JSONArray ims, JSONArray websites, JSONArray photos) { + try { + // Only return the array if it has at least one entry if (organizations.length() > 0) { contact.put("organizations", organizations); } @@ -442,26 +442,25 @@ public class ContactAccessorSdk5 extends ContactAccessor { if (photos.length() > 0) { contact.put("photos", photos); } + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return contact; } - catch (JSONException e) { - Log.e(LOG_TAG,e.getMessage(),e); - } - return contact; - } - /** - * Take the search criteria passed into the method and create a SQL WHERE clause. - * @param fields the properties to search against - * @param searchTerm the string to search for - * @return an object containing the selection and selection args - */ - private WhereOptions buildWhereClause(JSONArray fields, String searchTerm) { + /** + * Take the search criteria passed into the method and create a SQL WHERE clause. + * @param fields the properties to search against + * @param searchTerm the string to search for + * @return an object containing the selection and selection args + */ + private WhereOptions buildWhereClause(JSONArray fields, String searchTerm) { + + ArrayList where = new ArrayList(); + ArrayList whereArgs = new ArrayList(); + + WhereOptions options = new WhereOptions(); - ArrayList where = new ArrayList(); - ArrayList whereArgs = new ArrayList(); - - WhereOptions options = new WhereOptions(); - /* * Special case where the user wants all fields returned */ @@ -469,1329 +468,1323 @@ public class ContactAccessorSdk5 extends ContactAccessor { // Get all contacts with all properties if ("%".equals(searchTerm)) { options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )"); - options.setWhereArgs(new String[] {searchTerm}); + options.setWhereArgs(new String[] { searchTerm }); return options; } else { // Get all contacts that match the filter but return all properties where.add("(" + dbMap.get("displayName") + " LIKE ? )"); whereArgs.add(searchTerm); - where.add("(" + dbMap.get("name") + " LIKE ? AND " + where.add("(" + dbMap.get("name") + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("nickname") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("nickname") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("phoneNumbers") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("phoneNumbers") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("emails") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("emails") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("addresses") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("addresses") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("ims") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("ims") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("organizations") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("organizations") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("note") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("note") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE); - where.add("(" + dbMap.get("urls") + " LIKE ? AND " - + ContactsContract.Data.MIMETYPE + " = ? )"); + where.add("(" + dbMap.get("urls") + " LIKE ? AND " + + ContactsContract.Data.MIMETYPE + " = ? )"); whereArgs.add(searchTerm); whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE); } } - /* - * Special case for when the user wants all the contacts but - */ - if ("%".equals(searchTerm)) { - options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )"); - options.setWhereArgs(new String[] {searchTerm}); - return options; - } - - String key; - try { - //Log.d(LOG_TAG, "How many fields do we have = " + fields.length()); - for (int i=0; i 1) { - for(Account a : accounts) { - if(a.type.contains("eas")&& a.name.matches(EMAIL_REGEXP)) /*Exchange ActiveSync*/ { - accountName = a.name; - accountType = a.type; - break; - } - } - if(accountName == null){ - for(Account a : accounts){ - if(a.type.contains("com.google") && a.name.matches(EMAIL_REGEXP)) /*Google sync provider*/ { - accountName = a.name; - accountType = a.type; - break; - } - } - } - if(accountName == null){ - for(Account a : accounts){ - if(a.name.matches(EMAIL_REGEXP)) /*Last resort, just look for an email address...*/ { - accountName = a.name; - accountType = a.type; - break; - } - } + organization.put("department", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.DEPARTMENT))); + organization.put("name", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY))); + organization.put("title", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TITLE))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); } + return organization; } - String id = getJsonString(contact, "id"); - // Create new contact - if (id == null) { - return createNewContact(contact, accountType, accountName); - } - // Modify existing contact - else { - return modifyContact(id, contact, accountType, accountName); - } - } - - /** - * Creates a new contact and stores it in the database - * - * @param id the raw contact id which is required for linking items to the contact - * @param contact the contact to be saved - * @param account the account to be saved under - */ - private String modifyContact(String id, JSONObject contact, String accountType, String accountName) { - // Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact. - // But not needed to update existing values. - int rawId = (new Integer(getJsonString(contact,"rawId"))).intValue(); - - // Create a list of attributes to add to the contact database - ArrayList ops = new ArrayList(); - - //Add contact type - ops.add(ContentProviderOperation.newUpdate(ContactsContract.RawContacts.CONTENT_URI) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) - .build()); - - // Modify name - JSONObject name; - try { - String displayName = getJsonString(contact, "displayName"); - name = contact.getJSONObject("name"); - if (displayName != null || name != null) { - ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) - .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + - ContactsContract.Data.MIMETYPE + "=?", - new String[]{id, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE}); - - if (displayName != null) { - builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName); - } - - String familyName = getJsonString(name, "familyName"); - if (familyName != null) { - builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName); - } - String middleName = getJsonString(name, "middleName"); - if (middleName != null) { - builder.withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName); - } - String givenName = getJsonString(name, "givenName"); - if (givenName != null) { - builder.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName); - } - String honorificPrefix = getJsonString(name, "honorificPrefix"); - if (honorificPrefix != null) { - builder.withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, honorificPrefix); - } - String honorificSuffix = getJsonString(name, "honorificSuffix"); - if (honorificSuffix != null) { - builder.withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, honorificSuffix); - } - - ops.add(builder.build()); - } - } catch (JSONException e1) { - Log.d(LOG_TAG, "Could not get name"); - } - - // Modify phone numbers - JSONArray phones = null; - try { - phones = contact.getJSONArray("phoneNumbers"); - if (phones != null) { - for (int i=0; i ops, - JSONObject website) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value")) - .withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))) - .build()); - } - - /** - * Add an im to a list of database actions to be performed - * - * @param ops the list of database actions - * @param im the item to be inserted - */ - private void insertIm(ArrayList ops, JSONObject im) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .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"))) - .build()); - } - - /** - * Add an organization to a list of database actions to be performed - * - * @param ops the list of database actions - * @param org the item to be inserted - */ - private void insertOrganization(ArrayList ops, - JSONObject org) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))) - .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")) - .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) - .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) - .build()); - } - - /** - * Add an address to a list of database actions to be performed - * - * @param ops the list of database actions - * @param address the item to be inserted - */ - private void insertAddress(ArrayList ops, - JSONObject address) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) - .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) - .build()); - } - - /** - * Add an email to a list of database actions to be performed - * - * @param ops the list of database actions - * @param email the item to be inserted - */ - private void insertEmail(ArrayList ops, - JSONObject email) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value")) - .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getPhoneType(getJsonString(email, "type"))) - .build()); - } - - /** - * Add a phone to a list of database actions to be performed - * - * @param ops the list of database actions - * @param phone the item to be inserted - */ - private void insertPhone(ArrayList ops, - JSONObject phone) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")) - .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) - .build()); - } - - /** - * Add a phone to a list of database actions to be performed - * - * @param ops the list of database actions - * @param phone the item to be inserted - */ - private void insertPhoto(ArrayList ops, - JSONObject photo) { - byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes) - .build()); - } - - /** - * Gets the raw bytes from the supplied filename - * - * @param filename the file to read the bytes from - * @return a byte array - * @throws IOException - */ - private byte[] getPhotoBytes(String filename) { - ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - try { - int bytesRead = 0; - long totalBytesRead = 0; - byte[] data = new byte[8192]; - InputStream in = getPathFromUri(filename); - - while ((bytesRead = in.read(data, 0, data.length)) != -1 && totalBytesRead <= MAX_PHOTO_SIZE) { - buffer.write(data, 0, bytesRead); - totalBytesRead += bytesRead; - } - - in.close(); - buffer.flush(); - } catch (FileNotFoundException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } catch (IOException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } - return buffer.toByteArray(); - } - /** - * Get an input stream based on file path or uri content://, http://, file:// - * - * @param path - * @return an input stream - * @throws IOException + /** + * Create a ContactAddress JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactAddress */ - private InputStream getPathFromUri(String path) throws IOException { - if (path.startsWith("content:")) { - Uri uri = Uri.parse(path); - return mApp.getContentResolver().openInputStream(uri); - } - if (path.startsWith("http:") || path.startsWith("file:")) { - URL url = new URL(path); - return url.openStream(); - } - else { - return new FileInputStream(path); - } - } + private JSONObject addressQuery(Cursor cursor) { + JSONObject address = new JSONObject(); + try { + address.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal._ID))); + address.put("pref", false); // Android does not store pref attribute + address.put("type", getAddressType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TYPE)))); + address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS))); + address.put("streetAddress", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.STREET))); + address.put("locality", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY))); + address.put("region", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.REGION))); + address.put("postalCode", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE))); + address.put("country", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return address; + } - /** - * Creates a new contact and stores it in the database - * - * @param contact the contact to be saved - * @param account the account to be saved under - */ - private String createNewContact(JSONObject contact, String accountType, String accountName) { - // Create a list of attributes to add to the contact database - ArrayList ops = new ArrayList(); + /** + * Create a ContactName JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactName + */ + private JSONObject nameQuery(Cursor cursor) { + JSONObject contactName = new JSONObject(); + try { + String familyName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME)); + String givenName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME)); + String middleName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME)); + String honorificPrefix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PREFIX)); + String honorificSuffix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.SUFFIX)); - //Add contact type - ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) - .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) - .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) - .build()); + // Create the formatted name + StringBuffer formatted = new StringBuffer(""); + if (honorificPrefix != null) { + formatted.append(honorificPrefix + " "); + } + if (givenName != null) { + formatted.append(givenName + " "); + } + if (middleName != null) { + formatted.append(middleName + " "); + } + if (familyName != null) { + formatted.append(familyName + " "); + } + if (honorificSuffix != null) { + formatted.append(honorificSuffix + " "); + } - // Add name - try { - JSONObject name = contact.optJSONObject("name"); - String displayName = contact.getString("displayName"); - if (displayName != null || name != null) { + contactName.put("familyName", familyName); + contactName.put("givenName", givenName); + contactName.put("middleName", middleName); + contactName.put("honorificPrefix", honorificPrefix); + contactName.put("honorificSuffix", honorificSuffix); + contactName.put("formatted", formatted); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return contactName; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject phoneQuery(Cursor cursor) { + JSONObject phoneNumber = new JSONObject(); + try { + phoneNumber.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID))); + phoneNumber.put("pref", false); // Android does not store pref attribute + phoneNumber.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))); + phoneNumber.put("type", getPhoneType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (Exception excp) { + Log.e(LOG_TAG, excp.getMessage(), excp); + } + return phoneNumber; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject emailQuery(Cursor cursor) { + JSONObject email = new JSONObject(); + try { + email.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email._ID))); + email.put("pref", false); // Android does not store pref attribute + email.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA))); + email.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return email; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject imQuery(Cursor cursor) { + JSONObject im = new JSONObject(); + try { + 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)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return im; + } + + /** + * Create a ContactField JSONObject + * @param cursor the current database row + * @return a JSONObject representing a ContactField + */ + private JSONObject websiteQuery(Cursor cursor) { + JSONObject website = new JSONObject(); + try { + website.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website._ID))); + website.put("pref", false); // Android does not store pref attribute + website.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.URL))); + website.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.TYPE)))); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return website; + } + + /** + * Create a ContactField JSONObject + * @param contactId + * @return a JSONObject representing a ContactField + */ + private JSONObject photoQuery(Cursor cursor, String contactId) { + JSONObject photo = new JSONObject(); + try { + photo.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo._ID))); + photo.put("pref", false); + photo.put("type", "url"); + Uri person = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, (new Long(contactId))); + Uri photoUri = Uri.withAppendedPath(person, ContactsContract.Contacts.Photo.CONTENT_DIRECTORY); + photo.put("value", photoUri.toString()); + } catch (JSONException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return photo; + } + + @Override + /** + * This method will save a contact object into the devices contacts database. + * + * @param contact the contact to be saved. + * @returns the id if the contact is successfully saved, null otherwise. + */ + public String save(JSONObject contact) { + AccountManager mgr = AccountManager.get(mApp.getActivity()); + Account[] accounts = mgr.getAccounts(); + String accountName = null; + String accountType = null; + + if (accounts.length == 1) { + accountName = accounts[0].name; + accountType = accounts[0].type; + } + else if (accounts.length > 1) { + for (Account a : accounts) { + if (a.type.contains("eas") && a.name.matches(EMAIL_REGEXP)) /*Exchange ActiveSync*/{ + accountName = a.name; + accountType = a.type; + break; + } + } + if (accountName == null) { + for (Account a : accounts) { + if (a.type.contains("com.google") && a.name.matches(EMAIL_REGEXP)) /*Google sync provider*/{ + accountName = a.name; + accountType = a.type; + break; + } + } + } + if (accountName == null) { + for (Account a : accounts) { + if (a.name.matches(EMAIL_REGEXP)) /*Last resort, just look for an email address...*/{ + accountName = a.name; + accountType = a.type; + break; + } + } + } + } + + String id = getJsonString(contact, "id"); + // Create new contact + if (id == null) { + return createNewContact(contact, accountType, accountName); + } + // Modify existing contact + else { + return modifyContact(id, contact, accountType, accountName); + } + } + + /** + * Creates a new contact and stores it in the database + * + * @param id the raw contact id which is required for linking items to the contact + * @param contact the contact to be saved + * @param account the account to be saved under + */ + private String modifyContact(String id, JSONObject contact, String accountType, String accountName) { + // Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact. + // But not needed to update existing values. + int rawId = (new Integer(getJsonString(contact, "rawId"))).intValue(); + + // Create a list of attributes to add to the contact database + ArrayList ops = new ArrayList(); + + //Add contact type + ops.add(ContentProviderOperation.newUpdate(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .build()); + + // Modify name + JSONObject name; + try { + String displayName = getJsonString(contact, "displayName"); + name = contact.getJSONObject("name"); + if (displayName != null || name != null) { + ContentProviderOperation.Builder builder = ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { id, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE }); + + if (displayName != null) { + builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName); + } + + String familyName = getJsonString(name, "familyName"); + if (familyName != null) { + builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName); + } + String middleName = getJsonString(name, "middleName"); + if (middleName != null) { + builder.withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName); + } + String givenName = getJsonString(name, "givenName"); + if (givenName != null) { + builder.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName); + } + String honorificPrefix = getJsonString(name, "honorificPrefix"); + if (honorificPrefix != null) { + builder.withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, honorificPrefix); + } + String honorificSuffix = getJsonString(name, "honorificSuffix"); + if (honorificSuffix != null) { + builder.withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, honorificSuffix); + } + + ops.add(builder.build()); + } + } catch (JSONException e1) { + Log.d(LOG_TAG, "Could not get name"); + } + + // Modify phone numbers + JSONArray phones = null; + try { + phones = contact.getJSONArray("phoneNumbers"); + if (phones != null) { + for (int i = 0; i < phones.length(); i++) { + JSONObject phone = (JSONObject) phones.get(i); + String phoneId = getJsonString(phone, "id"); + // This is a new phone so do a DB insert + if (phoneId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")); + contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing phone so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.Phone._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { phoneId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get phone numbers"); + } + + // Modify emails + JSONArray emails = null; + try { + emails = contact.getJSONArray("emails"); + if (emails != null) { + for (int i = 0; i < emails.length(); i++) { + JSONObject email = (JSONObject) emails.get(i); + String emailId = getJsonString(email, "id"); + // This is a new email so do a DB insert + if (emailId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value")); + contentValues.put(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing email so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.Email._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { emailId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value")) + .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type"))) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Modify addresses + JSONArray addresses = null; + try { + addresses = contact.getJSONArray("addresses"); + if (addresses != null) { + for (int i = 0; i < addresses.length(); i++) { + JSONObject address = (JSONObject) addresses.get(i); + String addressId = getJsonString(address, "id"); + // This is a new address so do a DB insert + if (addressId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")); + contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing address so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.StructuredPostal._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { addressId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get addresses"); + } + + // Modify organizations + JSONArray organizations = null; + try { + organizations = contact.getJSONArray("organizations"); + if (organizations != null) { + for (int i = 0; i < organizations.length(); i++) { + JSONObject org = (JSONObject) organizations.get(i); + String orgId = getJsonString(org, "id"); + // This is a new organization so do a DB insert + if (orgId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))); + contentValues.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")); + contentValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")); + contentValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing organization so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.Organization._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { orgId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))) + .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")) + .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) + .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get organizations"); + } + + // Modify IMs + JSONArray ims = null; + try { + ims = contact.getJSONArray("ims"); + if (ims != null) { + for (int i = 0; i < ims.length(); i++) { + JSONObject im = (JSONObject) ims.get(i); + String imId = getJsonString(im, "id"); + // This is a new IM so do a DB insert + if (imId == null) { + ContentValues contentValues = new ContentValues(); + 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"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing IM so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.Im._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { imId, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value")) + .withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type"))) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Modify note + String note = getJsonString(contact, "note"); + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { id, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note) + .build()); + + // Modify nickname + String nickname = getJsonString(contact, "nickname"); + if (nickname != null) { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { id, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname) + .build()); + } + + // Modify urls + JSONArray websites = null; + try { + websites = contact.getJSONArray("websites"); + if (websites != null) { + for (int i = 0; i < websites.length(); i++) { + JSONObject website = (JSONObject) websites.get(i); + String websiteId = getJsonString(website, "id"); + // This is a new website so do a DB insert + if (websiteId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value")); + contentValues.put(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing website so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.Website._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { websiteId, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value")) + .withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type"))) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get websites"); + } + + // Modify birthday + String birthday = getJsonString(contact, "birthday"); + if (birthday != null) { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.Data.CONTACT_ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=? AND " + + ContactsContract.CommonDataKinds.Event.TYPE + "=?", + new String[] { id, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE, new String("" + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) }) + .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) + .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, birthday) + .build()); + } + + // Modify photos + JSONArray photos = null; + try { + photos = contact.getJSONArray("photos"); + if (photos != null) { + for (int i = 0; i < photos.length(); i++) { + JSONObject photo = (JSONObject) photos.get(i); + String photoId = getJsonString(photo, "id"); + byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); + // This is a new photo so do a DB insert + if (photoId == null) { + ContentValues contentValues = new ContentValues(); + contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId); + contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE); + contentValues.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1); + contentValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes); + + ops.add(ContentProviderOperation.newInsert( + ContactsContract.Data.CONTENT_URI).withValues(contentValues).build()); + } + // This is an existing photo so do a DB update + else { + ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI) + .withSelection(ContactsContract.CommonDataKinds.Photo._ID + "=? AND " + + ContactsContract.Data.MIMETYPE + "=?", + new String[] { photoId, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE }) + .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) + .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes) + .build()); + } + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get photos"); + } + + boolean retVal = true; + + //Modify contact + try { + mApp.getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage(), e); + Log.e(LOG_TAG, Log.getStackTraceString(e), e); + retVal = false; + } catch (OperationApplicationException e) { + Log.e(LOG_TAG, e.getMessage(), e); + Log.e(LOG_TAG, Log.getStackTraceString(e), e); + retVal = false; + } + + // if the save was a succes return the contact ID + if (retVal) { + return id; + } else { + return null; + } + } + + /** + * Add a website to a list of database actions to be performed + * + * @param ops the list of database actions + * @param website the item to be inserted + */ + private void insertWebsite(ArrayList ops, + JSONObject website) { ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) - .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, getJsonString(name, "familyName")) - .withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, getJsonString(name, "middleName")) - .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, getJsonString(name, "givenName")) - .withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, getJsonString(name, "honorificPrefix")) - .withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, getJsonString(name, "honorificSuffix")) - .build()); - } - } - catch (JSONException e) { - Log.d(LOG_TAG, "Could not get name object"); - } - - //Add phone numbers - JSONArray phones = null; - try { - phones = contact.getJSONArray("phoneNumbers"); - if (phones != null) { - for (int i=0; i ops, JSONObject im) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .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"))) + .build()); } - // Add organizations - JSONArray organizations = null; - try { - organizations = contact.getJSONArray("organizations"); - if (organizations != null) { - for (int i=0; i ops, + JSONObject org) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type"))) + .withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department")) + .withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name")) + .withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title")) + .build()); } - // Add IMs - JSONArray ims = null; - try { - ims = contact.getJSONArray("ims"); - if (ims != null) { - for (int i=0; i ops, + JSONObject address) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type"))) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode")) + .withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country")) + .build()); } - // Add note - String note = getJsonString(contact, "note"); - if (note != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note) - .build()); + /** + * Add an email to a list of database actions to be performed + * + * @param ops the list of database actions + * @param email the item to be inserted + */ + private void insertEmail(ArrayList ops, + JSONObject email) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value")) + .withValue(ContactsContract.CommonDataKinds.Email.TYPE, getPhoneType(getJsonString(email, "type"))) + .build()); } - // Add nickname - String nickname = getJsonString(contact, "nickname"); - if (nickname != null) { - ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname) - .build()); - } - - // Add urls - JSONArray websites = null; - try { - websites = contact.getJSONArray("websites"); - if (websites != null) { - for (int i=0; i ops, + JSONObject phone) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value")) + .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type"))) + .build()); } - String newId = null; - //Add contact - try { - ContentProviderResult[] cpResults = mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); + /** + * Add a phone to a list of database actions to be performed + * + * @param ops the list of database actions + * @param phone the item to be inserted + */ + private void insertPhoto(ArrayList ops, + JSONObject photo) { + byte[] bytes = getPhotoBytes(getJsonString(photo, "value")); + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes) + .build()); + } + + /** + * Gets the raw bytes from the supplied filename + * + * @param filename the file to read the bytes from + * @return a byte array + * @throws IOException + */ + private byte[] getPhotoBytes(String filename) { + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + try { + int bytesRead = 0; + long totalBytesRead = 0; + byte[] data = new byte[8192]; + InputStream in = getPathFromUri(filename); + + while ((bytesRead = in.read(data, 0, data.length)) != -1 && totalBytesRead <= MAX_PHOTO_SIZE) { + buffer.write(data, 0, bytesRead); + totalBytesRead += bytesRead; + } + + in.close(); + buffer.flush(); + } catch (FileNotFoundException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (IOException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return buffer.toByteArray(); + } + + /** + * Get an input stream based on file path or uri content://, http://, file:// + * + * @param path + * @return an input stream + * @throws IOException + */ + private InputStream getPathFromUri(String path) throws IOException { + if (path.startsWith("content:")) { + Uri uri = Uri.parse(path); + return mApp.getActivity().getContentResolver().openInputStream(uri); + } + if (path.startsWith("http:") || path.startsWith("file:")) { + URL url = new URL(path); + return url.openStream(); + } + else { + return new FileInputStream(path); + } + } + + /** + * Creates a new contact and stores it in the database + * + * @param contact the contact to be saved + * @param account the account to be saved under + */ + private String createNewContact(JSONObject contact, String accountType, String accountName) { + // Create a list of attributes to add to the contact database + ArrayList ops = new ArrayList(); + + //Add contact type + ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) + .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .build()); + + // Add name + try { + JSONObject name = contact.optJSONObject("name"); + String displayName = contact.getString("displayName"); + if (displayName != null || name != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName) + .withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, getJsonString(name, "familyName")) + .withValue(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, getJsonString(name, "middleName")) + .withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, getJsonString(name, "givenName")) + .withValue(ContactsContract.CommonDataKinds.StructuredName.PREFIX, getJsonString(name, "honorificPrefix")) + .withValue(ContactsContract.CommonDataKinds.StructuredName.SUFFIX, getJsonString(name, "honorificSuffix")) + .build()); + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get name object"); + } + + //Add phone numbers + JSONArray phones = null; + try { + phones = contact.getJSONArray("phoneNumbers"); + if (phones != null) { + for (int i = 0; i < phones.length(); i++) { + JSONObject phone = (JSONObject) phones.get(i); + insertPhone(ops, phone); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get phone numbers"); + } + + // Add emails + JSONArray emails = null; + try { + emails = contact.getJSONArray("emails"); + if (emails != null) { + for (int i = 0; i < emails.length(); i++) { + JSONObject email = (JSONObject) emails.get(i); + insertEmail(ops, email); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Add addresses + JSONArray addresses = null; + try { + addresses = contact.getJSONArray("addresses"); + if (addresses != null) { + for (int i = 0; i < addresses.length(); i++) { + JSONObject address = (JSONObject) addresses.get(i); + insertAddress(ops, address); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get addresses"); + } + + // Add organizations + JSONArray organizations = null; + try { + organizations = contact.getJSONArray("organizations"); + if (organizations != null) { + for (int i = 0; i < organizations.length(); i++) { + JSONObject org = (JSONObject) organizations.get(i); + insertOrganization(ops, org); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get organizations"); + } + + // Add IMs + JSONArray ims = null; + try { + ims = contact.getJSONArray("ims"); + if (ims != null) { + for (int i = 0; i < ims.length(); i++) { + JSONObject im = (JSONObject) ims.get(i); + insertIm(ops, im); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get emails"); + } + + // Add note + String note = getJsonString(contact, "note"); + if (note != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Note.NOTE, note) + .build()); + } + + // Add nickname + String nickname = getJsonString(contact, "nickname"); + if (nickname != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Nickname.NAME, nickname) + .build()); + } + + // Add urls + JSONArray websites = null; + try { + websites = contact.getJSONArray("websites"); + if (websites != null) { + for (int i = 0; i < websites.length(); i++) { + JSONObject website = (JSONObject) websites.get(i); + insertWebsite(ops, website); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get websites"); + } + + // Add birthday + String birthday = getJsonString(contact, "birthday"); + if (birthday != null) { + ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) + .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) + .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE) + .withValue(ContactsContract.CommonDataKinds.Event.TYPE, ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY) + .withValue(ContactsContract.CommonDataKinds.Event.START_DATE, birthday) + .build()); + } + + // Add photos + JSONArray photos = null; + try { + photos = contact.getJSONArray("photos"); + if (photos != null) { + for (int i = 0; i < photos.length(); i++) { + JSONObject photo = (JSONObject) photos.get(i); + insertPhoto(ops, photo); + } + } + } catch (JSONException e) { + Log.d(LOG_TAG, "Could not get photos"); + } + + String newId = null; + //Add contact + try { + ContentProviderResult[] cpResults = mApp.getActivity().getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); if (cpResults.length >= 0) { newId = cpResults[0].uri.getLastPathSegment(); } - } catch (RemoteException e) { - Log.e(LOG_TAG, e.getMessage(), e); - } catch (OperationApplicationException e) { - Log.e(LOG_TAG, e.getMessage(), e); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } catch (OperationApplicationException e) { + Log.e(LOG_TAG, e.getMessage(), e); + } + return newId; } - return newId; - } - @Override - /** - * This method will remove a Contact from the database based on ID. - * @param id the unique ID of the contact to remove - */ - public boolean remove(String id) { + @Override + /** + * This method will remove a Contact from the database based on ID. + * @param id the unique ID of the contact to remove + */ + public boolean remove(String id) { int result = 0; - Cursor cursor = mApp.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, - null, - ContactsContract.Contacts._ID + " = ?", - new String[] {id}, null); - if(cursor.getCount() == 1) { + Cursor cursor = mApp.getActivity().getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, + null, + ContactsContract.Contacts._ID + " = ?", + new String[] { id }, null); + if (cursor.getCount() == 1) { cursor.moveToFirst(); String lookupKey = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)); Uri uri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey); - result = mApp.getContentResolver().delete(uri, null, null); + result = mApp.getActivity().getContentResolver().delete(uri, null, null); } else { - Log.d(LOG_TAG, "Could not find contact with ID"); + Log.d(LOG_TAG, "Could not find contact with ID"); } - return (result > 0) ? true : false; - } - -/************************************************************************** - * - * All methods below this comment are used to convert from JavaScript - * text types to Android integer types and vice versa. - * - *************************************************************************/ - - /** - * Converts a string from the W3C Contact API to it's Android int value. - * @param string - * @return Android int value - */ - private int getPhoneType(String string) { - int type = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; - if (string != null) { - if ("home".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_HOME; - } - else if ("mobile".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE; - } - else if ("work".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_WORK; - } - else if ("work fax".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; - } - else if ("home fax".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME; - } - else if ("fax".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; - } - else if ("pager".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_PAGER; - } - else if ("other".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; - } - else if ("car".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_CAR; - } - else if ("company main".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN; - } - else if ("isdn".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_ISDN; - } - else if ("main".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_MAIN; - } - else if ("other fax".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX; - } - else if ("radio".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_RADIO; - } - else if ("telex".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_TELEX; - } - else if ("work mobile".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE; - } - else if ("work pager".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER; - } - else if ("assistant".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT; - } - else if ("mms".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_MMS; - } - else if ("callback".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK; - } - else if ("tty ttd".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD; - } - else if ("custom".equals(string.toLowerCase())) { - return ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM; - } + return (result > 0) ? true : false; } - return type; - } - /** - * getPhoneType converts an Android phone type into a string - * @param type - * @return phone type as string. - */ - private String getPhoneType(int type) { - String stringType; - switch (type) { - case ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM: - stringType = "custom"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME: - stringType = "home fax"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK: - stringType = "work fax"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: - stringType = "home"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: - stringType = "mobile"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER: - stringType = "pager"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: - stringType = "work"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK: - stringType = "callback"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_CAR: - stringType = "car"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN: - stringType = "company main"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX: - stringType = "other fax"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_RADIO: - stringType = "radio"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_TELEX: - stringType = "telex"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD: - stringType = "tty tdd"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE: - stringType = "work mobile"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER: - stringType = "work pager"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT: - stringType = "assistant"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_MMS: - stringType = "mms"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_ISDN: - stringType = "isdn"; - break; - case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER: - default: - stringType = "other"; - break; + /************************************************************************** + * + * All methods below this comment are used to convert from JavaScript + * text types to Android integer types and vice versa. + * + *************************************************************************/ + + /** + * Converts a string from the W3C Contact API to it's Android int value. + * @param string + * @return Android int value + */ + private int getPhoneType(String string) { + int type = ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; + if (string != null) { + if ("home".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_HOME; + } + else if ("mobile".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE; + } + else if ("work".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_WORK; + } + else if ("work fax".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; + } + else if ("home fax".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME; + } + else if ("fax".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK; + } + else if ("pager".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_PAGER; + } + else if ("other".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER; + } + else if ("car".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_CAR; + } + else if ("company main".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN; + } + else if ("isdn".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_ISDN; + } + else if ("main".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_MAIN; + } + else if ("other fax".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX; + } + else if ("radio".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_RADIO; + } + else if ("telex".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_TELEX; + } + else if ("work mobile".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE; + } + else if ("work pager".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER; + } + else if ("assistant".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT; + } + else if ("mms".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_MMS; + } + else if ("callback".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK; + } + else if ("tty ttd".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD; + } + else if ("custom".equals(string.toLowerCase())) { + return ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM; + } + } + return type; + } + + /** + * getPhoneType converts an Android phone type into a string + * @param type + * @return phone type as string. + */ + private String getPhoneType(int type) { + String stringType; + switch (type) { + case ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM: + stringType = "custom"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME: + stringType = "home fax"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK: + stringType = "work fax"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_HOME: + stringType = "home"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE: + stringType = "mobile"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_PAGER: + stringType = "pager"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_WORK: + stringType = "work"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_CALLBACK: + stringType = "callback"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_CAR: + stringType = "car"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN: + stringType = "company main"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER_FAX: + stringType = "other fax"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_RADIO: + stringType = "radio"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_TELEX: + stringType = "telex"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_TTY_TDD: + stringType = "tty tdd"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_MOBILE: + stringType = "work mobile"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_WORK_PAGER: + stringType = "work pager"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_ASSISTANT: + stringType = "assistant"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_MMS: + stringType = "mms"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_ISDN: + stringType = "isdn"; + break; + case ContactsContract.CommonDataKinds.Phone.TYPE_OTHER: + default: + stringType = "other"; + break; + } + return stringType; } - return stringType; - } /** * Converts a string from the W3C Contact API to it's Android int value. @@ -1800,7 +1793,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { */ private int getContactType(String string) { int type = ContactsContract.CommonDataKinds.Email.TYPE_OTHER; - if (string!=null) { + if (string != null) { if ("home".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_HOME; } @@ -1815,7 +1808,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { } else if ("custom".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM; - } + } } return type; } @@ -1828,22 +1821,22 @@ public class ContactAccessorSdk5 extends ContactAccessor { private String getContactType(int type) { String stringType; switch (type) { - case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: - stringType = "custom"; - break; - case ContactsContract.CommonDataKinds.Email.TYPE_HOME: - stringType = "home"; - break; - case ContactsContract.CommonDataKinds.Email.TYPE_WORK: - stringType = "work"; - break; - case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: - stringType = "mobile"; - break; - case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: - default: - stringType = "other"; - break; + case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM: + stringType = "custom"; + break; + case ContactsContract.CommonDataKinds.Email.TYPE_HOME: + stringType = "home"; + break; + case ContactsContract.CommonDataKinds.Email.TYPE_WORK: + stringType = "work"; + break; + case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE: + stringType = "mobile"; + break; + case ContactsContract.CommonDataKinds.Email.TYPE_OTHER: + default: + stringType = "other"; + break; } return stringType; } @@ -1855,7 +1848,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { */ private int getOrgType(String string) { int type = ContactsContract.CommonDataKinds.Organization.TYPE_OTHER; - if (string!=null) { + if (string != null) { if ("work".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Organization.TYPE_WORK; } @@ -1864,7 +1857,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { } else if ("custom".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM; - } + } } return type; } @@ -1877,16 +1870,16 @@ public class ContactAccessorSdk5 extends ContactAccessor { private String getOrgType(int type) { String stringType; switch (type) { - case ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM: - stringType = "custom"; - break; - case ContactsContract.CommonDataKinds.Organization.TYPE_WORK: - stringType = "work"; - break; - case ContactsContract.CommonDataKinds.Organization.TYPE_OTHER: - default: - stringType = "other"; - break; + case ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM: + stringType = "custom"; + break; + case ContactsContract.CommonDataKinds.Organization.TYPE_WORK: + stringType = "work"; + break; + case ContactsContract.CommonDataKinds.Organization.TYPE_OTHER: + default: + stringType = "other"; + break; } return stringType; } @@ -1898,7 +1891,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { */ private int getAddressType(String string) { int type = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER; - if (string!=null) { + if (string != null) { if ("work".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK; } @@ -1907,7 +1900,7 @@ public class ContactAccessorSdk5 extends ContactAccessor { } else if ("home".equals(string.toLowerCase())) { return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME; - } + } } return type; } @@ -1920,16 +1913,16 @@ public class ContactAccessorSdk5 extends ContactAccessor { private String getAddressType(int type) { String stringType; switch (type) { - case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME: - stringType = "home"; - break; - case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK: - stringType = "work"; - break; - case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER: - default: - stringType = "other"; - break; + case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME: + stringType = "home"; + break; + case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK: + stringType = "work"; + break; + case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER: + default: + stringType = "other"; + break; } return stringType; } diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java index a9f1fc1a..691c276e 100755 --- a/framework/src/org/apache/cordova/CordovaChromeClient.java +++ b/framework/src/org/apache/cordova/CordovaChromeClient.java @@ -17,17 +17,18 @@ under the License. */ package org.apache.cordova; + +import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import org.json.JSONArray; import org.json.JSONException; - -import android.app.Activity; +//import android.app.Activity; import android.app.AlertDialog; -import android.content.Context; +//import android.content.Context; import android.content.DialogInterface; import android.view.KeyEvent; -import android.view.View; +//import android.view.View; import android.webkit.ConsoleMessage; import android.webkit.JsPromptResult; import android.webkit.JsResult; @@ -41,34 +42,41 @@ import android.widget.EditText; * This class is the WebChromeClient that implements callbacks for our web view. */ public class CordovaChromeClient extends WebChromeClient { - private String TAG = "CordovaLog"; private long MAX_QUOTA = 100 * 1024 * 1024; - private Activity ctx; + private CordovaInterface ctx; private CordovaWebView appView; - + /** * Constructor. * * @param ctx */ - public CordovaChromeClient(Context ctx) { - this.ctx = (Activity) ctx; - //appView = this.ctx.appView; - } - - public CordovaChromeClient(Context ctx, CordovaWebView app) - { - this.ctx = (Activity) ctx; - appView = app; + public CordovaChromeClient(CordovaInterface ctx) { + this.ctx = ctx; } - public void setWebView(CordovaWebView view) - { - appView = view; + /** + * Constructor. + * + * @param ctx + * @param app + */ + public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) { + this.ctx = ctx; + this.appView = app; } - + + /** + * Constructor. + * + * @param view + */ + public void setWebView(CordovaWebView view) { + this.appView = view; + } + /** * Tell the client to display a javascript alert dialog. * @@ -79,39 +87,39 @@ public class CordovaChromeClient extends WebChromeClient { */ @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { - AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx); + AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx.getActivity()); dlg.setMessage(message); dlg.setTitle("Alert"); //Don't let alerts break the back button dlg.setCancelable(true); dlg.setPositiveButton(android.R.string.ok, - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - result.confirm(); - } - }); + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.confirm(); + } + }); dlg.setOnCancelListener( - new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - result.confirm(); - } - }); + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + result.confirm(); + } + }); dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { //DO NOTHING public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_BACK) + if (keyCode == KeyEvent.KEYCODE_BACK) { result.confirm(); return false; } else return true; - } - }); + } + }); dlg.create(); dlg.show(); return true; - } + } /** * Tell the client to display a confirm dialog to the user. @@ -123,40 +131,40 @@ public class CordovaChromeClient extends WebChromeClient { */ @Override public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { - AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx); + AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx.getActivity()); dlg.setMessage(message); dlg.setTitle("Confirm"); dlg.setCancelable(true); - dlg.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - result.confirm(); - } - }); - dlg.setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - result.cancel(); - } - }); + dlg.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.confirm(); + } + }); + dlg.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + result.cancel(); + } + }); dlg.setOnCancelListener( - new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - result.cancel(); + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + result.cancel(); } }); dlg.setOnKeyListener(new DialogInterface.OnKeyListener() { //DO NOTHING public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - if(keyCode == KeyEvent.KEYCODE_BACK) + if (keyCode == KeyEvent.KEYCODE_BACK) { result.cancel(); return false; } else return true; - } - }); + } + }); dlg.create(); dlg.show(); return true; @@ -178,14 +186,14 @@ public class CordovaChromeClient extends WebChromeClient { */ @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { - + // Security check to make sure any requests are coming from the page initially // loaded in webview and not another loaded in an iframe. boolean reqOk = false; - if (url.startsWith("file://") || url.indexOf(appView.baseUrl) == 0 || appView.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) { reqOk = true; } - + // Calling PluginManager.exec() to call a native service using // prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true])); if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) { @@ -196,67 +204,72 @@ public class CordovaChromeClient extends WebChromeClient { String action = array.getString(1); String callbackId = array.getString(2); boolean async = array.getBoolean(3); - String r = appView.pluginManager.exec(service, action, callbackId, message, async); + String r = this.appView.pluginManager.exec(service, action, callbackId, message, async); result.confirm(r); } catch (JSONException e) { e.printStackTrace(); } } - + // Polling for JavaScript messages else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) { - String r = appView.callbackServer.getJavascript(); + String r = this.appView.callbackServer.getJavascript(); result.confirm(r); } - + + // Do NO-OP so older code doesn't display dialog + else if (defaultValue.equals("gap_init:")) { + result.confirm("OK"); + } + // Calling into CallbackServer else if (reqOk && defaultValue != null && defaultValue.equals("gap_callbackServer:")) { String r = ""; if (message.equals("usePolling")) { - r = ""+ appView.callbackServer.usePolling(); + r = "" + this.appView.callbackServer.usePolling(); } else if (message.equals("restartServer")) { - appView.callbackServer.restartServer(); + this.appView.callbackServer.restartServer(); } else if (message.equals("getPort")) { - r = Integer.toString(appView.callbackServer.getPort()); + r = Integer.toString(this.appView.callbackServer.getPort()); } else if (message.equals("getToken")) { - r = appView.callbackServer.getToken(); + r = this.appView.callbackServer.getToken(); } result.confirm(r); } - + // Show dialog else { final JsPromptResult res = result; - AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx); + AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx.getActivity()); dlg.setMessage(message); - final EditText input = new EditText(this.ctx); + final EditText input = new EditText(this.ctx.getActivity()); if (defaultValue != null) { input.setText(defaultValue); } dlg.setView(input); dlg.setCancelable(false); - dlg.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - String usertext = input.getText().toString(); - res.confirm(usertext); - } - }); - dlg.setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - res.cancel(); - } - }); + dlg.setPositiveButton(android.R.string.ok, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + String usertext = input.getText().toString(); + res.confirm(usertext); + } + }); + dlg.setNegativeButton(android.R.string.cancel, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + res.cancel(); + } + }); dlg.create(); dlg.show(); } return true; } - + /** * Handle database quota exceeded notification. * @@ -273,7 +286,7 @@ public class CordovaChromeClient extends WebChromeClient { { LOG.d(TAG, "DroidGap: onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota); - if( estimatedSize < MAX_QUOTA) + if (estimatedSize < MAX_QUOTA) { //increase for 1Mb long newQuota = estimatedSize; @@ -289,17 +302,18 @@ public class CordovaChromeClient extends WebChromeClient { } // console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html + @SuppressWarnings("deprecation") @Override public void onConsoleMessage(String message, int lineNumber, String sourceID) - { + { LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message); super.onConsoleMessage(message, lineNumber, sourceID); } - + @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) - { - if(consoleMessage.message() != null) + { + if (consoleMessage.message() != null) LOG.d(TAG, consoleMessage.message()); return super.onConsoleMessage(consoleMessage); } @@ -315,6 +329,4 @@ public class CordovaChromeClient extends WebChromeClient { super.onGeolocationPermissionsShowPrompt(origin, callback); callback.invoke(origin, true, false); } - - } diff --git a/framework/src/org/apache/cordova/CordovaException.java b/framework/src/org/apache/cordova/CordovaException.java old mode 100644 new mode 100755 index c714b723..da024970 --- a/framework/src/org/apache/cordova/CordovaException.java +++ b/framework/src/org/apache/cordova/CordovaException.java @@ -20,5 +20,4 @@ package org.apache.cordova; public class CordovaException extends Exception { - } diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 3318ec7d..594b7bf8 100644 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -28,6 +28,7 @@ import java.util.Stack; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import org.apache.cordova.api.PluginManager; import org.xmlpull.v1.XmlPullParserException; @@ -36,497 +37,606 @@ import android.content.Context; import android.content.Intent; import android.content.res.XmlResourceParser; import android.net.Uri; +import android.os.Bundle; import android.util.AttributeSet; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebSettings.LayoutAlgorithm; -import android.app.Activity; public class CordovaWebView extends WebView { - - public static final String TAG = "CordovaWebView"; - - /** The authorization tokens. */ - private Hashtable authenticationTokens = new Hashtable(); - - /** The whitelist **/ - private ArrayList whiteList = new ArrayList(); - private HashMap whiteListCache = new HashMap(); - protected PluginManager pluginManager; - public CallbackServer callbackServer; - - /** Actvities and other important classes **/ - private Context mCtx; - CordovaWebViewClient viewClient; - private CordovaChromeClient chromeClient; + public static final String TAG = "CordovaWebView"; - //This is for the polyfil history - private String url; - String baseUrl; - private Stack urls = new Stack(); + /** The authorization tokens. */ + //private Hashtable authenticationTokens = new Hashtable(); - boolean useBrowserHistory = false; - - protected int loadUrlTimeout; + /** The whitelist **/ + private ArrayList whiteList = new ArrayList(); + private HashMap whiteListCache = new HashMap(); + public PluginManager pluginManager; + public CallbackServer callbackServer; - protected long loadUrlTimeoutValue; + /** Actvities and other important classes **/ + private CordovaInterface mCtx; + CordovaWebViewClient viewClient; + private CordovaChromeClient chromeClient; - //preferences read from cordova.xml - protected PreferenceSet preferences; - - public CordovaWebView(Context context) { - super(context); - mCtx = context; - setup(); - } - - public CordovaWebView(Context context, AttributeSet attrs) { - super(context, attrs); - mCtx = context; - preferences = new PreferenceSet(); - loadConfiguration(); - setup(); - } - - public CordovaWebView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - mCtx = context; - preferences = new PreferenceSet(); - loadConfiguration(); - setup(); - } - - public CordovaWebView(Context context, AttributeSet attrs, int defStyle, - boolean privateBrowsing) { - super(context, attrs, defStyle, privateBrowsing); - mCtx = context; - preferences = new PreferenceSet(); - loadConfiguration(); - setup(); - } - - private void setup() - { - this.setInitialScale(0); - this.setVerticalScrollBarEnabled(false); - this.requestFocusFromTouch(); + //This is for the polyfil history + private String url; + String baseUrl; + private Stack urls = new Stack(); - // Enable JavaScript - WebSettings settings = this.getSettings(); - settings.setJavaScriptEnabled(true); - settings.setJavaScriptCanOpenWindowsAutomatically(true); - settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); - - //Set the nav dump for HTC - settings.setNavDump(true); + boolean useBrowserHistory = false; - // Enable database - settings.setDatabaseEnabled(true); - String databasePath = mCtx.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); - settings.setDatabasePath(databasePath); - - //Setup the WebChromeClient and WebViewClient - setWebViewClient(new CordovaWebViewClient(mCtx, this)); - setWebChromeClient(new CordovaChromeClient(mCtx, this)); - - // Enable DOM storage - settings.setDomStorageEnabled(true); - - // Enable built-in geolocation - settings.setGeolocationEnabled(true); - - //Start up the plugin manager - this.pluginManager = new PluginManager(this, mCtx); - } - - - //This sets it up so that we can save copies of the clients that we might need later. - public void setWebViewClient(CordovaWebViewClient client) - { - viewClient = client; - super.setWebViewClient(client); - } - - - public void setWebChromeClient(CordovaChromeClient client) - { - chromeClient = client; - super.setWebChromeClient(client); - } - /** - * Sets the authentication token. - * - * @param authenticationToken - * the authentication token - * @param host - * the host - * @param realm - * the realm - */ - public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) { - - if(host == null) { - host = ""; - } - - if(realm == null) { - realm = ""; - } - - authenticationTokens.put(host.concat(realm), authenticationToken); - } - - /** - * Removes the authentication token. - * - * @param host - * the host - * @param realm - * the realm - * @return the authentication token or null if did not exist - */ - public AuthenticationToken removeAuthenticationToken(String host, String realm) { - return authenticationTokens.remove(host.concat(realm)); - } - - /** - * Gets the authentication token. - * - * In order it tries: - * 1- host + realm - * 2- host - * 3- realm - * 4- no host, no realm - * - * @param host - * the host - * @param realm - * the realm - * @return the authentication token - */ - public AuthenticationToken getAuthenticationToken(String host, String realm) { - AuthenticationToken token = null; - - token = authenticationTokens.get(host.concat(realm)); - - if(token == null) { - // try with just the host - token = authenticationTokens.get(host); - - // Try the realm - if(token == null) { - token = authenticationTokens.get(realm); - } - - // if no host found, just query for default - if(token == null) { - token = authenticationTokens.get(""); - } - } - - return token; - } - - /** - * Clear all authentication tokens. - */ - public void clearAuthenticationTokens() { - authenticationTokens.clear(); - } - + // Flag to track that a loadUrl timeout occurred + int loadUrlTimeout = 0; - /** - * Add entry to approved list of URLs (whitelist) - * - * @param origin URL regular expression to allow - * @param subdomains T=include all subdomains under origin - */ - public void addWhiteListEntry(String origin, boolean subdomains) { - try { - // Unlimited access to network resources - if(origin.compareTo("*") == 0) { - LOG.d(TAG, "Unlimited access to network resources"); - whiteList.add(Pattern.compile(".*")); - } else { // specific access - // check if subdomains should be included - // TODO: we should not add more domains if * has already been added - if (subdomains) { - // XXX making it stupid friendly for people who forget to include protocol/SSL - if(origin.startsWith("http")) { - whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?"))); - } else { - whiteList.add(Pattern.compile("^https?://(.*\\.)?"+origin)); - } - LOG.d(TAG, "Origin to allow with subdomains: %s", origin); - } else { - // XXX making it stupid friendly for people who forget to include protocol/SSL - if(origin.startsWith("http")) { - whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://"))); - } else { - whiteList.add(Pattern.compile("^https?://"+origin)); - } - LOG.d(TAG, "Origin to allow: %s", origin); - } - } - } catch(Exception e) { - LOG.d(TAG, "Failed to add origin %s", origin); - } - } + // LoadUrl timeout value in msec (default of 20 sec) + //protected int loadUrlTimeoutValue = 20000; - /** - * Determine if URL is in approved list of URLs to load. - * - * @param url - * @return - */ - public boolean isUrlWhiteListed(String url) { + //preferences read from cordova.xml + //protected PreferenceSet preferences; - // Check to see if we have matched url previously - if (whiteListCache.get(url) != null) { - return true; - } - - // Look for match in white list - Iterator pit = whiteList.iterator(); - while (pit.hasNext()) { - Pattern p = pit.next(); - Matcher m = p.matcher(url); - - // If match found, then cache it to speed up subsequent comparisons - if (m.find()) { - whiteListCache.put(url, true); - return true; - } - } - return false; - } - - /** - * We override loadUrl so that we can track the back history - * @see android.webkit.WebView#loadUrl(java.lang.String) - */ - - @Override - public void loadUrl(String url) - { - if (!url.startsWith("javascript:")) { - this.url = url; - if (this.baseUrl == null) { - int i = url.lastIndexOf('/'); - if (i > 0) { - this.baseUrl = url.substring(0, i+1); - } - else { - this.baseUrl = this.url + "/"; - } - - pluginManager.init(); - - if(!useBrowserHistory) - this.urls.push(url); - } - } - - super.loadUrl(url); - } - - - public void loadUrl(final String url, final int time) - { - // If not first page of app, then load immediately - // Add support for browser history if we use it. - if (this.urls.size() > 0 || this.canGoBack()) { - this.loadUrl(url); - } - - if (!url.startsWith("javascript:")) { - LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time); + /** + * Constructor. + * + * @param context + */ + public CordovaWebView(CordovaInterface context) { + super(context.getActivity()); + this.mCtx = context; + //preferences = new PreferenceSet(); + this.loadConfiguration(); + this.setup(); } - final CordovaWebView me = this; - Runnable runnable = new Runnable() { - public void run() { - try { - synchronized(this) { - this.wait(time); + /** + * Constructor. + * + * @param context + * @param attrs + */ + public CordovaWebView(CordovaInterface context, AttributeSet attrs) { + super(context.getActivity(), attrs); + this.mCtx = context; + //preferences = new PreferenceSet(); + this.loadConfiguration(); + this.setup(); + } + + /** + * Constructor. + * + * @param context + * @param attrs + * @param defStyle + */ + public CordovaWebView(CordovaInterface context, AttributeSet attrs, int defStyle) { + super(context.getActivity(), attrs, defStyle); + this.mCtx = context; + //preferences = new PreferenceSet(); + this.loadConfiguration(); + this.setup(); + } + + /** + * Constructor. + * + * @param context + * @param attrs + * @param defStyle + * @param privateBrowsing + */ + public CordovaWebView(CordovaInterface context, AttributeSet attrs, int defStyle, boolean privateBrowsing) { + super(context.getActivity(), attrs, defStyle, privateBrowsing); + this.mCtx = context; + //preferences = new PreferenceSet(); + this.loadConfiguration(); + this.setup(); + } + + /** + * Initialize webview. + */ + private void setup() { + + this.setInitialScale(0); + this.setVerticalScrollBarEnabled(false); + this.requestFocusFromTouch(); + + // Enable JavaScript + WebSettings settings = this.getSettings(); + settings.setJavaScriptEnabled(true); + settings.setJavaScriptCanOpenWindowsAutomatically(true); + settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); + + //Set the nav dump for HTC + settings.setNavDump(true); + + // Enable database + settings.setDatabaseEnabled(true); + String databasePath = this.mCtx.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); + settings.setDatabasePath(databasePath); + + //Setup the WebChromeClient and WebViewClient + //setWebViewClient(new CordovaWebViewClient(mCtx, this)); + //setWebChromeClient(new CordovaChromeClient(mCtx, this)); + + // Enable DOM storage + settings.setDomStorageEnabled(true); + + // Enable built-in geolocation + settings.setGeolocationEnabled(true); + + //Start up the plugin manager + this.pluginManager = new PluginManager(this, this.mCtx); + } + + /** + * Set the WebViewClient. + * + * @param client + */ + public void setWebViewClient(CordovaWebViewClient client) { + this.viewClient = client; + super.setWebViewClient(client); + } + + /** + * Set the WebChromeClient. + * + * @param client + */ + public void setWebChromeClient(CordovaChromeClient client) { + this.chromeClient = client; + super.setWebChromeClient(client); + } + + /** + * Add entry to approved list of URLs (whitelist) + * + * @param origin URL regular expression to allow + * @param subdomains T=include all subdomains under origin + */ + public void addWhiteListEntry(String origin, boolean subdomains) { + try { + // Unlimited access to network resources + if (origin.compareTo("*") == 0) { + LOG.d(TAG, "Unlimited access to network resources"); + this.whiteList.add(Pattern.compile(".*")); + } else { // specific access + // check if subdomains should be included + // TODO: we should not add more domains if * has already been added + if (subdomains) { + // XXX making it stupid friendly for people who forget to include protocol/SSL + if (origin.startsWith("http")) { + this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?"))); + } else { + this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin)); + } + LOG.d(TAG, "Origin to allow with subdomains: %s", origin); + } else { + // XXX making it stupid friendly for people who forget to include protocol/SSL + if (origin.startsWith("http")) { + this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://"))); + } else { + this.whiteList.add(Pattern.compile("^https?://" + origin)); + } + LOG.d(TAG, "Origin to allow: %s", origin); } - } catch (InterruptedException e) { + } + } catch (Exception e) { + LOG.d(TAG, "Failed to add origin %s", origin); + } + } + + /** + * Determine if URL is in approved list of URLs to load. + * + * @param url + * @return + */ + public boolean isUrlWhiteListed(String url) { + + // Check to see if we have matched url previously + if (this.whiteListCache.get(url) != null) { + return true; + } + + // Look for match in white list + Iterator pit = this.whiteList.iterator(); + while (pit.hasNext()) { + Pattern p = pit.next(); + Matcher m = p.matcher(url); + + // If match found, then cache it to speed up subsequent comparisons + if (m.find()) { + this.whiteListCache.put(url, true); + return true; + } + } + return false; + } + + /** + * Load the url into the webview. + * + * @param url + */ + @Override + public void loadUrl(String url) { + String initUrl = this.getProperty("url", null); + + // If first page of app, then set URL to load to be the one passed in + if (initUrl == null || (this.urls.size() > 0)) { + this.loadUrlIntoView(url); + } + // Otherwise use the URL specified in the activity's extras bundle + else { + this.loadUrlIntoView(initUrl); + } + } + + /** + * Load the url into the webview after waiting for period of time. + * This is used to display the splashscreen for certain amount of time. + * + * @param url + * @param time The number of ms to wait before loading webview + */ + public void loadUrl(final String url, int time) { + String initUrl = this.getProperty("url", null); + + // If first page of app, then set URL to load to be the one passed in + if (initUrl == null || (this.urls.size() > 0)) { + this.loadUrlIntoView(url, time); + } + // Otherwise use the URL specified in the activity's extras bundle + else { + this.loadUrlIntoView(initUrl); + } + } + + /** + * Load the url into the webview. + * + * @param url + */ + public void loadUrlIntoView(final String url) { + if (!url.startsWith("javascript:")) { + LOG.d(TAG, ">>> loadUrl(" + url + ")"); + + this.url = url; + if (this.baseUrl == null) { + int i = url.lastIndexOf('/'); + if (i > 0) { + this.baseUrl = url.substring(0, i + 1); + } + else { + this.baseUrl = this.url + "/"; + } + + this.pluginManager.init(); + + if (!this.useBrowserHistory) { + this.urls.push(url); + } + } + + // Create a timeout timer for loadUrl + final CordovaWebView me = this; + final int currentLoadUrlTimeout = me.loadUrlTimeout; + final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("loadUrlTimeoutValue", "20000")); + + // Timeout error method + final Runnable loadError = new Runnable() { + public void run() { + me.stopLoading(); + LOG.e(TAG, "CordovaWebView: TIMEOUT ERROR!"); + if (viewClient != null) { + viewClient.onReceivedError(me, -6, "The connection to the server was unsuccessful.", url); + } + } + }; + + // Timeout timer method + final Runnable timeoutCheck = new Runnable() { + public void run() { + try { + synchronized (this) { + wait(loadUrlTimeoutValue); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + // If timeout, then stop loading and handle error + if (me.loadUrlTimeout == currentLoadUrlTimeout) { + me.mCtx.getActivity().runOnUiThread(loadError); + } + } + }; + + // Load url + this.mCtx.getActivity().runOnUiThread(new Runnable() { + public void run() { + Thread thread = new Thread(timeoutCheck); + thread.start(); + me.loadUrlNow(url); + } + }); + } + + // If Javascript, then just load it now + else { + super.loadUrl(url); + } + } + + /** + * Load URL in webview. + * + * @param url + */ + private void loadUrlNow(String url) { + LOG.d(TAG, ">>> loadUrlNow()"); + super.loadUrl(url); + } + + /** + * Load the url into the webview after waiting for period of time. + * This is used to display the splashscreen for certain amount of time. + * + * @param url + * @param time The number of ms to wait before loading webview + */ + public void loadUrlIntoView(final String url, final int time) { + + // If not first page of app, then load immediately + // Add support for browser history if we use it. + if ((url.startsWith("javascript:")) || this.urls.size() > 0 || this.canGoBack()) { + } + + // If first page, then show splashscreen + else { + + LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time); + + // Send message to show splashscreen now if desired + this.postMessage("splashscreen", "show"); + } + + // Load url + this.loadUrlIntoView(url); + } + + /** + * Send JavaScript statement back to JavaScript. + * (This is a convenience method) + * + * @param message + */ + public void sendJavascript(String statement) { + if (this.callbackServer != null) { + this.callbackServer.sendJavascript(statement); + } + } + + /** + * Send a message to all plugins. + * + * @param id The message id + * @param data The message data + */ + public void postMessage(String id, Object data) { + if (this.pluginManager != null) { + this.pluginManager.postMessage(id, data); + } + } + + /** + * Returns the top url on the stack without removing it from + * the stack. + */ + public String peekAtUrlStack() { + if (this.urls.size() > 0) { + return this.urls.peek(); + } + return ""; + } + + /** + * Add a url to the stack + * + * @param url + */ + public void pushUrl(String url) { + this.urls.push(url); + } + + /** + * Go to previous page in history. (We manage our own history) + * + * @return true if we went back, false if we are already at top + */ + public boolean backHistory() { + + // 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()) { + super.goBack(); + return true; + } + + // If our managed history has prev url + if (this.urls.size() > 1) { + 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); + return true; + } + + return false; + } + + /** + * Return true if there is a history item. + * + * @return + */ + public boolean canGoBack() { + if (super.canGoBack()) { + return true; + } + if (this.urls.size() > 1) { + return true; + } + return false; + } + + /** + * Load the specified URL in the Cordova webview or a new browser instance. + * + * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded. + * + * @param url The url to load. + * @param openExternal Load url in browser instead of Cordova webview. + * @param clearHistory Clear the history stack, so new page becomes top of history + * @param params DroidGap parameters for new app + */ + public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap params) { + LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory); + + // If clearing history + if (clearHistory) { + this.clearHistory(); + } + + // If loading into our webview + if (!openExternal) { + + // Make sure url is in whitelist + if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || isUrlWhiteListed(url)) { + // TODO: What about params? + + // Clear out current url from history, since it will be replacing it + if (clearHistory) { + this.urls.clear(); + } + + // Load new URL + this.loadUrl(url); + } + // Load in default viewer if not + else { + LOG.w(TAG, "showWebPage: Cannot load URL into webview since it is not in white list. Loading into browser instead. (URL=" + url + ")"); + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + mCtx.getActivity().startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + LOG.e(TAG, "Error loading url " + url, e); + } + } + } + + // Load in default view intent + else { + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse(url)); + mCtx.getActivity().startActivity(intent); + } catch (android.content.ActivityNotFoundException e) { + LOG.e(TAG, "Error loading url " + url, e); + } + } + } + + /** + * Load Cordova configuration from res/xml/cordova.xml. + * Approved list of URLs that can be loaded into DroidGap + * + * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR) + * + */ + private void loadConfiguration() { + int id = getResources().getIdentifier("cordova", "xml", this.mCtx.getActivity().getPackageName()); + if (id == 0) { + LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); + return; + } + XmlResourceParser xml = getResources().getXml(id); + int eventType = -1; + while (eventType != XmlResourceParser.END_DOCUMENT) { + if (eventType == XmlResourceParser.START_TAG) { + String strNode = xml.getName(); + if (strNode.equals("access")) { + String origin = xml.getAttributeValue(null, "origin"); + String subdomains = xml.getAttributeValue(null, "subdomains"); + if (origin != null) { + this.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); + } + } + else if (strNode.equals("log")) { + String level = xml.getAttributeValue(null, "level"); + LOG.i("CordovaLog", "Found log level %s", level); + if (level != null) { + LOG.setLogLevel(level); + } + } + else if (strNode.equals("preference")) { + String name = xml.getAttributeValue(null, "name"); + String value = xml.getAttributeValue(null, "value"); + + // TODO @bc Is preferences needed? Just use Intent.putExtra? + //String readonlyString = xml.getAttributeValue(null, "readonly"); + + //boolean readonly = (readonlyString != null && + // readonlyString.equals("true")); + + LOG.i("CordovaLog", "Found preference for %s=%s", name, value); + + //preferences.add(new PreferenceNode(name, value, readonly)); + + // Save preferences in Intent + this.mCtx.getActivity().getIntent().putExtra(name, value); + } + } + try { + eventType = xml.next(); + } catch (XmlPullParserException e) { + e.printStackTrace(); + } catch (IOException e) { e.printStackTrace(); } - //I'm pretty sure this has to be on the UI thread - me.loadUrl(url); - } - }; - Thread thread = new Thread(runnable); - thread.start(); - } + } - public void sendJavascript(String statement) { - callbackServer.sendJavascript(statement); - } + // Init preferences + //this.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true"); + if ("true".equals(this.getProperty("useBrowserHistory", "true"))) { + this.useBrowserHistory = true; + } + else { + this.useBrowserHistory = false; + } + } - public void postMessage(String id, String data) { - pluginManager.postMessage(id, data); - } - - /** - * Returns the top url on the stack without removing it from - * the stack. - */ - public String peekAtUrlStack() { - if (urls.size() > 0) { - return urls.peek(); - } - return ""; - } - - /** - * Add a url to the stack - * - * @param url - */ - public void pushUrl(String url) { - urls.push(url); - } - - /** - * Go to previous page in history. (We manage our own history) - * - * @return true if we went back, false if we are already at top - */ - public boolean backHistory() { - - // 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 (this.canGoBack()) { - this.goBack(); - return true; - } - - // If our managed history has prev url - if (this.urls.size() > 1) { - 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() - loadUrl(url); - return true; - } - - return false; - } - - - /** - * Load the specified URL in the Cordova webview or a new browser instance. - * - * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded. - * - * @param url The url to load. - * @param openExternal Load url in browser instead of Cordova webview. - * @param clearHistory Clear the history stack, so new page becomes top of history - * @param params DroidGap parameters for new app - */ - public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap params) { //throws android.content.ActivityNotFoundException { - LOG.d(TAG, "showWebPage(%s, %b, %b, HashMap", url, openExternal, clearHistory); - - // If clearing history - if (clearHistory) { - this.clearHistory(); - } - - // If loading into our webview - if (!openExternal) { - - // Make sure url is in whitelist - if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || isUrlWhiteListed(url)) { - // TODO: What about params? - - // Clear out current url from history, since it will be replacing it - if (clearHistory) { - this.urls.clear(); - } - - // Load new URL - this.loadUrl(url); - } - // Load in default viewer if not - else { - LOG.w(TAG, "showWebPage: Cannot load URL into webview since it is not in white list. Loading into browser instead. (URL="+url+")"); - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - mCtx.startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error loading url "+url, e); - } - } - } - - // Load in default view intent - else { - try { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setData(Uri.parse(url)); - mCtx.startActivity(intent); - } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error loading url "+url, e); - } - } - } - - /** - * Load Cordova configuration from res/xml/cordova.xml. - * Approved list of URLs that can be loaded into DroidGap - * - * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR) - * - */ - private void loadConfiguration() { - int id = getResources().getIdentifier("cordova", "xml", mCtx.getPackageName()); - if (id == 0) { - LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); - return; - } - XmlResourceParser xml = getResources().getXml(id); - int eventType = -1; - while (eventType != XmlResourceParser.END_DOCUMENT) { - if (eventType == XmlResourceParser.START_TAG) { - String strNode = xml.getName(); - if (strNode.equals("access")) { - String origin = xml.getAttributeValue(null, "origin"); - String subdomains = xml.getAttributeValue(null, "subdomains"); - if (origin != null) { - addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); - } - } - else if (strNode.equals("log")) { - String level = xml.getAttributeValue(null, "level"); - LOG.i("CordovaLog", "Found log level %s", level); - if (level != null) { - LOG.setLogLevel(level); - } - } - else if (strNode.equals("preference")) { - String name = xml.getAttributeValue(null, "name"); - String value = xml.getAttributeValue(null, "value"); - String readonlyString = xml.getAttributeValue(null, "readonly"); - - boolean readonly = (readonlyString != null && - readonlyString.equals("true")); - - LOG.i("CordovaLog", "Found preference for %s", name); - - preferences.add(new PreferenceNode(name, value, readonly)); - } - } - try { - eventType = xml.next(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - + /** + * Get string property for activity. + * + * @param name + * @param defaultValue + * @return + */ + public String getProperty(String name, String defaultValue) { + Bundle bundle = this.mCtx.getActivity().getIntent().getExtras(); + if (bundle == null) { + return defaultValue; + } + Object p = bundle.get(name); + if (p == null) { + return defaultValue; + } + return p.toString(); + } } diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index f809b23f..21af0000 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -18,10 +18,15 @@ */ package org.apache.cordova; -import org.apache.cordova.api.LOG; +import java.util.Hashtable; -import android.app.Activity; -import android.content.Context; +import org.apache.cordova.api.CordovaInterface; +import org.apache.cordova.api.LOG; +import org.json.JSONException; +import org.json.JSONObject; + +//import android.app.Activity; +//import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -29,7 +34,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; -import android.util.Log; +//import android.util.Log; import android.view.View; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; @@ -40,32 +45,44 @@ import android.webkit.WebViewClient; * This class is the WebViewClient that implements callbacks for our web view. */ public class CordovaWebViewClient extends WebViewClient { - + private static final String TAG = "Cordova"; - Activity ctx; + CordovaInterface ctx; CordovaWebView appView; private boolean doClearHistory = false; + /** The authorization tokens. */ + private Hashtable authenticationTokens = new Hashtable(); + /** * Constructor. * * @param ctx */ - public CordovaWebViewClient(Activity ctx) { + public CordovaWebViewClient(CordovaInterface ctx) { this.ctx = ctx; } - - public CordovaWebViewClient(Context ctx, CordovaWebView view) - { - this.ctx = (Activity) ctx; - appView = view; + + /** + * Constructor. + * + * @param ctx + * @param view + */ + public CordovaWebViewClient(CordovaInterface ctx, CordovaWebView view) { + this.ctx = ctx; + this.appView = view; } - - public void setWebView(CordovaWebView view) - { - appView = view; + + /** + * Constructor. + * + * @param view + */ + public void setWebView(CordovaWebView view) { + this.appView = view; } - + /** * Give the host application a chance to take over the control when a new url * is about to be loaded in the current WebView. @@ -76,19 +93,19 @@ public class CordovaWebViewClient extends WebViewClient { */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - + // First give any plugins the chance to handle the url themselves - if ((appView.pluginManager != null) && appView.pluginManager.onOverrideUrlLoading(url)) { + if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) { } - + // If dialing phone (tel:5551212) else if (url.startsWith(WebView.SCHEME_TEL)) { try { Intent intent = new Intent(Intent.ACTION_DIAL); intent.setData(Uri.parse(url)); - ctx.startActivity(intent); + this.ctx.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error dialing "+url+": "+ e.toString()); + LOG.e(TAG, "Error dialing " + url + ": " + e.toString()); } } @@ -97,9 +114,9 @@ public class CordovaWebViewClient extends WebViewClient { try { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); - ctx.startActivity(intent); + this.ctx.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error showing map "+url+": "+ e.toString()); + LOG.e(TAG, "Error showing map " + url + ": " + e.toString()); } } @@ -108,9 +125,9 @@ public class CordovaWebViewClient extends WebViewClient { try { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); - ctx.startActivity(intent); + this.ctx.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error sending email "+url+": "+ e.toString()); + LOG.e(TAG, "Error sending email " + url + ": " + e.toString()); } } @@ -137,12 +154,12 @@ public class CordovaWebViewClient extends WebViewClient { } } } - intent.setData(Uri.parse("sms:"+address)); + intent.setData(Uri.parse("sms:" + address)); intent.putExtra("address", address); intent.setType("vnd.android-dir/mms-sms"); - ctx.startActivity(intent); + this.ctx.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error sending sms "+url+":"+ e.toString()); + LOG.e(TAG, "Error sending sms " + url + ":" + e.toString()); } } @@ -151,12 +168,12 @@ public class CordovaWebViewClient extends WebViewClient { // If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity. // Our app continues to run. When BACK is pressed, our app is redisplayed. - if (url.startsWith("file://") || url.indexOf(appView.baseUrl) == 0 || appView.isUrlWhiteListed(url)) { + if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) { //This will fix iFrames - if(appView.useBrowserHistory) - return false; + if (appView.useBrowserHistory) + return false; else - appView.loadUrl(url); + this.appView.loadUrl(url); } // If not our application, let default viewer handle @@ -164,61 +181,56 @@ public class CordovaWebViewClient extends WebViewClient { try { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(url)); - ctx.startActivity(intent); + this.ctx.getActivity().startActivity(intent); } catch (android.content.ActivityNotFoundException e) { - LOG.e(TAG, "Error loading url "+url, e); + LOG.e(TAG, "Error loading url " + url, e); } } } return true; } - + /** * On received http auth request. * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination * * @param view - * the view * @param handler - * the handler * @param host - * the host * @param realm - * the realm */ @Override - public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, - String realm) { - - // get the authentication token - // Note: The WebView MUST be a CordoaWebView - AuthenticationToken token = ((CordovaWebView) view).getAuthenticationToken(host,realm); - - if(token != null) { + public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { + + // Get the authentication token + AuthenticationToken token = this.getAuthenticationToken(host, realm); + if (token != null) { handler.proceed(token.getUserName(), token.getPassword()); } } - @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { // Clear history so history.back() doesn't do anything. - // So we can reinit() native side CallbackServer & PluginManager.\ - if(!appView.useBrowserHistory) - { - view.clearHistory(); - this.doClearHistory = true; + // So we can reinit() native side CallbackServer & PluginManager. + if (!this.appView.useBrowserHistory) { + view.clearHistory(); + this.doClearHistory = true; } + // Create callback server and plugin manager - if (appView.callbackServer == null) { - appView.callbackServer = new CallbackServer(); - appView.callbackServer.init(url); + if (this.appView.callbackServer == null) { + this.appView.callbackServer = new CallbackServer(); + this.appView.callbackServer.init(url); } else { - appView.callbackServer.reinit(url); + this.appView.callbackServer.reinit(url); } + + // Broadcast message that page has loaded + this.appView.postMessage("onPageStarted", url); } - + /** * Notify the host application that a page has finished loading. * @@ -241,26 +253,29 @@ public class CordovaWebViewClient extends WebViewClient { } // Clear timeout flag - appView.loadUrlTimeout++; + this.appView.loadUrlTimeout++; // Try firing the onNativeReady event in JS. If it fails because the JS is // not loaded yet then just set a flag so that the onNativeReady can be fired // from the JS side when the JS gets to that code. if (!url.equals("about:blank")) { - appView.loadUrl("javascript:try{ cordova.require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}"); - appView.postMessage("onNativeReady", null); + this.appView.loadUrl("javascript:try{ cordova.require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}"); + this.appView.postMessage("onNativeReady", null); } + // Broadcast message that page has loaded + this.appView.postMessage("onPageFinished", url); + // Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly - if (appView.getVisibility() == View.INVISIBLE) { + if (this.appView.getVisibility() == View.INVISIBLE) { Thread t = new Thread(new Runnable() { public void run() { try { Thread.sleep(2000); - ctx.runOnUiThread(new Runnable() { + ctx.getActivity().runOnUiThread(new Runnable() { public void run() { - appView.setVisibility(View.VISIBLE); - //ctx.spinnerStop(); + //appView.setVisibility(View.VISIBLE); + appView.postMessage("spinner", "stop"); } }); } catch (InterruptedException e) { @@ -270,17 +285,16 @@ public class CordovaWebViewClient extends WebViewClient { t.start(); } - // Shutdown if blank loaded if (url.equals("about:blank")) { - if (appView.callbackServer != null) { - appView.callbackServer.destroy(); + if (this.appView.callbackServer != null) { + this.appView.callbackServer.destroy(); } //this.ctx.endActivity(); - this.ctx.finish(); + this.ctx.getActivity().finish(); } } - + /** * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). * The errorCode parameter corresponds to one of the ERROR_* constants. @@ -292,22 +306,32 @@ public class CordovaWebViewClient extends WebViewClient { */ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - LOG.d(TAG, "DroidGap: GapViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl); + LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl); // Clear timeout flag - //this.ctx.loadUrlTimeout++; + this.appView.loadUrlTimeout++; // Stop "app loading" spinner if showing //this.ctx.spinnerStop(); // Handle error //this.ctx.onReceivedError(errorCode, description, failingUrl); + JSONObject data = new JSONObject(); + try { + data.put("errorCode", errorCode); + data.put("description", description); + data.put("url", failingUrl); + } catch (JSONException e) { + e.printStackTrace(); + } + this.appView.postMessage("onReceivedError", data); } - + + @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { - - final String packageName = this.ctx.getPackageName(); - final PackageManager pm = this.ctx.getPackageManager(); + + final String packageName = this.ctx.getActivity().getPackageName(); + final PackageManager pm = this.ctx.getActivity().getPackageManager(); ApplicationInfo appInfo; try { appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA); @@ -317,7 +341,7 @@ public class CordovaWebViewClient extends WebViewClient { return; } else { // debug = false - super.onReceivedSslError(view, handler, error); + super.onReceivedSslError(view, handler, error); } } catch (NameNotFoundException e) { // When it doubt, lock it out! @@ -331,8 +355,81 @@ public class CordovaWebViewClient extends WebViewClient { * If you do a document.location.href the url does not get pushed on the stack * so we do a check here to see if the url should be pushed. */ - if (!appView.peekAtUrlStack().equals(url)) { - appView.pushUrl(url); + if (!this.appView.peekAtUrlStack().equals(url)) { + this.appView.pushUrl(url); } } + + /** + * Sets the authentication token. + * + * @param authenticationToken + * @param host + * @param realm + */ + public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) { + if (host == null) { + host = ""; + } + if (realm == null) { + realm = ""; + } + this.authenticationTokens.put(host.concat(realm), authenticationToken); + } + + /** + * Removes the authentication token. + * + * @param host + * @param realm + * + * @return the authentication token or null if did not exist + */ + public AuthenticationToken removeAuthenticationToken(String host, String realm) { + return this.authenticationTokens.remove(host.concat(realm)); + } + + /** + * Gets the authentication token. + * + * In order it tries: + * 1- host + realm + * 2- host + * 3- realm + * 4- no host, no realm + * + * @param host + * @param realm + * + * @return the authentication token + */ + public AuthenticationToken getAuthenticationToken(String host, String realm) { + AuthenticationToken token = null; + token = this.authenticationTokens.get(host.concat(realm)); + + if (token == null) { + // try with just the host + token = this.authenticationTokens.get(host); + + // Try the realm + if (token == null) { + token = this.authenticationTokens.get(realm); + } + + // if no host found, just query for default + if (token == null) { + token = this.authenticationTokens.get(""); + } + } + + return token; + } + + /** + * Clear all authentication tokens. + */ + public void clearAuthenticationTokens() { + this.authenticationTokens.clear(); + } + } diff --git a/framework/src/org/apache/cordova/Device.java b/framework/src/org/apache/cordova/Device.java index da15d492..2d67b95a 100644 --- a/framework/src/org/apache/cordova/Device.java +++ b/framework/src/org/apache/cordova/Device.java @@ -41,7 +41,7 @@ public class Device extends Plugin { public static String cordovaVersion = "1.7.0"; // Cordova version public static String platform = "Android"; // Device OS public static String uuid; // Device UUID - + BroadcastReceiver telephonyReceiver = null; /** @@ -49,14 +49,14 @@ public class Device extends Plugin { */ public Device() { } - + /** * Sets the context of the Command. This can then be used to do things like * get file paths associated with the Activity. * * @param ctx The context of the main Activity. */ - public void setContext(Context ctx) { + public void setContext(CordovaInterface ctx) { super.setContext(ctx); Device.uuid = getUuid(); this.initTelephonyReceiver(); @@ -72,8 +72,8 @@ public class Device extends Plugin { */ public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - + String result = ""; + try { if (action.equals("getDeviceInfo")) { JSONObject r = new JSONObject(); @@ -105,32 +105,32 @@ public class Device extends Plugin { } return false; } - + /** * Unregister receiver. */ public void onDestroy() { - this.ctx.unregisterReceiver(this.telephonyReceiver); + this.ctx.getActivity().unregisterReceiver(this.telephonyReceiver); } //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- - + /** * Listen for telephony events: RINGING, OFFHOOK and IDLE * Send these events to all plugins using * DroidGap.onMessage("telephone", "ringing" | "offhook" | "idle") */ private void initTelephonyReceiver() { - IntentFilter intentFilter = new IntentFilter() ; + IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); - final Context myctx = this.ctx; + //final CordovaInterface myctx = this.ctx; this.telephonyReceiver = new BroadcastReceiver() { - + @Override public void onReceive(Context context, Intent intent) { - + // If state has changed if ((intent != null) && intent.getAction().equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) { if (intent.hasExtra(TelephonyManager.EXTRA_STATE)) { @@ -151,9 +151,9 @@ public class Device extends Plugin { } } }; - + // Register the receiver - this.ctx.registerReceiver(this.telephonyReceiver, intentFilter); + this.ctx.getActivity().registerReceiver(this.telephonyReceiver, intentFilter); } /** @@ -164,14 +164,14 @@ public class Device extends Plugin { public String getPlatform() { return Device.platform; } - + /** * Get the device's Universally Unique Identifier (UUID). * * @return */ - public String getUuid() { - String uuid = Settings.Secure.getString(this.ctx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); + public String getUuid() { + String uuid = Settings.Secure.getString(this.ctx.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID); return uuid; } @@ -182,18 +182,18 @@ public class Device extends Plugin { */ public String getCordovaVersion() { return Device.cordovaVersion; - } - + } + public String getModel() { String model = android.os.Build.MODEL; return model; } - + public String getProductName() { String productname = android.os.Build.PRODUCT; return productname; } - + /** * Get the OS version. * @@ -203,17 +203,16 @@ public class Device extends Plugin { String osversion = android.os.Build.VERSION.RELEASE; return osversion; } - + public String getSDKVersion() { + @SuppressWarnings("deprecation") String sdkversion = android.os.Build.VERSION.SDK; return sdkversion; } - - - public String getTimeZoneID() { - TimeZone tz = TimeZone.getDefault(); - return(tz.getID()); - } - -} + public String getTimeZoneID() { + TimeZone tz = TimeZone.getDefault(); + return (tz.getID()); + } + +} diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 1c832c41..5be0fa9c 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -18,22 +18,24 @@ */ package org.apache.cordova; -import java.io.IOException; -import java.util.ArrayList; +//import java.io.IOException; +//import java.util.ArrayList; import java.util.HashMap; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Stack; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +//import java.util.Hashtable; +//import java.util.Iterator; +//import java.util.Stack; +//import java.util.regex.Matcher; +//import java.util.regex.Pattern; -import org.apache.cordova.PreferenceNode; -import org.apache.cordova.PreferenceSet; +//import org.apache.cordova.PreferenceNode; +//import org.apache.cordova.PreferenceSet; import org.apache.cordova.api.IPlugin; import org.apache.cordova.api.LOG; import org.apache.cordova.api.CordovaInterface; -import org.apache.cordova.api.PluginManager; -import org.xmlpull.v1.XmlPullParserException; +import org.json.JSONException; +import org.json.JSONObject; +//import org.apache.cordova.api.PluginManager; +//import org.xmlpull.v1.XmlPullParserException; import android.app.Activity; import android.app.AlertDialog; @@ -43,10 +45,10 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; -import android.content.res.XmlResourceParser; +//import android.content.res.XmlResourceParser; import android.graphics.Color; import android.media.AudioManager; -import android.net.Uri; +//import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.Display; @@ -57,15 +59,13 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.webkit.WebChromeClient; -import android.webkit.WebSettings; -import android.webkit.WebSettings.LayoutAlgorithm; -import android.webkit.WebView; +//import android.webkit.WebChromeClient; +//import android.webkit.WebSettings; +//import android.webkit.WebSettings.LayoutAlgorithm; +//import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; - - /** * This class is the main Android activity that represents the Cordova * application. It should be extended by the user to load the specific @@ -149,10 +149,10 @@ import android.widget.LinearLayout; */ public class DroidGap extends Activity implements CordovaInterface { public static String TAG = "DroidGap"; - + // The webview for our app protected CordovaWebView appView; - protected WebViewClient webViewClient; + protected CordovaWebViewClient webViewClient; protected LinearLayout root; public boolean bound = false; @@ -162,16 +162,16 @@ public class DroidGap extends Activity implements CordovaInterface { // The initial URL for our app // ie http://server/path/index.html#abc?query private String url = null; - private Stack urls = new Stack(); - + //private Stack urls = new Stack(); + // Url was specified from extras (activity was started programmatically) - private String initUrl = null; - + //private String initUrl = null; + private static int ACTIVITY_STARTING = 0; private static int ACTIVITY_RUNNING = 1; private static int ACTIVITY_EXITING = 2; private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down - + // The base of the initial URL for our app. // Does not include file name. Ends with / // ie http://server/path/ @@ -182,12 +182,12 @@ public class DroidGap extends Activity implements CordovaInterface { protected boolean activityResultKeepRunning; // Flag indicates that a loadUrl timeout occurred - int loadUrlTimeout = 0; - + //int loadUrlTimeout = 0; + // Default background color for activity // (this is not the color for the webview, which is set in HTML) private int backgroundColor = Color.BLACK; - + /* * The variables below are used to cache some of the activity properties. */ @@ -195,30 +195,89 @@ public class DroidGap extends Activity implements CordovaInterface { // Draw a splash screen using an image located in the drawable resource directory. // This is not the same as calling super.loadSplashscreen(url) protected int splashscreen = 0; + protected int splashscreenTime = 0; // LoadUrl timeout value in msec (default of 20 sec) - protected int loadUrlTimeoutValue = 20000; - + //protected int loadUrlTimeoutValue = 20000; + // Keep app running when pause is received. (default = true) // If true, then the JavaScript and native code continue to run in the background // when another application (activity) is started. protected boolean keepRunning = true; - + // Store the useBrowserHistory preference until we actually need it. - private boolean useBrowserHistory = false; - + //private boolean useBrowserHistory = false; + // preferences read from cordova.xml - protected PreferenceSet preferences; - - + //protected PreferenceSet preferences; + + /** + * Sets the authentication token. + * + * @param authenticationToken + * @param host + * @param realm + */ + public void setAuthenticationToken(AuthenticationToken authenticationToken, String host, String realm) { + if (this.appView != null && this.appView.viewClient != null) { + this.appView.viewClient.setAuthenticationToken(authenticationToken, host, realm); + } + } + + /** + * Removes the authentication token. + * + * @param host + * @param realm + * + * @return the authentication token or null if did not exist + */ + public AuthenticationToken removeAuthenticationToken(String host, String realm) { + if (this.appView != null && this.appView.viewClient != null) { + return this.appView.viewClient.removeAuthenticationToken(host, realm); + } + return null; + } + + /** + * Gets the authentication token. + * + * In order it tries: + * 1- host + realm + * 2- host + * 3- realm + * 4- no host, no realm + * + * @param host + * @param realm + * + * @return the authentication token + */ + public AuthenticationToken getAuthenticationToken(String host, String realm) { + if (this.appView != null && this.appView.viewClient != null) { + return this.appView.viewClient.getAuthenticationToken(host, realm); + } + return null; + } + + /** + * Clear all authentication tokens. + */ + public void clearAuthenticationTokens() { + if (this.appView != null && this.appView.viewClient != null) { + this.appView.viewClient.clearAuthenticationTokens(); + } + } + /** * Called when the activity is first created. * * @param savedInstanceState */ + @SuppressWarnings("deprecation") @Override public void onCreate(Bundle savedInstanceState) { - preferences = new PreferenceSet(); + //preferences = new PreferenceSet(); LOG.d(TAG, "DroidGap.onCreate()"); super.onCreate(savedInstanceState); @@ -226,38 +285,48 @@ public class DroidGap extends Activity implements CordovaInterface { getWindow().requestFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); - + WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + // This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket! - Display display = getWindowManager().getDefaultDisplay(); + Display display = getWindowManager().getDefaultDisplay(); int width = display.getWidth(); int height = display.getHeight(); - + root = new LinearLayoutSoftKeyboardDetect(this, width, height); root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(this.backgroundColor); - root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0F)); // If url was passed in to intent, then init webview, which will load the url - Bundle bundle = this.getIntent().getExtras(); - if (bundle != null) { - String url = bundle.getString("url"); - if (url != null) { - this.initUrl = url; - } - } +// Bundle bundle = this.getIntent().getExtras(); +// if (bundle != null) { +// String url = bundle.getString("url"); +// if (url != null) { +// this.initUrl = url; +// } +// } // Setup the hardware volume controls to handle volume control setVolumeControlStream(AudioManager.STREAM_MUSIC); } - + + /** + * Get the Android activity. + * + * @return + */ + public Activity getActivity() { + return this; + } + /** * Create and initialize web container with default web view objects. */ public void init() { - this.init(new CordovaWebView(DroidGap.this), new CordovaWebViewClient(this), new CordovaChromeClient(DroidGap.this)); + CordovaWebView webView = new CordovaWebView(DroidGap.this); + this.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView)); } - + /** * Initialize web container with web view objects. * @@ -265,177 +334,201 @@ public class DroidGap extends Activity implements CordovaInterface { * @param webViewClient * @param webChromeClient */ - public void init(CordovaWebView webView, WebViewClient webViewClient, WebChromeClient webChromeClient) { + public void init(CordovaWebView webView, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient) { LOG.d(TAG, "DroidGap.init()"); - + // Set up web container - this.appView = webView; + this.appView = webView; this.appView.setId(100); - + + this.appView.setWebViewClient(webViewClient); + this.appView.setWebChromeClient(webChromeClient); + webViewClient.setWebView(this.appView); + webChromeClient.setWebView(this.appView); + // Load Cordova configuration: // white list of allowed URLs // debug setting - this.loadConfiguration(); + //this.loadConfiguration(); //Now we can check the preference - appView.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true"); - + //this.appView.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true"); + // - + this.appView.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, 1.0F)); // Add web view but make it invisible while loading URL this.appView.setVisibility(View.INVISIBLE); - root.addView(this.appView); - setContentView(root); - + this.root.addView(this.appView); + setContentView(this.root); + // Clear cancel flag this.cancelLoadUrl = false; - } - + /** * Look at activity parameters and process them. * This must be called from the main UI thread. */ - private void handleActivityParameters() { + //private void handleActivityParameters() { - // If backgroundColor - this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK); - this.root.setBackgroundColor(this.backgroundColor); + // If backgroundColor + //this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK); + //LOG.e(TAG, "Setting background color=" + this.backgroundColor); + //this.root.setBackgroundColor(this.backgroundColor); - // If spashscreen - this.splashscreen = this.getIntegerProperty("splashscreen", 0); + // If spashscreen + //this.splashscreen = this.getIntegerProperty("splashscreen", 0); + + // If loadUrlTimeoutValue + //int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0); + //if (timeout > 0) { + // this.loadUrlTimeoutValue = timeout; + //} + + // If keepRunning + //this.keepRunning = this.getBooleanProperty("keepRunning", true); + //} - // If loadUrlTimeoutValue - int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0); - if (timeout > 0) { - this.loadUrlTimeoutValue = timeout; - } - - // If keepRunning - this.keepRunning = this.getBooleanProperty("keepRunning", true); - } - /** * Load the url into the webview. * * @param url */ public void loadUrl(String url) { - - // If first page of app, then set URL to load to be the one passed in - if (this.initUrl == null || (this.urls.size() > 0)) { - this.loadUrlIntoView(url); - } - // Otherwise use the URL specified in the activity's extras bundle - else { - this.loadUrlIntoView(this.initUrl); + + // Init web view if not already done + if (this.appView == null) { + this.init(); } + + // Handle activity parameters + //this.handleActivityParameters(); + + // If backgroundColor + this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK); + LOG.e(TAG, "Setting background color=" + this.backgroundColor); + this.root.setBackgroundColor(this.backgroundColor); + + // If keepRunning + this.keepRunning = this.getBooleanProperty("keepRunning", true); + + // Then load the spinner + this.loadSpinner(); + + this.appView.loadUrl(url); + +// // If first page of app, then set URL to load to be the one passed in +// if (this.initUrl == null || (this.urls.size() > 0)) { +// this.loadUrlIntoView(url); +// } +// // Otherwise use the URL specified in the activity's extras bundle +// else { +// this.loadUrlIntoView(this.initUrl); +// } } - + /* * Load the spinner - * */ - void loadSpinner() - { - // If loadingDialog property, then show the App loading dialog for first page of app - // (This doesn't seem to actually do anything here) - String loading = null; - if (this.urls.size() == 1) { - loading = this.getStringProperty("loadingDialog", null); - } - else { - loading = this.getStringProperty("loadingPageDialog", null); - } - if (loading != null) { + void loadSpinner() { - String title = ""; - String message = "Loading Application..."; + // If loadingDialog property, then show the App loading dialog for first page of app + String loading = null; + if ((this.appView == null) || !this.appView.canGoBack()) { + loading = this.getStringProperty("loadingDialog", null); + } + else { + loading = this.getStringProperty("loadingPageDialog", null); + } + if (loading != null) { - if (loading.length() > 0) { - int comma = loading.indexOf(','); - if (comma > 0) { - title = loading.substring(0, comma); - message = loading.substring(comma+1); - } - else { - title = ""; - message = loading; - } - } - this.spinnerStart(title, message); - } + String title = ""; + String message = "Loading Application..."; + + if (loading.length() > 0) { + int comma = loading.indexOf(','); + if (comma > 0) { + title = loading.substring(0, comma); + message = loading.substring(comma + 1); + } + else { + title = ""; + message = loading; + } + } + this.spinnerStart(title, message); + } } - + /** * Load the url into the webview. * * @param url */ - private void loadUrlIntoView(final String url) { - if (!url.startsWith("javascript:")) { - LOG.d(TAG, "DroidGap.loadUrl(%s)", url); - } +// private void loadUrlIntoView(final String url) { +// if (!url.startsWith("javascript:")) { +// LOG.d(TAG, "DroidGap.loadUrl(%s)", url); +// } +// +// if (!url.startsWith("javascript:")) { +// LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl); +// } +// +// // Load URL on UI thread +// final DroidGap me = this; +// +//// final Runnable loadError = new Runnable() { +//// public void run() { +//// me.appView.stopLoading(); +//// LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient"); +//// appView.viewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url); +//// } +//// }; +// +// this.runOnUiThread(new Runnable() { +// public void run() { +// +// // Init web view if not already done +// if (me.appView == null) { +// me.init(); +// } +// +// // Handle activity parameters (TODO: Somehow abstract this) +// me.handleActivityParameters(); +// +// // Then load the spinner +// me.loadSpinner(); +// +//// // Create a timeout timer for loadUrl +//// final int currentLoadUrlTimeout = me.loadUrlTimeout; +//// Runnable runnable = new Runnable() { +//// public void run() { +//// try { +//// synchronized (this) { +//// wait(me.loadUrlTimeoutValue); +//// } +//// } catch (InterruptedException e) { +//// e.printStackTrace(); +//// } +//// +//// // If timeout, then stop loading and handle error +//// if (me.loadUrlTimeout == currentLoadUrlTimeout) { +//// me.runOnUiThread(loadError); +//// +//// } +//// } +//// }; +//// Thread thread = new Thread(runnable); +//// thread.start(); +// me.appView.loadUrl(url); +// } +// }); +// } - if (!url.startsWith("javascript:")) { - LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl); - } - - // Load URL on UI thread - final DroidGap me = this; - - final Runnable loadError = new Runnable() { - public void run() { - me.appView.stopLoading(); - LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient"); - appView.viewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url); - } - }; - - this.runOnUiThread(new Runnable() { - public void run() { - - // Init web view if not already done - if (me.appView == null) { - me.init(); - } - - // Handle activity parameters (TODO: Somehow abstract this) - me.handleActivityParameters(); - - // Then load the spinner - me.loadSpinner(); - - // Create a timeout timer for loadUrl - final int currentLoadUrlTimeout = me.loadUrlTimeout; - Runnable runnable = new Runnable() { - public void run() { - try { - synchronized(this) { - wait(me.loadUrlTimeoutValue); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - - // If timeout, then stop loading and handle error - if (me.loadUrlTimeout == currentLoadUrlTimeout) { - me.runOnUiThread(loadError); - - } - } - }; - Thread thread = new Thread(runnable); - thread.start(); - me.appView.loadUrl(url); - } - }); - } - /** * Load the url into the webview after waiting for period of time. * This is used to display the splashscreen for certain amount of time. @@ -444,15 +537,23 @@ public class DroidGap extends Activity implements CordovaInterface { * @param time The number of ms to wait before loading webview */ public void loadUrl(final String url, int time) { - - // If first page of app, then set URL to load to be the one passed in - if (this.initUrl == null || (this.urls.size() > 0)) { - this.loadUrlIntoView(url, time); - } - // Otherwise use the URL specified in the activity's extras bundle - else { - this.loadUrlIntoView(this.initUrl); + + // Init web view if not already done + if (this.appView == null) { + this.init(); } + + this.splashscreenTime = time; + this.appView.loadUrl(url, time); + +// // If first page of app, then set URL to load to be the one passed in +// if (this.initUrl == null || (this.urls.size() > 0)) { +// this.loadUrlIntoView(url, time); +// } +// // Otherwise use the URL specified in the activity's extras bundle +// else { +// this.loadUrlIntoView(this.initUrl); +// } } /** @@ -460,36 +561,37 @@ public class DroidGap extends Activity implements CordovaInterface { * This is used to display the splashscreen for certain amount of time. * * @param url - * @param time The number of ms to wait before loading webview + * @param time The number of ms to wait before showing webview */ - private void loadUrlIntoView(final String url, final int time) { +// private void loadUrlIntoView(final String url, final int time) { +// +// // Clear cancel flag +// this.cancelLoadUrl = false; +// +// // If not first page of app, then load immediately +// if (this.urls.size() > 0) { +// this.loadUrlIntoView(url); +// } +// +// if (!url.startsWith("javascript:")) { +// LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time); +// } +// +// this.handleActivityParameters(); +// if (this.splashscreen != 0) { +// this.showSplashScreen(time); +// } +// this.loadUrlIntoView(url); +// } - // Clear cancel flag - this.cancelLoadUrl = false; - - // If not first page of app, then load immediately - if (this.urls.size() > 0) { - this.loadUrlIntoView(url); - } - - if (!url.startsWith("javascript:")) { - LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time); - } - - this.handleActivityParameters(); - if (this.splashscreen != 0) { - this.showSplashScreen(time); - } - this.loadUrlIntoView(url); - } - /** * Cancel loadUrl before it has been loaded. */ + // TODO NO-OP public void cancelLoadUrl() { this.cancelLoadUrl = true; } - + /** * Clear the resource cache. */ @@ -504,16 +606,27 @@ public class DroidGap extends Activity implements CordovaInterface { * Clear web history in this web view. */ public void clearHistory() { - this.urls.clear(); this.appView.clearHistory(); - - // Leave current url on history stack - if (this.url != null) { - this.urls.push(this.url); - } +// this.urls.clear(); +// this.appView.clearHistory(); +// +// // Leave current url on history stack +// if (this.url != null) { +// this.urls.push(this.url); +// } + } + + /** + * Go to previous page in history. (We manage our own history) + * + * @return true if we went back, false if we are already at top + */ + public boolean backHistory() { + if (this.appView != null) { + return appView.backHistory(); + } + return false; } - - @Override /** @@ -525,7 +638,7 @@ public class DroidGap extends Activity implements CordovaInterface { //don't reload the current page when the orientation is changed super.onConfigurationChanged(newConfig); } - + /** * Get boolean property for activity. * @@ -538,7 +651,7 @@ public class DroidGap extends Activity implements CordovaInterface { if (bundle == null) { return defaultValue; } - Boolean p = (Boolean)bundle.get(name); + Boolean p = (Boolean) bundle.get(name); if (p == null) { return defaultValue; } @@ -557,7 +670,7 @@ public class DroidGap extends Activity implements CordovaInterface { if (bundle == null) { return defaultValue; } - Integer p = (Integer)bundle.get(name); + Integer p = (Integer) bundle.get(name); if (p == null) { return defaultValue; } @@ -595,7 +708,7 @@ public class DroidGap extends Activity implements CordovaInterface { if (bundle == null) { return defaultValue; } - Double p = (Double)bundle.get(name); + Double p = (Double) bundle.get(name); if (p == null) { return defaultValue; } @@ -611,7 +724,7 @@ public class DroidGap extends Activity implements CordovaInterface { public void setBooleanProperty(String name, boolean value) { this.getIntent().putExtra(name, value); } - + /** * Set int property on activity. * @@ -621,7 +734,7 @@ public class DroidGap extends Activity implements CordovaInterface { public void setIntegerProperty(String name, int value) { this.getIntent().putExtra(name, value); } - + /** * Set string property on activity. * @@ -648,7 +761,7 @@ public class DroidGap extends Activity implements CordovaInterface { */ protected void onPause() { super.onPause(); - + // Don't process pause if shutting down, since onDestroy() will be called if (this.activityState == ACTIVITY_EXITING) { return; @@ -662,8 +775,10 @@ public class DroidGap extends Activity implements CordovaInterface { this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onPause.fire();}catch(e){console.log('exception firing pause event from native');};"); // Forward to plugins - appView.pluginManager.onPause(this.keepRunning); - + if (this.appView.pluginManager != null) { + this.appView.pluginManager.onPause(this.keepRunning); + } + // If app doesn't want to run in background if (!this.keepRunning) { @@ -680,16 +795,18 @@ public class DroidGap extends Activity implements CordovaInterface { super.onNewIntent(intent); //Forward to plugins - appView.pluginManager.onNewIntent(intent); + if ((this.appView != null) && (this.appView.pluginManager != null)) { + this.appView.pluginManager.onNewIntent(intent); + } } - + @Override /** * Called when the activity will start interacting with the user. */ protected void onResume() { super.onResume(); - + if (this.activityState == ACTIVITY_STARTING) { this.activityState = ACTIVITY_RUNNING; return; @@ -703,8 +820,10 @@ public class DroidGap extends Activity implements CordovaInterface { this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onResume.fire();}catch(e){console.log('exception firing resume event from native');};"); // Forward to plugins - appView.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning); - + if (this.appView.pluginManager != null) { + this.appView.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning); + } + // If app doesn't want to run in background if (!this.keepRunning || this.activityResultKeepRunning) { @@ -715,19 +834,20 @@ public class DroidGap extends Activity implements CordovaInterface { } // Resume JavaScript timers (including setInterval) - this.appView.resumeTimers(); + if (this.appView.pluginManager != null) { + this.appView.resumeTimers(); + } } } - + @Override /** * The final call you receive before your activity is destroyed. */ public void onDestroy() { super.onDestroy(); - - if (this.appView != null) { + if (this.appView != null) { // Send destroy event to JavaScript this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};"); @@ -736,7 +856,9 @@ public class DroidGap extends Activity implements CordovaInterface { this.appView.loadUrl("about:blank"); // Forward to plugins - appView.pluginManager.onDestroy(); + if (this.appView.pluginManager != null) { + this.appView.pluginManager.onDestroy(); + } } else { this.endActivity(); @@ -750,9 +872,9 @@ public class DroidGap extends Activity implements CordovaInterface { * @param data The message data */ public void postMessage(String id, Object data) { - - // Forward to plugins - appView.pluginManager.postMessage(id, data); + if (this.appView != null) { + this.appView.postMessage(id, data); + } } /** @@ -764,11 +886,12 @@ public class DroidGap extends Activity implements CordovaInterface { * @param serviceType * @param className */ - @Deprecated public void addService(String serviceType, String className) { - appView.pluginManager.addService(serviceType, className); + if (this.appView != null && this.appView.pluginManager != null) { + this.appView.pluginManager.addService(serviceType, className); + } } - + /** * Send JavaScript statement back to JavaScript. * (This is a convenience method) @@ -776,9 +899,10 @@ public class DroidGap extends Activity implements CordovaInterface { * @param message */ public void sendJavascript(String statement) { - //We need to check for the null case on the Kindle Fire beacuse it changes the width and height on load - if(this.appView.callbackServer != null) - appView.callbackServer.sendJavascript(statement); + //We need to check for the null case on the Kindle Fire because it changes the width and height on load + if (this.appView != null && this.appView.callbackServer != null) { + this.appView.callbackServer.sendJavascript(statement); + } } /** @@ -793,12 +917,12 @@ public class DroidGap extends Activity implements CordovaInterface { this.spinnerDialog = null; } final DroidGap me = this; - this.spinnerDialog = ProgressDialog.show(DroidGap.this, title , message, true, true, - new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - me.spinnerDialog = null; - } - }); + this.spinnerDialog = ProgressDialog.show(DroidGap.this, title, message, true, true, + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + me.spinnerDialog = null; + } + }); } /** @@ -810,17 +934,25 @@ public class DroidGap extends Activity implements CordovaInterface { this.spinnerDialog = null; } } - + /** * End this activity by calling finish for activity */ public void endActivity() { - this.activityState = ACTIVITY_EXITING; + //this.activityState = ACTIVITY_EXITING; this.finish(); } - + /** - * Called when a key is de-pressed. (Key UP) + * Finish for Activity has been called. + */ + public void finish() { + this.activityState = ACTIVITY_EXITING; + super.finish(); + } + + /** + * Called when a key is released. (Key UP) * * @param keyCode * @param event @@ -877,11 +1009,11 @@ public class DroidGap extends Activity implements CordovaInterface { * * @throws RuntimeException */ - @Override - public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException { - LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode); - super.startActivityForResult(intent, requestCode); - } + //@Override + //public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException { + // LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode); + // super.startActivityForResult(intent, requestCode); + //} /** * Launch an activity for which you would like a result when it finished. When this activity exits, @@ -894,17 +1026,17 @@ public class DroidGap extends Activity implements CordovaInterface { public void startActivityForResult(IPlugin command, Intent intent, int requestCode) { this.activityResultCallback = command; this.activityResultKeepRunning = this.keepRunning; - + // If multitasking turned on, then disable it for activities that return results if (command != null) { this.keepRunning = false; } - + // Start activity super.startActivityForResult(intent, requestCode); } - @Override + @Override /** * Called when an activity you launched exits, giving you the requestCode you started it with, * the resultCode it returned, and any additional data from it. @@ -914,87 +1046,89 @@ public class DroidGap extends Activity implements CordovaInterface { * @param resultCode The integer result code returned by the child activity through its setResult(). * @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras"). */ - protected void onActivityResult(int requestCode, int resultCode, Intent intent) { - super.onActivityResult(requestCode, resultCode, intent); - IPlugin callback = this.activityResultCallback; - if (callback != null) { - callback.onActivityResult(requestCode, resultCode, intent); - } - } + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + IPlugin callback = this.activityResultCallback; + if (callback != null) { + callback.onActivityResult(requestCode, resultCode, intent); + } + } - public void setActivityResultCallback(IPlugin plugin) { - this.activityResultCallback = plugin; - } + public void setActivityResultCallback(IPlugin plugin) { + this.activityResultCallback = plugin; + } - /** - * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). - * The errorCode parameter corresponds to one of the ERROR_* constants. - * - * @param errorCode The error code corresponding to an ERROR_* value. - * @param description A String describing the error. - * @param failingUrl The url that failed to load. - */ - public void onReceivedError(final int errorCode, final String description, final String failingUrl) { - final DroidGap me = this; + /** + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * The errorCode parameter corresponds to one of the ERROR_* constants. + * + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + */ + public void onReceivedError(final int errorCode, final String description, final String failingUrl) { + final DroidGap me = this; - // If errorUrl specified, then load it - final String errorUrl = me.getStringProperty("errorUrl", null); - if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { + // Stop "app loading" spinner if showing + this.spinnerStop(); - // Load URL on UI thread - me.runOnUiThread(new Runnable() { - public void run() { - me.showWebPage(errorUrl, false, true, null); - } - }); - } - // If not, then display error dialog - else { - final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP); - me.runOnUiThread(new Runnable() { - public void run() { - if(exit) - { - me.appView.setVisibility(View.GONE); - me.displayError("Application Error", description + " ("+failingUrl+")", "OK", exit); - } - } - }); - } - } + // If errorUrl specified, then load it + final String errorUrl = me.getStringProperty("errorUrl", null); + if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) { + + // Load URL on UI thread + me.runOnUiThread(new Runnable() { + public void run() { + me.appView.showWebPage(errorUrl, false, true, null); + } + }); + } + // If not, then display error dialog + else { + final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP); + me.runOnUiThread(new Runnable() { + public void run() { + if (exit) + { + me.appView.setVisibility(View.GONE); + me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit); + } + } + }); + } + } + + /** + * Display an error dialog and optionally exit application. + * + * @param title + * @param message + * @param button + * @param exit + */ + public void displayError(final String title, final String message, final String button, final boolean exit) { + final DroidGap me = this; + me.runOnUiThread(new Runnable() { + public void run() { + AlertDialog.Builder dlg = new AlertDialog.Builder(me); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(false); + dlg.setPositiveButton(button, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (exit) { + me.endActivity(); + } + } + }); + dlg.create(); + dlg.show(); + } + }); + } - /** - * Display an error dialog and optionally exit application. - * - * @param title - * @param message - * @param button - * @param exit - */ - public void displayError(final String title, final String message, final String button, final boolean exit) { - final DroidGap me = this; - me.runOnUiThread(new Runnable() { - public void run() { - AlertDialog.Builder dlg = new AlertDialog.Builder(me); - dlg.setMessage(message); - dlg.setTitle(title); - dlg.setCancelable(false); - dlg.setPositiveButton(button, - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - if (exit) { - me.endActivity(); - } - } - }); - dlg.create(); - dlg.show(); - } - }); - } - - /** * Load Cordova configuration from res/xml/cordova.xml. * Approved list of URLs that can be loaded into DroidGap @@ -1002,55 +1136,53 @@ public class DroidGap extends Activity implements CordovaInterface { * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR) * */ - private void loadConfiguration() { - int id = getResources().getIdentifier("cordova", "xml", getPackageName()); - if (id == 0) { - LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); - return; - } - XmlResourceParser xml = getResources().getXml(id); - int eventType = -1; - while (eventType != XmlResourceParser.END_DOCUMENT) { - if (eventType == XmlResourceParser.START_TAG) { - String strNode = xml.getName(); - if (strNode.equals("access")) { - String origin = xml.getAttributeValue(null, "origin"); - String subdomains = xml.getAttributeValue(null, "subdomains"); - if (origin != null) { - appView.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); - } - } - else if (strNode.equals("log")) { - String level = xml.getAttributeValue(null, "level"); - LOG.i("CordovaLog", "Found log level %s", level); - if (level != null) { - LOG.setLogLevel(level); - } - } - else if (strNode.equals("preference")) { - String name = xml.getAttributeValue(null, "name"); - String value = xml.getAttributeValue(null, "value"); - String readonlyString = xml.getAttributeValue(null, "readonly"); - - boolean readonly = (readonlyString != null && - readonlyString.equals("true")); - - LOG.i("CordovaLog", "Found preference for %s", name); - - preferences.add(new PreferenceNode(name, value, readonly)); - } - } - try { - eventType = xml.next(); - } catch (XmlPullParserException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - +// private void loadConfiguration() { +// int id = getResources().getIdentifier("cordova", "xml", getPackageName()); +// if (id == 0) { +// LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); +// return; +// } +// XmlResourceParser xml = getResources().getXml(id); +// int eventType = -1; +// while (eventType != XmlResourceParser.END_DOCUMENT) { +// if (eventType == XmlResourceParser.START_TAG) { +// String strNode = xml.getName(); +// if (strNode.equals("access")) { +// String origin = xml.getAttributeValue(null, "origin"); +// String subdomains = xml.getAttributeValue(null, "subdomains"); +// if (origin != null) { +// appView.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); +// } +// } +// else if (strNode.equals("log")) { +// String level = xml.getAttributeValue(null, "level"); +// LOG.i("CordovaLog", "Found log level %s", level); +// if (level != null) { +// LOG.setLogLevel(level); +// } +// } +// else if (strNode.equals("preference")) { +// String name = xml.getAttributeValue(null, "name"); +// String value = xml.getAttributeValue(null, "value"); +// String readonlyString = xml.getAttributeValue(null, "readonly"); +// +// boolean readonly = (readonlyString != null && +// readonlyString.equals("true")); +// +// LOG.i("CordovaLog", "Found preference for %s", name); +// +// preferences.add(new PreferenceNode(name, value, readonly)); +// } +// } +// try { +// eventType = xml.next(); +// } catch (XmlPullParserException e) { +// e.printStackTrace(); +// } catch (IOException e) { +// e.printStackTrace(); +// } +// } +// } /** * Determine if URL is in approved list of URLs to load. @@ -1060,66 +1192,79 @@ public class DroidGap extends Activity implements CordovaInterface { */ public boolean isUrlWhiteListed(String url) { // Check to see if we have matched url previously - return this.appView.isUrlWhiteListed(url); + if (this.appView != null) { + return this.appView.isUrlWhiteListed(url); + } + return false; } - - /* - * URL stack manipulators - */ - - - + /* * Hook in DroidGap for menu plugins * */ - @Override - public boolean onCreateOptionsMenu(Menu menu) - { + public boolean onCreateOptionsMenu(Menu menu) { this.postMessage("onCreateOptionsMenu", menu); return super.onCreateOptionsMenu(menu); } - + @Override - public boolean onPrepareOptionsMenu(Menu menu) - { + public boolean onPrepareOptionsMenu(Menu menu) { this.postMessage("onPrepareOptionsMenu", menu); return super.onPrepareOptionsMenu(menu); } - + @Override - public boolean onOptionsItemSelected(MenuItem item) - { + public boolean onOptionsItemSelected(MenuItem item) { this.postMessage("onOptionsItemSelected", item); return true; } + /** + * Get Activity context. + * + * @return + */ public Context getContext() { - return this; + return this.getContext(); } + /** + * Override the backbutton. + * + * @param override + */ public void bindBackButton(boolean override) { - // TODO Auto-generated method stub - this.bound = override; + this.bound = override; } + /** + * Determine of backbutton is overridden. + * + * @return + */ public boolean isBackButtonBound() { - // TODO Auto-generated method stub - return this.bound; + return this.bound; } - public boolean backHistory() { - return appView.backHistory(); - } - - public void showWebPage(String url, boolean openExternal, - boolean clearHistory, HashMap params) { - appView.showWebPage(url, openExternal, clearHistory, params); + /** + * Load the specified URL in the Cordova webview or a new browser instance. + * + * NOTE: If openExternal is false, only URLs listed in whitelist can be loaded. + * + * @param url The url to load. + * @param openExternal Load url in browser instead of Cordova webview. + * @param clearHistory Clear the history stack, so new page becomes top of history + * @param params DroidGap parameters for new app + */ + public void showWebPage(String url, boolean openExternal, boolean clearHistory, HashMap params) { + if (this.appView != null) { + appView.showWebPage(url, openExternal, clearHistory, params); + } } protected Dialog splashDialog; - + /** * Removes the Dialog that displays the splash screen */ @@ -1129,36 +1274,72 @@ public class DroidGap extends Activity implements CordovaInterface { splashDialog = null; } } - + /** * Shows the splash screen over the full Activity */ + @SuppressWarnings("deprecation") protected void showSplashScreen(int time) { + // Get reference to display Display display = getWindowManager().getDefaultDisplay(); - + // Create the layout for the dialog LinearLayout root = new LinearLayout(this); root.setMinimumHeight(display.getHeight()); root.setMinimumWidth(display.getWidth()); root.setOrientation(LinearLayout.VERTICAL); root.setBackgroundColor(this.getIntegerProperty("backgroundColor", Color.BLACK)); - root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0F)); root.setBackgroundResource(this.splashscreen); // Create and show the dialog - splashDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar); + splashDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar); splashDialog.setContentView(root); splashDialog.setCancelable(false); splashDialog.show(); - + // Set Runnable to remove splash screen just in case final Handler handler = new Handler(); handler.postDelayed(new Runnable() { - public void run() { - removeSplashScreen(); - } + public void run() { + removeSplashScreen(); + } }, time); } + + /** + * Called when a message is sent to plugin. + * + * @param id The message id + * @param data The message data + */ + public void onMessage(String id, Object data) { + LOG.d(TAG, "onMessage(" + id + "," + data + ")"); + if ("splashscreen".equals(id)) { + if ("hide".equals(data.toString())) { + this.removeSplashScreen(); + } + else { + this.splashscreen = this.getIntegerProperty("splashscreen", 0); + this.showSplashScreen(this.splashscreenTime); + } + } + else if ("spinner".equals(id)) { + if ("stop".equals(data.toString())) { + this.spinnerStop(); + this.appView.setVisibility(View.VISIBLE); + } + } + else if ("onReceivedError".equals(id)) { + JSONObject d = (JSONObject) data; + try { + this.onReceivedError(d.getInt("errorCode"), d.getString("description"), d.getString("url")); + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + } diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java index 6eee05ac..c3456e0b 100644 --- a/framework/src/org/apache/cordova/FileTransfer.java +++ b/framework/src/org/apache/cordova/FileTransfer.java @@ -51,13 +51,12 @@ import android.net.Uri; import android.util.Log; import android.webkit.CookieManager; - 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 BOUNDRY = "*****"; + private static final String BOUNDRY = "*****"; public static int FILE_NOT_FOUND_ERR = 1; public static int INVALID_URL_ERR = 2; @@ -76,8 +75,7 @@ public class FileTransfer extends Plugin { try { source = args.getString(0); target = args.getString(1); - } - catch (JSONException e) { + } catch (JSONException e) { Log.d(LOG_TAG, "Missing source or target"); return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing source or target"); } @@ -151,11 +149,11 @@ public class FileTransfer extends Plugin { } public void checkClientTrusted(X509Certificate[] chain, - String authType) throws CertificateException { + String authType) throws CertificateException { } public void checkServerTrusted(X509Certificate[] chain, - String authType) throws CertificateException { + String authType) throws CertificateException { } } }; @@ -199,7 +197,7 @@ public class FileTransfer extends Plugin { */ private String getArgument(JSONArray args, int position, String defaultString) { String arg = defaultString; - if(args.length() >= position) { + if (args.length() >= position) { arg = args.optString(position); if (arg == null || "null".equals(arg)) { arg = defaultString; @@ -275,26 +273,26 @@ public class FileTransfer extends Plugin { // Use a post method. conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); - conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY); + conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDRY); // Handle the other headers try { - JSONObject headers = params.getJSONObject("headers"); - for (Iterator iter = headers.keys(); iter.hasNext();) - { - String headerKey = iter.next().toString(); - conn.setRequestProperty(headerKey, headers.getString(headerKey)); - } + JSONObject headers = params.getJSONObject("headers"); + for (@SuppressWarnings("rawtypes") + Iterator iter = headers.keys(); iter.hasNext();) + { + String headerKey = iter.next().toString(); + conn.setRequestProperty(headerKey, headers.getString(headerKey)); + } } catch (JSONException e1) { - // No headers to be manipulated! + // No headers to be manipulated! } - + // Set the cookies on the response String cookie = CookieManager.getInstance().getCookie(server); if (cookie != null) { conn.setRequestProperty("Cookie", cookie); } - /* * Store the non-file portions of the multipart data as a string, so that we can add it @@ -302,42 +300,42 @@ public class FileTransfer extends Plugin { */ String extraParams = ""; try { - for (Iterator iter = params.keys(); iter.hasNext();) { + for (@SuppressWarnings("rawtypes") + Iterator iter = params.keys(); iter.hasNext();) { Object key = iter.next(); - if(key.toString() != "headers") + if (key.toString() != "headers") { - extraParams += LINE_START + BOUNDRY + LINE_END; - extraParams += "Content-Disposition: form-data; name=\"" + key.toString() + "\";"; - extraParams += LINE_END + LINE_END; - extraParams += params.getString(key.toString()); - extraParams += LINE_END; + extraParams += LINE_START + BOUNDRY + LINE_END; + extraParams += "Content-Disposition: form-data; name=\"" + key.toString() + "\";"; + extraParams += LINE_END + LINE_END; + extraParams += params.getString(key.toString()); + extraParams += LINE_END; } } } catch (JSONException e) { Log.e(LOG_TAG, e.getMessage(), e); } - + extraParams += LINE_START + BOUNDRY + LINE_END; extraParams += "Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\""; - + String midParams = "\"" + LINE_END + "Content-Type: " + mimeType + LINE_END + LINE_END; String tailParams = LINE_END + LINE_START + BOUNDRY + LINE_START + LINE_END; - + // Should set this up as an option if (chunkedMode) { conn.setChunkedStreamingMode(maxBufferSize); } else { - int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length; - Log.d(LOG_TAG, "String Length: " + stringLength); - int fixedLength = (int) fileInputStream.getChannel().size() + stringLength; - Log.d(LOG_TAG, "Content Length: " + fixedLength); - conn.setFixedLengthStreamingMode(fixedLength); + int stringLength = extraParams.length() + midParams.length() + tailParams.length() + fileName.getBytes("UTF-8").length; + Log.d(LOG_TAG, "String Length: " + stringLength); + int fixedLength = (int) fileInputStream.getChannel().size() + stringLength; + Log.d(LOG_TAG, "Content Length: " + fixedLength); + conn.setFixedLengthStreamingMode(fixedLength); } - - dos = new DataOutputStream( conn.getOutputStream() ); + dos = new DataOutputStream(conn.getOutputStream()); dos.writeBytes(extraParams); //We don't want to chagne encoding, we just want this to write for all Unicode. dos.write(fileName.getBytes("UTF-8")); @@ -373,13 +371,13 @@ public class FileTransfer extends Plugin { StringBuffer responseString = new StringBuffer(""); DataInputStream inStream; try { - inStream = new DataInputStream ( conn.getInputStream() ); - } catch(FileNotFoundException e) { + inStream = new DataInputStream(conn.getInputStream()); + } catch (FileNotFoundException e) { throw new IOException("Received error from server"); } String line; - while (( line = inStream.readLine()) != null) { + while ((line = inStream.readLine()) != null) { responseString.append(line); } Log.d(LOG_TAG, "got response from server"); @@ -394,7 +392,7 @@ public class FileTransfer extends Plugin { // Revert back to the proper verifier and socket factories if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) { - ((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier); + ((HttpsURLConnection) conn).setHostnameVerifier(defaultHostnameVerifier); HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory); } @@ -416,46 +414,46 @@ public class FileTransfer extends Plugin { file.getParentFile().mkdirs(); // connect to server - if(webView.isUrlWhiteListed(source)) + if (webView.isUrlWhiteListed(source)) { - URL url = new URL(source); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - - //Add cookie support - String cookie = CookieManager.getInstance().getCookie(source); - if(cookie != null) - { - connection.setRequestProperty("cookie", cookie); - } - - connection.connect(); + URL url = new URL(source); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); - Log.d(LOG_TAG, "Download file:" + url); + //Add cookie support + String cookie = CookieManager.getInstance().getCookie(source); + if (cookie != null) + { + connection.setRequestProperty("cookie", cookie); + } - InputStream inputStream = connection.getInputStream(); - byte[] buffer = new byte[1024]; - int bytesRead = 0; + connection.connect(); - FileOutputStream outputStream = new FileOutputStream(file); + Log.d(LOG_TAG, "Download file:" + url); - // write bytes to file - while ( (bytesRead = inputStream.read(buffer)) > 0 ) { - outputStream.write(buffer,0, bytesRead); - } + InputStream inputStream = connection.getInputStream(); + byte[] buffer = new byte[1024]; + int bytesRead = 0; - outputStream.close(); + FileOutputStream outputStream = new FileOutputStream(file); - Log.d(LOG_TAG, "Saved file: " + target); + // write bytes to file + while ((bytesRead = inputStream.read(buffer)) > 0) { + outputStream.write(buffer, 0, bytesRead); + } - // create FileEntry object - FileUtils fileUtil = new FileUtils(); + outputStream.close(); - return fileUtil.getEntry(file); + Log.d(LOG_TAG, "Saved file: " + target); + + // create FileEntry object + FileUtils fileUtil = new FileUtils(); + + return fileUtil.getEntry(file); } else { - throw new IOException("Error: Unable to connect to domain"); + throw new IOException("Error: Unable to connect to domain"); } } catch (Exception e) { Log.d(LOG_TAG, e.getMessage(), e); @@ -473,7 +471,7 @@ public class FileTransfer extends Plugin { private InputStream getPathFromUri(String path) throws FileNotFoundException { if (path.startsWith("content:")) { Uri uri = Uri.parse(path); - return ctx.getContentResolver().openInputStream(uri); + return ctx.getActivity().getContentResolver().openInputStream(uri); } else if (path.startsWith("file://")) { int question = path.indexOf("?"); diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index fb5694b0..5a73c800 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -37,15 +37,15 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import android.content.Context; +//import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; -import android.util.Log; +//import android.util.Log; import android.webkit.MimeTypeMap; -import android.app.Activity; +//import android.app.Activity; /** * This class provides SD card file and directory services to JavaScript. @@ -132,7 +132,7 @@ public class FileUtils extends Plugin { else if (action.equals("requestFileSystem")) { long size = args.optLong(1); if (size != 0) { - if (size > (DirectoryManager.getFreeDiskSpace(true)*1024)) { + if (size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) { return new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR); } } @@ -222,9 +222,9 @@ public class FileUtils extends Plugin { * @param filePath the path to check */ private void notifyDelete(String filePath) { - int result = this.ctx.getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + int result = this.ctx.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.DATA + " = ?", - new String[] {filePath}); + new String[] { filePath }); } /** @@ -244,7 +244,7 @@ public class FileUtils extends Plugin { // Handle the special case where you get an Android content:// uri. if (decoded.startsWith("content:")) { - Cursor cursor = ((Activity) this.ctx).managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null); + Cursor cursor = this.ctx.getActivity().managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null); // Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data" int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); @@ -295,7 +295,7 @@ public class FileUtils extends Plugin { if (fp.isDirectory()) { File[] files = fp.listFiles(); - for (int i=0; i 0) { this.truncateFile(filename, offset); append = true; } - byte [] rawData = data.getBytes(); + byte[] rawData = data.getBytes(); ByteArrayInputStream in = new ByteArrayInputStream(rawData); FileOutputStream out = new FileOutputStream(filename, append); byte buff[] = new byte[rawData.length]; @@ -1007,9 +1006,9 @@ public class FileUtils extends Plugin { RandomAccessFile raf = new RandomAccessFile(filename, "rw"); if (raf.length() >= size) { - FileChannel channel = raf.getChannel(); - channel.truncate(size); - return size; + FileChannel channel = raf.getChannel(); + channel.truncate(size); + return size; } return raf.length(); @@ -1025,7 +1024,7 @@ public class FileUtils extends Plugin { private InputStream getPathFromUri(String path) throws FileNotFoundException { if (path.startsWith("content")) { Uri uri = Uri.parse(path); - return ctx.getContentResolver().openInputStream(uri); + return ctx.getActivity().getContentResolver().openInputStream(uri); } else { path = stripFileProtocol(path); @@ -1040,9 +1039,9 @@ public class FileUtils extends Plugin { * @param ctx) the current applicaiton context * @return the full path to the file */ - protected static String getRealPathFromURI(Uri contentUri, Activity ctx) { + protected static String getRealPathFromURI(Uri contentUri, CordovaInterface ctx) { String[] proj = { _DATA }; - Cursor cursor = ctx.managedQuery(contentUri, proj, null, null, null); + Cursor cursor = ctx.getActivity().managedQuery(contentUri, proj, null, null, null); int column_index = cursor.getColumnIndexOrThrow(_DATA); cursor.moveToFirst(); return cursor.getString(column_index); diff --git a/framework/src/org/apache/cordova/GeoListener.java b/framework/src/org/apache/cordova/GeoListener.java index fdfcc429..92a2ca66 100755 --- a/framework/src/org/apache/cordova/GeoListener.java +++ b/framework/src/org/apache/cordova/GeoListener.java @@ -21,113 +21,114 @@ package org.apache.cordova; import android.content.Context; import android.location.Location; import android.location.LocationManager; -import android.webkit.WebView; + +//import android.webkit.WebView; public class GeoListener { - public static int PERMISSION_DENIED = 1; - public static int POSITION_UNAVAILABLE = 2; - public static int TIMEOUT = 3; + public static int PERMISSION_DENIED = 1; + public static int POSITION_UNAVAILABLE = 2; + public static int TIMEOUT = 3; - String id; // Listener ID - String successCallback; // - String failCallback; + String id; // Listener ID + String successCallback; // + String failCallback; GpsListener mGps; // GPS listener NetworkListener mNetwork; // Network listener LocationManager mLocMan; // Location manager - - private GeoBroker broker; // GeoBroker object - - int interval; - - /** - * Constructor. - * - * @param id Listener id - * @param ctx - * @param time Sampling period in msec - * @param appView - */ - GeoListener(GeoBroker broker, String id, int time) { - this.id = id; - this.interval = time; - this.broker = broker; - this.mGps = null; - this.mNetwork = null; - this.mLocMan = (LocationManager) broker.ctx.getSystemService(Context.LOCATION_SERVICE); - // If GPS provider, then create and start GPS listener - if (this.mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null) { - this.mGps = new GpsListener(broker.ctx, time, this); - } - - // If network provider, then create and start network listener - if (this.mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null) { - this.mNetwork = new NetworkListener(broker.ctx, time, this); - } - } - - /** - * Destroy listener. - */ - public void destroy() { - this.stop(); - } - - /** - * Location found. Send location back to JavaScript. - * - * @param loc - */ - void success(Location loc) { - - String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + - "," + loc.getAccuracy() + "," + loc.getBearing() + - "," + loc.getSpeed() + "," + loc.getTime(); - - if (id == "global") { - this.stop(); - } - this.broker.sendJavascript("navigator._geo.success('" + id + "'," + params + ");"); - } - - /** - * Location failed. Send error back to JavaScript. - * - * @param code The error code - * @param msg The error message - */ - void fail(int code, String msg) { - this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');"); - this.stop(); - } - - /** - * Start retrieving location. - * - * @param interval - */ - void start(int interval) { - if (this.mGps != null) { - this.mGps.start(interval); - } - if (this.mNetwork != null) { - this.mNetwork.start(interval); - } - if (this.mNetwork == null && this.mGps == null) { - this.fail(POSITION_UNAVAILABLE, "No location providers available."); - } - } - - /** - * Stop listening for location. - */ - void stop() { - if (this.mGps != null) { - this.mGps.stop(); - } - if (this.mNetwork != null) { - this.mNetwork.stop(); - } - } + private GeoBroker broker; // GeoBroker object + + int interval; + + /** + * Constructor. + * + * @param id Listener id + * @param ctx + * @param time Sampling period in msec + * @param appView + */ + GeoListener(GeoBroker broker, String id, int time) { + this.id = id; + this.interval = time; + this.broker = broker; + this.mGps = null; + this.mNetwork = null; + this.mLocMan = (LocationManager) broker.ctx.getActivity().getSystemService(Context.LOCATION_SERVICE); + + // If GPS provider, then create and start GPS listener + if (this.mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null) { + this.mGps = new GpsListener(broker.ctx, time, this); + } + + // If network provider, then create and start network listener + if (this.mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null) { + this.mNetwork = new NetworkListener(broker.ctx, time, this); + } + } + + /** + * Destroy listener. + */ + public void destroy() { + this.stop(); + } + + /** + * Location found. Send location back to JavaScript. + * + * @param loc + */ + void success(Location loc) { + + String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + + "," + loc.getAccuracy() + "," + loc.getBearing() + + "," + loc.getSpeed() + "," + loc.getTime(); + + if (id == "global") { + this.stop(); + } + this.broker.sendJavascript("navigator._geo.success('" + id + "'," + params + ");"); + } + + /** + * Location failed. Send error back to JavaScript. + * + * @param code The error code + * @param msg The error message + */ + void fail(int code, String msg) { + this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');"); + this.stop(); + } + + /** + * Start retrieving location. + * + * @param interval + */ + void start(int interval) { + if (this.mGps != null) { + this.mGps.start(interval); + } + if (this.mNetwork != null) { + this.mNetwork.start(interval); + } + if (this.mNetwork == null && this.mGps == null) { + this.fail(POSITION_UNAVAILABLE, "No location providers available."); + } + } + + /** + * Stop listening for location. + */ + void stop() { + if (this.mGps != null) { + this.mGps.stop(); + } + if (this.mNetwork != null) { + this.mNetwork.stop(); + } + } } diff --git a/framework/src/org/apache/cordova/GpsListener.java b/framework/src/org/apache/cordova/GpsListener.java index 264c00c5..6cd85fb0 100755 --- a/framework/src/org/apache/cordova/GpsListener.java +++ b/framework/src/org/apache/cordova/GpsListener.java @@ -32,132 +32,132 @@ import android.os.Bundle; * */ public class GpsListener implements LocationListener { - - private Context mCtx; // CordovaActivity object - - private LocationManager mLocMan; // Location manager object - private GeoListener owner; // Geolistener object (parent) - private boolean hasData = false; // Flag indicates if location data is available in cLoc - private Location cLoc; // Last recieved location - private boolean running = false; // Flag indicates if listener is running - - /** - * Constructor. - * Automatically starts listening. - * - * @param ctx - * @param interval - * @param m - */ - public GpsListener(Context ctx, int interval, GeoListener m) { - this.owner = m; - this.mCtx = ctx; - this.mLocMan = (LocationManager) this.mCtx.getSystemService(Context.LOCATION_SERVICE); - this.running = false; - this.start(interval); - } - - /** - * Get last location. - * - * @return Location object - */ - public Location getLocation() { - this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER); - if (this.cLoc != null) { - this.hasData = true; - } - return this.cLoc; - } - - /** - * Called when the provider is disabled by the user. - * - * @param provider - */ - public void onProviderDisabled(String provider) { - this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS provider disabled."); - } - /** - * Called when the provider is enabled by the user. - * - * @param provider - */ - public void onProviderEnabled(String provider) { - System.out.println("GpsListener: The provider "+ provider + " is enabled"); - } + private CordovaInterface mCtx; // CordovaActivity object - /** - * Called when the provider status changes. This method is called when a - * provider is unable to fetch a location or if the provider has recently - * become available after a period of unavailability. - * - * @param provider - * @param status - * @param extras - */ - public void onStatusChanged(String provider, int status, Bundle extras) { - System.out.println("GpsListener: The status of the provider " + provider + " has changed"); - if (status == 0) { - System.out.println("GpsListener: " + provider + " is OUT OF SERVICE"); - this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS out of service."); - } - else if (status == 1) { - System.out.println("GpsListener: " + provider + " is TEMPORARILY_UNAVAILABLE"); - } - else { - System.out.println("GpsListener: " + provider + " is Available"); - } - } + private LocationManager mLocMan; // Location manager object + private GeoListener owner; // Geolistener object (parent) + private boolean hasData = false; // Flag indicates if location data is available in cLoc + private Location cLoc; // Last recieved location + private boolean running = false; // Flag indicates if listener is running - /** - * Called when the location has changed. - * - * @param location - */ - public void onLocationChanged(Location location) { - System.out.println("GpsListener: The location has been updated!"); - this.hasData = true; - this.cLoc = location; - this.owner.success(location); - } + /** + * Constructor. + * Automatically starts listening. + * + * @param ctx + * @param interval + * @param m + */ + public GpsListener(CordovaInterface ctx, int interval, GeoListener m) { + this.owner = m; + this.mCtx = ctx; + this.mLocMan = (LocationManager) this.mCtx.getActivity().getSystemService(Context.LOCATION_SERVICE); + this.running = false; + this.start(interval); + } - /** - * Determine if location data is available. - * - * @return - */ - public boolean hasLocation() { - return this.hasData; - } - - /** - * Start requesting location updates. - * - * @param interval - */ - public void start(int interval) { - if (!this.running) { - this.running = true; - this.mLocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 0, this); - this.getLocation(); + /** + * Get last location. + * + * @return Location object + */ + public Location getLocation() { + this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER); + if (this.cLoc != null) { + this.hasData = true; + } + return this.cLoc; + } - // If GPS provider has data, then send now - if (this.hasData) { - this.owner.success(this.cLoc); - } - } - } + /** + * Called when the provider is disabled by the user. + * + * @param provider + */ + public void onProviderDisabled(String provider) { + this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS provider disabled."); + } + + /** + * Called when the provider is enabled by the user. + * + * @param provider + */ + public void onProviderEnabled(String provider) { + System.out.println("GpsListener: The provider " + provider + " is enabled"); + } + + /** + * Called when the provider status changes. This method is called when a + * provider is unable to fetch a location or if the provider has recently + * become available after a period of unavailability. + * + * @param provider + * @param status + * @param extras + */ + public void onStatusChanged(String provider, int status, Bundle extras) { + System.out.println("GpsListener: The status of the provider " + provider + " has changed"); + if (status == 0) { + System.out.println("GpsListener: " + provider + " is OUT OF SERVICE"); + this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS out of service."); + } + else if (status == 1) { + System.out.println("GpsListener: " + provider + " is TEMPORARILY_UNAVAILABLE"); + } + else { + System.out.println("GpsListener: " + provider + " is Available"); + } + } + + /** + * Called when the location has changed. + * + * @param location + */ + public void onLocationChanged(Location location) { + System.out.println("GpsListener: The location has been updated!"); + this.hasData = true; + this.cLoc = location; + this.owner.success(location); + } + + /** + * Determine if location data is available. + * + * @return + */ + public boolean hasLocation() { + return this.hasData; + } + + /** + * Start requesting location updates. + * + * @param interval + */ + public void start(int interval) { + if (!this.running) { + this.running = true; + this.mLocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 0, this); + this.getLocation(); + + // If GPS provider has data, then send now + if (this.hasData) { + this.owner.success(this.cLoc); + } + } + } + + /** + * Stop receiving location updates. + */ + public void stop() { + if (this.running) { + this.mLocMan.removeUpdates(this); + } + this.running = false; + } - /** - * Stop receiving location updates. - */ - public void stop() { - if (this.running) { - this.mLocMan.removeUpdates(this); - } - this.running = false; - } - } diff --git a/framework/src/org/apache/cordova/HttpHandler.java b/framework/src/org/apache/cordova/HttpHandler.java index f64b5fd2..4799e311 100755 --- a/framework/src/org/apache/cordova/HttpHandler.java +++ b/framework/src/org/apache/cordova/HttpHandler.java @@ -29,52 +29,61 @@ import org.apache.http.impl.client.DefaultHttpClient; public class HttpHandler { - protected Boolean get(String url, String file) - { - HttpEntity entity = getHttpEntity(url); - try { - writeToDisk(entity, file); - } catch (Exception e) { e.printStackTrace(); return false; } - try { - entity.consumeContent(); - } catch (Exception e) { e.printStackTrace(); return false; } - return true; - } - - private HttpEntity getHttpEntity(String url) - /** - * get the http entity at a given url - */ - { - HttpEntity entity=null; - try { - DefaultHttpClient httpclient = new DefaultHttpClient(); - HttpGet httpget = new HttpGet(url); - HttpResponse response = httpclient.execute(httpget); - entity = response.getEntity(); - } catch (Exception e) { e.printStackTrace(); return null; } - return entity; - } - - private void writeToDisk(HttpEntity entity, String file) throws IllegalStateException, IOException - /** - * writes a HTTP entity to the specified filename and location on disk - */ - { - int i=0; - String FilePath="/sdcard/" + file; - InputStream in = entity.getContent(); - byte buff[] = new byte[1024]; - FileOutputStream out= - new FileOutputStream(FilePath); - do { - int numread = in.read(buff); - if (numread <= 0) - break; - out.write(buff, 0, numread); - i++; - } while (true); - out.flush(); - out.close(); - } + protected Boolean get(String url, String file) + { + HttpEntity entity = getHttpEntity(url); + try { + writeToDisk(entity, file); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + try { + entity.consumeContent(); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + private HttpEntity getHttpEntity(String url) + /** + * get the http entity at a given url + */ + { + HttpEntity entity = null; + try { + DefaultHttpClient httpclient = new DefaultHttpClient(); + HttpGet httpget = new HttpGet(url); + HttpResponse response = httpclient.execute(httpget); + entity = response.getEntity(); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + return entity; + } + + private void writeToDisk(HttpEntity entity, String file) throws IllegalStateException, IOException + /** + * writes a HTTP entity to the specified filename and location on disk + */ + { + int i = 0; + String FilePath = "/sdcard/" + file; + InputStream in = entity.getContent(); + byte buff[] = new byte[1024]; + FileOutputStream out = + new FileOutputStream(FilePath); + do { + int numread = in.read(buff); + if (numread <= 0) + break; + out.write(buff, 0, numread); + i++; + } while (true); + out.flush(); + out.close(); + } } diff --git a/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java b/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java index a024b23b..de70020d 100755 --- a/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java +++ b/framework/src/org/apache/cordova/LinearLayoutSoftKeyboardDetect.java @@ -17,10 +17,11 @@ under the License. */ package org.apache.cordova; + import org.apache.cordova.api.LOG; import android.content.Context; -import android.view.View.MeasureSpec; +//import android.view.View.MeasureSpec; import android.widget.LinearLayout; /** @@ -29,15 +30,15 @@ import android.widget.LinearLayout; public class LinearLayoutSoftKeyboardDetect extends LinearLayout { private static final String TAG = "SoftKeyboardDetect"; - + private int oldHeight = 0; // Need to save the old height as not to send redundant events private int oldWidth = 0; // Need to save old width for orientation change private int screenWidth = 0; private int screenHeight = 0; private DroidGap app = null; - + public LinearLayoutSoftKeyboardDetect(Context context, int width, int height) { - super(context); + super(context); screenWidth = width; screenHeight = height; app = (DroidGap) context; @@ -55,8 +56,8 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout { * @param heightMeasureSpec */ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + LOG.v(TAG, "We are in our onMeasure method"); // Get the current height of the visible part of the screen. @@ -66,7 +67,7 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout { height = MeasureSpec.getSize(heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); LOG.v(TAG, "Old Height = %d", oldHeight); - LOG.v(TAG, "Height = %d", height); + LOG.v(TAG, "Height = %d", height); LOG.v(TAG, "Old Width = %d", oldWidth); LOG.v(TAG, "Width = %d", width); @@ -76,7 +77,7 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout { LOG.d(TAG, "Ignore this event"); } // Account for orientation change and ignore this event/Fire orientation change - else if(screenHeight == width) + else if (screenHeight == width) { int tmp_var = screenHeight; screenHeight = screenWidth; @@ -86,14 +87,14 @@ public class LinearLayoutSoftKeyboardDetect extends LinearLayout { // If the height as gotten bigger then we will assume the soft keyboard has // gone away. else if (height > oldHeight) { - if(app != null) - app.sendJavascript("cordova.fireDocumentEvent('hidekeyboard');"); - } + if (app != null) + app.appView.sendJavascript("cordova.fireDocumentEvent('hidekeyboard');"); + } // If the height as gotten smaller then we will assume the soft keyboard has // been displayed. else if (height < oldHeight) { - if(app != null) - app.sendJavascript("cordova.fireDocumentEvent('showkeyboard');"); + if (app != null) + app.appView.sendJavascript("cordova.fireDocumentEvent('showkeyboard');"); } // Update the old height for the next event diff --git a/framework/src/org/apache/cordova/NetworkListener.java b/framework/src/org/apache/cordova/NetworkListener.java index 1b70cd7b..7fa135fc 100755 --- a/framework/src/org/apache/cordova/NetworkListener.java +++ b/framework/src/org/apache/cordova/NetworkListener.java @@ -27,127 +27,127 @@ import android.location.LocationListener; import android.os.Bundle; public class NetworkListener implements LocationListener { - - private Context mCtx; // CordovaActivity object - - private LocationManager mLocMan; // Location manager object - private GeoListener owner; // Geolistener object (parent) - private boolean hasData = false; // Flag indicates if location data is available in cLoc - private Location cLoc; // Last recieved location - private boolean running = false; // Flag indicates if listener is running - /** - * Constructor. - * Automatically starts listening. - * - * @param ctx - * @param interval - * @param m - */ - public NetworkListener(Context ctx, int interval, GeoListener m) { - this.owner = m; - this.mCtx = ctx; - this.mLocMan = (LocationManager) this.mCtx.getSystemService(Context.LOCATION_SERVICE); - this.running = false; - this.start(interval); - } - - /** - * Get last location. - * - * @return Location object - */ - public Location getLocation() { - this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - if (this.cLoc != null) { - this.hasData = true; - } - return this.cLoc; - } + private CordovaInterface mCtx; // CordovaActivity object - /** - * Called when the provider is disabled by the user. - * - * @param provider - */ - public void onProviderDisabled(String provider) { - System.out.println("NetworkListener: The provider " + provider + " is disabled"); - } + private LocationManager mLocMan; // Location manager object + private GeoListener owner; // Geolistener object (parent) + private boolean hasData = false; // Flag indicates if location data is available in cLoc + private Location cLoc; // Last recieved location + private boolean running = false; // Flag indicates if listener is running - /** - * Called when the provider is enabled by the user. - * - * @param provider - */ - public void onProviderEnabled(String provider) { - System.out.println("NetworkListener: The provider "+ provider + " is enabled"); - } + /** + * Constructor. + * Automatically starts listening. + * + * @param ctx + * @param interval + * @param m + */ + public NetworkListener(CordovaInterface ctx, int interval, GeoListener m) { + this.owner = m; + this.mCtx = ctx; + this.mLocMan = (LocationManager) this.mCtx.getActivity().getSystemService(Context.LOCATION_SERVICE); + this.running = false; + this.start(interval); + } - /** - * Called when the provider status changes. This method is called when a - * provider is unable to fetch a location or if the provider has recently - * become available after a period of unavailability. - * - * @param provider - * @param status - * @param extras - */ - public void onStatusChanged(String provider, int status, Bundle extras) { - System.out.println("NetworkListener: The status of the provider " + provider + " has changed"); - if (status == 0) { - System.out.println("NetworkListener: " + provider + " is OUT OF SERVICE"); - } - else if (status == 1) { - System.out.println("NetworkListener: " + provider + " is TEMPORARILY_UNAVAILABLE"); - } - else { - System.out.println("NetworkListener: " + provider + " is Available"); - } - } + /** + * Get last location. + * + * @return Location object + */ + public Location getLocation() { + this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); + if (this.cLoc != null) { + this.hasData = true; + } + return this.cLoc; + } + + /** + * Called when the provider is disabled by the user. + * + * @param provider + */ + public void onProviderDisabled(String provider) { + System.out.println("NetworkListener: The provider " + provider + " is disabled"); + } + + /** + * Called when the provider is enabled by the user. + * + * @param provider + */ + public void onProviderEnabled(String provider) { + System.out.println("NetworkListener: The provider " + provider + " is enabled"); + } + + /** + * Called when the provider status changes. This method is called when a + * provider is unable to fetch a location or if the provider has recently + * become available after a period of unavailability. + * + * @param provider + * @param status + * @param extras + */ + public void onStatusChanged(String provider, int status, Bundle extras) { + System.out.println("NetworkListener: The status of the provider " + provider + " has changed"); + if (status == 0) { + System.out.println("NetworkListener: " + provider + " is OUT OF SERVICE"); + } + else if (status == 1) { + System.out.println("NetworkListener: " + provider + " is TEMPORARILY_UNAVAILABLE"); + } + else { + System.out.println("NetworkListener: " + provider + " is Available"); + } + } + + /** + * Called when the location has changed. + * + * @param location + */ + public void onLocationChanged(Location location) { + System.out.println("NetworkListener: The location has been updated!"); + this.hasData = true; + this.cLoc = location; + + // The GPS is the primary form of Geolocation in Cordova. + // Only fire the success variables if the GPS is down for some reason. + if (!this.owner.mGps.hasLocation()) { + this.owner.success(location); + } + } + + /** + * Start requesting location updates. + * + * @param interval + */ + public void start(int interval) { + if (!this.running) { + this.running = true; + this.mLocMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, 0, this); + this.getLocation(); + + // If Network provider has data but GPS provider doesn't, then send ours + if (this.hasData && !this.owner.mGps.hasLocation()) { + this.owner.success(this.cLoc); + } + } + } + + /** + * Stop receiving location updates. + */ + public void stop() { + if (this.running) { + this.mLocMan.removeUpdates(this); + } + this.running = false; + } - /** - * Called when the location has changed. - * - * @param location - */ - public void onLocationChanged(Location location) { - System.out.println("NetworkListener: The location has been updated!"); - this.hasData = true; - this.cLoc = location; - - // The GPS is the primary form of Geolocation in Cordova. - // Only fire the success variables if the GPS is down for some reason. - if (!this.owner.mGps.hasLocation()) { - this.owner.success(location); - } - } - - /** - * Start requesting location updates. - * - * @param interval - */ - public void start(int interval) { - if (!this.running) { - this.running = true; - this.mLocMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, 0, this); - this.getLocation(); - - // If Network provider has data but GPS provider doesn't, then send ours - if (this.hasData && !this.owner.mGps.hasLocation()) { - this.owner.success(this.cLoc); - } - } - } - - /** - * Stop receiving location updates. - */ - public void stop() { - if (this.running) { - this.mLocMan.removeUpdates(this); - } - this.running = false; - } - } diff --git a/framework/src/org/apache/cordova/NetworkManager.java b/framework/src/org/apache/cordova/NetworkManager.java index 6ef0c3ee..36fbbb38 100755 --- a/framework/src/org/apache/cordova/NetworkManager.java +++ b/framework/src/org/apache/cordova/NetworkManager.java @@ -23,7 +23,6 @@ import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; - import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -33,7 +32,7 @@ import android.net.NetworkInfo; import android.util.Log; public class NetworkManager extends Plugin { - + public static int NOT_REACHABLE = 0; public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1; public static int REACHABLE_VIA_WIFI_NETWORK = 2; @@ -66,18 +65,18 @@ public class NetworkManager extends Plugin { public static final String TYPE_3G = "3g"; public static final String TYPE_4G = "4g"; public static final String TYPE_NONE = "none"; - + private static final String LOG_TAG = "NetworkManager"; private String connectionCallbackId; ConnectivityManager sockMan; BroadcastReceiver receiver; - + /** * Constructor. */ - public NetworkManager() { + public NetworkManager() { this.receiver = null; } @@ -87,26 +86,27 @@ public class NetworkManager extends Plugin { * * @param ctx The context of the main Activity. */ - public void setContext(Context ctx) { + public void setContext(CordovaInterface ctx) { super.setContext(ctx); - this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE); + this.sockMan = (ConnectivityManager) ctx.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE); this.connectionCallbackId = null; - + // We need to listen to connectivity events to update navigator.connection - IntentFilter intentFilter = new IntentFilter() ; + IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); if (this.receiver == null) { this.receiver = new BroadcastReceiver() { + @SuppressWarnings("deprecation") @Override public void onReceive(Context context, Intent intent) { - updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)); + updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO)); } }; - ctx.registerReceiver(this.receiver, intentFilter); + ctx.getActivity().registerReceiver(this.receiver, intentFilter); } } - + /** * Executes the request and returns PluginResult. * @@ -118,7 +118,7 @@ public class NetworkManager extends Plugin { public PluginResult execute(String action, JSONArray args, String callbackId) { PluginResult.Status status = PluginResult.Status.INVALID_ACTION; String result = "Unsupported Operation: " + action; - + if (action.equals("getConnectionInfo")) { this.connectionCallbackId = callbackId; NetworkInfo info = sockMan.getActiveNetworkInfo(); @@ -126,7 +126,7 @@ public class NetworkManager extends Plugin { pluginResult.setKeepCallback(true); return pluginResult; } - + return new PluginResult(status, result); } @@ -139,14 +139,14 @@ public class NetworkManager extends Plugin { public boolean isSynch(String action) { return true; } - + /** * Stop network receiver. */ public void onDestroy() { if (this.receiver != null) { try { - this.ctx.unregisterReceiver(this.receiver); + this.ctx.getActivity().unregisterReceiver(this.receiver); } catch (Exception e) { Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e); } @@ -157,14 +157,13 @@ public class NetworkManager extends Plugin { // LOCAL METHODS //-------------------------------------------------------------------------- - /** * Updates the JavaScript side whenever the connection changes * * @param info the current active network info * @return */ - private void updateConnectionInfo(NetworkInfo info) { + private void updateConnectionInfo(NetworkInfo info) { // send update to javascript "navigator.network.connection" sendUpdate(this.getConnectionInfo(info)); } @@ -188,7 +187,7 @@ public class NetworkManager extends Plugin { } return type; } - + /** * Create a new plugin result and send it back to JavaScript * @@ -198,11 +197,11 @@ public class NetworkManager extends Plugin { PluginResult result = new PluginResult(PluginResult.Status.OK, type); result.setKeepCallback(true); this.success(result, this.connectionCallbackId); - + // Send to all plugins webView.postMessage("networkconnection", type); } - + /** * Determine the type of connection * @@ -218,13 +217,13 @@ public class NetworkManager extends Plugin { } else if (type.toLowerCase().equals(MOBILE)) { type = info.getSubtypeName(); - if (type.toLowerCase().equals(GSM) || + if (type.toLowerCase().equals(GSM) || type.toLowerCase().equals(GPRS) || type.toLowerCase().equals(EDGE)) { return TYPE_2G; } - else if (type.toLowerCase().startsWith(CDMA) || - type.toLowerCase().equals(UMTS) || + else if (type.toLowerCase().startsWith(CDMA) || + type.toLowerCase().equals(UMTS) || type.toLowerCase().equals(ONEXRTT) || type.toLowerCase().equals(EHRPD) || type.toLowerCase().equals(HSUPA) || @@ -232,13 +231,13 @@ public class NetworkManager extends Plugin { type.toLowerCase().equals(HSPA)) { return TYPE_3G; } - else if (type.toLowerCase().equals(LTE) || + else if (type.toLowerCase().equals(LTE) || type.toLowerCase().equals(UMB) || type.toLowerCase().equals(HSPA_PLUS)) { return TYPE_4G; } } - } + } else { return TYPE_NONE; } diff --git a/framework/src/org/apache/cordova/Notification.java b/framework/src/org/apache/cordova/Notification.java index 8d5e7584..ed0b4365 100755 --- a/framework/src/org/apache/cordova/Notification.java +++ b/framework/src/org/apache/cordova/Notification.java @@ -31,337 +31,336 @@ import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Vibrator; -import android.app.Activity; /** * This class provides access to notifications on the device. */ public class Notification extends Plugin { - - public int confirmResult = -1; - public ProgressDialog spinnerDialog = null; - public ProgressDialog progressDialog = null; - - /** - * Constructor. - */ - public Notification() { - } - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public PluginResult execute(String action, JSONArray args, String callbackId) { - PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - - try { - if (action.equals("beep")) { - this.beep(args.getLong(0)); - } - else if (action.equals("vibrate")) { - this.vibrate(args.getLong(0)); - } - else if (action.equals("alert")) { - this.alert(args.getString(0),args.getString(1),args.getString(2), callbackId); - PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); - r.setKeepCallback(true); - return r; - } - else if (action.equals("confirm")) { - this.confirm(args.getString(0),args.getString(1),args.getString(2), callbackId); - PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); - r.setKeepCallback(true); - return r; - } - else if (action.equals("activityStart")) { - this.activityStart(args.getString(0),args.getString(1)); - } - else if (action.equals("activityStop")) { - this.activityStop(); - } - else if (action.equals("progressStart")) { - this.progressStart(args.getString(0),args.getString(1)); - } - else if (action.equals("progressValue")) { - this.progressValue(args.getInt(0)); - } - else if (action.equals("progressStop")) { - this.progressStop(); - } - return new PluginResult(status, result); - } catch (JSONException e) { - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); - } - } + public int confirmResult = -1; + public ProgressDialog spinnerDialog = null; + public ProgressDialog progressDialog = null; - /** - * Identifies if action to be executed returns a value and should be run synchronously. - * - * @param action The action to execute - * @return T=returns value - */ - public boolean isSynch(String action) { - if (action.equals("alert")) { - return true; + /** + * Constructor. + */ + public Notification() { } - else if (action.equals("confirm")) { - return true; + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + try { + if (action.equals("beep")) { + this.beep(args.getLong(0)); + } + else if (action.equals("vibrate")) { + this.vibrate(args.getLong(0)); + } + else if (action.equals("alert")) { + this.alert(args.getString(0), args.getString(1), args.getString(2), callbackId); + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + return r; + } + else if (action.equals("confirm")) { + this.confirm(args.getString(0), args.getString(1), args.getString(2), callbackId); + PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT); + r.setKeepCallback(true); + return r; + } + else if (action.equals("activityStart")) { + this.activityStart(args.getString(0), args.getString(1)); + } + else if (action.equals("activityStop")) { + this.activityStop(); + } + else if (action.equals("progressStart")) { + this.progressStart(args.getString(0), args.getString(1)); + } + else if (action.equals("progressValue")) { + this.progressValue(args.getInt(0)); + } + else if (action.equals("progressStop")) { + this.progressStop(); + } + return new PluginResult(status, result); + } catch (JSONException e) { + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } } - else if (action.equals("activityStart")) { - return true; + + /** + * Identifies if action to be executed returns a value and should be run synchronously. + * + * @param action The action to execute + * @return T=returns value + */ + public boolean isSynch(String action) { + if (action.equals("alert")) { + return true; + } + else if (action.equals("confirm")) { + return true; + } + else if (action.equals("activityStart")) { + return true; + } + else if (action.equals("activityStop")) { + return true; + } + else if (action.equals("progressStart")) { + return true; + } + else if (action.equals("progressValue")) { + return true; + } + else if (action.equals("progressStop")) { + return true; + } + else { + return false; + } } - else if (action.equals("activityStop")) { - return true; - } - else if (action.equals("progressStart")) { - return true; - } - else if (action.equals("progressValue")) { - return true; - } - else if (action.equals("progressStop")) { - return true; - } - else { - return false; - } - } //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- - /** - * Beep plays the default notification ringtone. - * - * @param count Number of times to play notification - */ - public void beep(long count) { - Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); - Ringtone notification = RingtoneManager.getRingtone(this.ctx, ringtone); - - // If phone is not set to silent mode - if (notification != null) { - for (long i = 0; i < count; ++i) { - notification.play(); - long timeout = 5000; - while (notification.isPlaying() && (timeout > 0)) { - timeout = timeout - 100; - try { - Thread.sleep(100); - } catch (InterruptedException e) { - } + /** + * Beep plays the default notification ringtone. + * + * @param count Number of times to play notification + */ + public void beep(long count) { + Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); + Ringtone notification = RingtoneManager.getRingtone(this.ctx.getActivity().getBaseContext(), ringtone); + + // If phone is not set to silent mode + if (notification != null) { + for (long i = 0; i < count; ++i) { + notification.play(); + long timeout = 5000; + while (notification.isPlaying() && (timeout > 0)) { + timeout = timeout - 100; + try { + Thread.sleep(100); + } catch (InterruptedException e) { + } + } + } } - } } - } - - /** - * Vibrates the device for the specified amount of time. - * - * @param time Time to vibrate in ms. - */ - public void vibrate(long time){ + + /** + * Vibrates the device for the specified amount of time. + * + * @param time Time to vibrate in ms. + */ + public void vibrate(long time) { // Start the vibration, 0 defaults to half a second. - if (time == 0) { - time = 500; - } - Vibrator vibrator = (Vibrator) this.ctx.getSystemService(Context.VIBRATOR_SERVICE); + if (time == 0) { + time = 500; + } + Vibrator vibrator = (Vibrator) this.ctx.getActivity().getSystemService(Context.VIBRATOR_SERVICE); vibrator.vibrate(time); - } - - /** - * Builds and shows a native Android alert with given Strings - * @param message The message the alert should display - * @param title The title of the alert - * @param buttonLabel The label of the button - * @param callbackId The callback id - */ - public synchronized void alert(final String message, final String title, final String buttonLabel, final String callbackId) { - - final Context ctx = this.ctx; - final Notification notification = this; - - Runnable runnable = new Runnable() { - public void run() { - - AlertDialog.Builder dlg = new AlertDialog.Builder(ctx); - dlg.setMessage(message); - dlg.setTitle(title); - dlg.setCancelable(false); - dlg.setPositiveButton(buttonLabel, - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - notification.success(new PluginResult(PluginResult.Status.OK, 0), callbackId); - } - }); - dlg.create(); - dlg.show(); - }; - }; - ((Activity) this.ctx).runOnUiThread(runnable); - } - - /** - * Builds and shows a native Android confirm dialog with given title, message, buttons. - * This dialog only shows up to 3 buttons. Any labels after that will be ignored. - * The index of the button pressed will be returned to the JavaScript callback identified by callbackId. - * - * @param message The message the dialog should display - * @param title The title of the dialog - * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) - * @param callbackId The callback id - */ - public synchronized void confirm(final String message, final String title, String buttonLabels, final String callbackId) { - - final Context ctx = this.ctx; - final Notification notification = this; - final String[] fButtons = buttonLabels.split(","); - - Runnable runnable = new Runnable() { - public void run() { - AlertDialog.Builder dlg = new AlertDialog.Builder(ctx); - dlg.setMessage(message); - dlg.setTitle(title); - dlg.setCancelable(false); - - // First button - if (fButtons.length > 0) { - dlg.setNegativeButton(fButtons[0], - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - notification.success(new PluginResult(PluginResult.Status.OK, 1), callbackId); - } - }); - } - - // Second button - if (fButtons.length > 1) { - dlg.setNeutralButton(fButtons[1], - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - notification.success(new PluginResult(PluginResult.Status.OK, 2), callbackId); - } - }); - } - - // Third button - if (fButtons.length > 2) { - dlg.setPositiveButton(fButtons[2], - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - notification.success(new PluginResult(PluginResult.Status.OK, 3), callbackId); - } - } - ); - } - - dlg.create(); - dlg.show(); - }; - }; - ((Activity) this.ctx).runOnUiThread(runnable); - } - - /** - * Show the spinner. - * - * @param title Title of the dialog - * @param message The message of the dialog - */ - public synchronized void activityStart(final String title, final String message) { - if (this.spinnerDialog != null) { - this.spinnerDialog.dismiss(); - this.spinnerDialog = null; } - final Notification notification = this; - final Activity ctx = (Activity) this.ctx; - Runnable runnable = new Runnable() { - public void run() { - notification.spinnerDialog = ProgressDialog.show(ctx, title , message, true, true, - new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - notification.spinnerDialog = null; - } - }); - } - }; - ctx.runOnUiThread(runnable); - } - - /** - * Stop spinner. - */ - public synchronized void activityStop() { - if (this.spinnerDialog != null) { - this.spinnerDialog.dismiss(); - this.spinnerDialog = null; - } - } - /** - * Show the progress dialog. - * - * @param title Title of the dialog - * @param message The message of the dialog - */ - public synchronized void progressStart(final String title, final String message) { - if (this.progressDialog != null) { - this.progressDialog.dismiss(); - this.progressDialog = null; + /** + * Builds and shows a native Android alert with given Strings + * @param message The message the alert should display + * @param title The title of the alert + * @param buttonLabel The label of the button + * @param callbackId The callback id + */ + public synchronized void alert(final String message, final String title, final String buttonLabel, final String callbackId) { + + final CordovaInterface ctx = this.ctx; + final Notification notification = this; + + Runnable runnable = new Runnable() { + public void run() { + + AlertDialog.Builder dlg = new AlertDialog.Builder(ctx.getActivity()); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(false); + dlg.setPositiveButton(buttonLabel, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + notification.success(new PluginResult(PluginResult.Status.OK, 0), callbackId); + } + }); + dlg.create(); + dlg.show(); + }; + }; + this.ctx.getActivity().runOnUiThread(runnable); } - final Notification notification = this; - final Activity ctx = (Activity) this.ctx; - Runnable runnable = new Runnable() { - public void run() { - notification.progressDialog = new ProgressDialog(ctx); - notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); - notification.progressDialog.setTitle(title); - notification.progressDialog.setMessage(message); - notification.progressDialog.setCancelable(true); - notification.progressDialog.setMax(100); - notification.progressDialog.setProgress(0); - notification.progressDialog.setOnCancelListener( - new DialogInterface.OnCancelListener() { - public void onCancel(DialogInterface dialog) { - notification.progressDialog = null; + + /** + * Builds and shows a native Android confirm dialog with given title, message, buttons. + * This dialog only shows up to 3 buttons. Any labels after that will be ignored. + * The index of the button pressed will be returned to the JavaScript callback identified by callbackId. + * + * @param message The message the dialog should display + * @param title The title of the dialog + * @param buttonLabels A comma separated list of button labels (Up to 3 buttons) + * @param callbackId The callback id + */ + public synchronized void confirm(final String message, final String title, String buttonLabels, final String callbackId) { + + final CordovaInterface ctx = this.ctx; + final Notification notification = this; + final String[] fButtons = buttonLabels.split(","); + + Runnable runnable = new Runnable() { + public void run() { + AlertDialog.Builder dlg = new AlertDialog.Builder(ctx.getActivity()); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(false); + + // First button + if (fButtons.length > 0) { + dlg.setNegativeButton(fButtons[0], + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + notification.success(new PluginResult(PluginResult.Status.OK, 1), callbackId); + } + }); + } + + // Second button + if (fButtons.length > 1) { + dlg.setNeutralButton(fButtons[1], + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + notification.success(new PluginResult(PluginResult.Status.OK, 2), callbackId); + } + }); + } + + // Third button + if (fButtons.length > 2) { + dlg.setPositiveButton(fButtons[2], + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + notification.success(new PluginResult(PluginResult.Status.OK, 3), callbackId); + } + } + ); + } + + dlg.create(); + dlg.show(); + }; + }; + this.ctx.getActivity().runOnUiThread(runnable); + } + + /** + * Show the spinner. + * + * @param title Title of the dialog + * @param message The message of the dialog + */ + public synchronized void activityStart(final String title, final String message) { + if (this.spinnerDialog != null) { + this.spinnerDialog.dismiss(); + this.spinnerDialog = null; + } + final Notification notification = this; + final CordovaInterface ctx = this.ctx; + Runnable runnable = new Runnable() { + public void run() { + notification.spinnerDialog = ProgressDialog.show(ctx.getActivity(), title, message, true, true, + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + notification.spinnerDialog = null; + } + }); } - }); - notification.progressDialog.show(); - } - }; - ctx.runOnUiThread(runnable); - } - - /** - * Set value of progress bar. - * - * @param value 0-100 - */ - public synchronized void progressValue(int value) { - if (this.progressDialog != null) { - this.progressDialog.setProgress(value); - } - } - - /** - * Stop progress dialog. - */ - public synchronized void progressStop() { - if (this.progressDialog != null) { - this.progressDialog.dismiss(); - this.progressDialog = null; + }; + this.ctx.getActivity().runOnUiThread(runnable); + } + + /** + * Stop spinner. + */ + public synchronized void activityStop() { + if (this.spinnerDialog != null) { + this.spinnerDialog.dismiss(); + this.spinnerDialog = null; + } + } + + /** + * Show the progress dialog. + * + * @param title Title of the dialog + * @param message The message of the dialog + */ + public synchronized void progressStart(final String title, final String message) { + if (this.progressDialog != null) { + this.progressDialog.dismiss(); + this.progressDialog = null; + } + final Notification notification = this; + final CordovaInterface ctx = this.ctx; + Runnable runnable = new Runnable() { + public void run() { + notification.progressDialog = new ProgressDialog(ctx.getActivity()); + notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); + notification.progressDialog.setTitle(title); + notification.progressDialog.setMessage(message); + notification.progressDialog.setCancelable(true); + notification.progressDialog.setMax(100); + notification.progressDialog.setProgress(0); + notification.progressDialog.setOnCancelListener( + new DialogInterface.OnCancelListener() { + public void onCancel(DialogInterface dialog) { + notification.progressDialog = null; + } + }); + notification.progressDialog.show(); + } + }; + this.ctx.getActivity().runOnUiThread(runnable); + } + + /** + * Set value of progress bar. + * + * @param value 0-100 + */ + public synchronized void progressValue(int value) { + if (this.progressDialog != null) { + this.progressDialog.setProgress(value); + } + } + + /** + * Stop progress dialog. + */ + public synchronized void progressStop() { + if (this.progressDialog != null) { + this.progressDialog.dismiss(); + this.progressDialog = null; + } } - } } diff --git a/framework/src/org/apache/cordova/SplashScreen.java b/framework/src/org/apache/cordova/SplashScreen.java index a53bb714..9fca9b25 100644 --- a/framework/src/org/apache/cordova/SplashScreen.java +++ b/framework/src/org/apache/cordova/SplashScreen.java @@ -31,7 +31,8 @@ public class SplashScreen extends Plugin { String result = ""; if (action.equals("hide")) { - ((DroidGap)this.ctx).removeSplashScreen(); + //((DroidGap)this.ctx).removeSplashScreen(); + this.webView.postMessage("splashscreen", "hide"); } else { status = PluginResult.Status.INVALID_ACTION; diff --git a/framework/src/org/apache/cordova/StandAlone.java b/framework/src/org/apache/cordova/StandAlone.java index d41771d7..82d95f27 100644 --- a/framework/src/org/apache/cordova/StandAlone.java +++ b/framework/src/org/apache/cordova/StandAlone.java @@ -18,18 +18,18 @@ */ package org.apache.cordova; -import java.lang.reflect.Field; +//import java.lang.reflect.Field; -import android.app.Activity; +//import android.app.Activity; import android.os.Bundle; public class StandAlone extends DroidGap { - - @Override - public void onCreate(Bundle savedInstanceState) { + + @Override + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - super.loadUrl("file:///android_asset/www/index.html"); - } - + + super.loadUrl("file:///android_asset/www/index.html"); + } + } diff --git a/framework/src/org/apache/cordova/Storage.java b/framework/src/org/apache/cordova/Storage.java index 98ec4225..f57621ba 100755 --- a/framework/src/org/apache/cordova/Storage.java +++ b/framework/src/org/apache/cordova/Storage.java @@ -37,203 +37,202 @@ import android.database.sqlite.*; */ public class Storage extends Plugin { - // Data Definition Language - private static final String ALTER = "alter"; - private static final String CREATE = "create"; - private static final String DROP = "drop"; - private static final String TRUNCATE = "truncate"; - - SQLiteDatabase myDb = null; // Database object - String path = null; // Database path - String dbName = null; // Database name + // Data Definition Language + private static final String ALTER = "alter"; + private static final String CREATE = "create"; + private static final String DROP = "drop"; + private static final String TRUNCATE = "truncate"; - /** - * Constructor. - */ - public Storage() { - } + SQLiteDatabase myDb = null; // Database object + String path = null; // Database path + String dbName = null; // Database name - /** - * Executes the request and returns PluginResult. - * - * @param action - * The action to execute. - * @param args - * JSONArry of arguments for the plugin. - * @param callbackId - * The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public PluginResult execute(String action, JSONArray args, String callbackId) { - PluginResult.Status status = PluginResult.Status.OK; - String result = ""; + /** + * Constructor. + */ + public Storage() { + } - try { - if (action.equals("openDatabase")) { - this.openDatabase(args.getString(0), args.getString(1), - args.getString(2), args.getLong(3)); - } else if (action.equals("executeSql")) { - String[] s = null; - if (args.isNull(1)) { - s = new String[0]; - } else { - JSONArray a = args.getJSONArray(1); - int len = a.length(); - s = new String[len]; - for (int i = 0; i < len; i++) { - s[i] = a.getString(i); - } - } - this.executeSql(args.getString(0), s, args.getString(2)); - } - return new PluginResult(status, result); - } catch (JSONException e) { - return new PluginResult(PluginResult.Status.JSON_EXCEPTION); - } - } + /** + * Executes the request and returns PluginResult. + * + * @param action + * The action to execute. + * @param args + * JSONArry of arguments for the plugin. + * @param callbackId + * The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; - /** - * Identifies if action to be executed returns a value and should be run - * synchronously. - * - * @param action - * The action to execute - * @return T=returns value - */ - public boolean isSynch(String action) { - return true; - } + try { + if (action.equals("openDatabase")) { + this.openDatabase(args.getString(0), args.getString(1), + args.getString(2), args.getLong(3)); + } else if (action.equals("executeSql")) { + String[] s = null; + if (args.isNull(1)) { + s = new String[0]; + } else { + JSONArray a = args.getJSONArray(1); + int len = a.length(); + s = new String[len]; + for (int i = 0; i < len; i++) { + s[i] = a.getString(i); + } + } + this.executeSql(args.getString(0), s, args.getString(2)); + } + return new PluginResult(status, result); + } catch (JSONException e) { + return new PluginResult(PluginResult.Status.JSON_EXCEPTION); + } + } - /** - * Clean up and close database. - */ - @Override - public void onDestroy() { - if (this.myDb != null) { - this.myDb.close(); - this.myDb = null; - } - } + /** + * Identifies if action to be executed returns a value and should be run + * synchronously. + * + * @param action + * The action to execute + * @return T=returns value + */ + public boolean isSynch(String action) { + return true; + } - // -------------------------------------------------------------------------- - // LOCAL METHODS - // -------------------------------------------------------------------------- + /** + * Clean up and close database. + */ + @Override + public void onDestroy() { + if (this.myDb != null) { + this.myDb.close(); + this.myDb = null; + } + } - /** - * Open database. - * - * @param db - * The name of the database - * @param version - * The version - * @param display_name - * The display name - * @param size - * The size in bytes - */ - public void openDatabase(String db, String version, String display_name, - long size) { + // -------------------------------------------------------------------------- + // LOCAL METHODS + // -------------------------------------------------------------------------- - // If database is open, then close it - if (this.myDb != null) { - this.myDb.close(); - } + /** + * Open database. + * + * @param db + * The name of the database + * @param version + * The version + * @param display_name + * The display name + * @param size + * The size in bytes + */ + public void openDatabase(String db, String version, String display_name, + long size) { - // If no database path, generate from application package - if (this.path == null) { - this.path = this.ctx.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); - } + // If database is open, then close it + if (this.myDb != null) { + this.myDb.close(); + } - this.dbName = this.path + File.pathSeparator + db + ".db"; - this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null); - } + // If no database path, generate from application package + if (this.path == null) { + this.path = this.ctx.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); + } - /** - * Execute SQL statement. - * - * @param query - * The SQL query - * @param params - * Parameters for the query - * @param tx_id - * Transaction id - */ - public void executeSql(String query, String[] params, String tx_id) { - try { - if (isDDL(query)) { - this.myDb.execSQL(query); - this.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', '');"); - } - else { - Cursor myCursor = this.myDb.rawQuery(query, params); - this.processResults(myCursor, tx_id); - myCursor.close(); - } - } - catch (SQLiteException ex) { - ex.printStackTrace(); - System.out.println("Storage.executeSql(): Error=" + ex.getMessage()); - - // Send error message back to JavaScript - this.sendJavascript("cordova.require('cordova/plugin/android/storage').fail('" + ex.getMessage() + "','" + tx_id + "');"); - } - } + this.dbName = this.path + File.pathSeparator + db + ".db"; + this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null); + } - /** - * Checks to see the the query is a Data Definintion command - * - * @param query to be executed - * @return true if it is a DDL command, false otherwise - */ - private boolean isDDL(String query) { - String cmd = query.toLowerCase(); - if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) { - return true; - } - return false; - } + /** + * Execute SQL statement. + * + * @param query + * The SQL query + * @param params + * Parameters for the query + * @param tx_id + * Transaction id + */ + public void executeSql(String query, String[] params, String tx_id) { + try { + if (isDDL(query)) { + this.myDb.execSQL(query); + this.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', '');"); + } + else { + Cursor myCursor = this.myDb.rawQuery(query, params); + this.processResults(myCursor, tx_id); + myCursor.close(); + } + } catch (SQLiteException ex) { + ex.printStackTrace(); + System.out.println("Storage.executeSql(): Error=" + ex.getMessage()); - /** - * Process query results. - * - * @param cur - * Cursor into query results - * @param tx_id - * Transaction id - */ - public void processResults(Cursor cur, String tx_id) { + // Send error message back to JavaScript + this.sendJavascript("cordova.require('cordova/plugin/android/storage').fail('" + ex.getMessage() + "','" + tx_id + "');"); + } + } - String result = "[]"; - // If query result has rows + /** + * Checks to see the the query is a Data Definintion command + * + * @param query to be executed + * @return true if it is a DDL command, false otherwise + */ + private boolean isDDL(String query) { + String cmd = query.toLowerCase(); + if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) { + return true; + } + return false; + } - if (cur.moveToFirst()) { - JSONArray fullresult = new JSONArray(); - String key = ""; - String value = ""; - int colCount = cur.getColumnCount(); + /** + * Process query results. + * + * @param cur + * Cursor into query results + * @param tx_id + * Transaction id + */ + public void processResults(Cursor cur, String tx_id) { - // Build up JSON result object for each row - do { - JSONObject row = new JSONObject(); - try { - for (int i = 0; i < colCount; ++i) { - key = cur.getColumnName(i); - value = cur.getString(i); - row.put(key, value); - } - fullresult.put(row); + String result = "[]"; + // If query result has rows - } catch (JSONException e) { - e.printStackTrace(); - } + if (cur.moveToFirst()) { + JSONArray fullresult = new JSONArray(); + String key = ""; + String value = ""; + int colCount = cur.getColumnCount(); - } while (cur.moveToNext()); + // Build up JSON result object for each row + do { + JSONObject row = new JSONObject(); + try { + for (int i = 0; i < colCount; ++i) { + key = cur.getColumnName(i); + value = cur.getString(i); + row.put(key, value); + } + fullresult.put(row); - result = fullresult.toString(); - } + } catch (JSONException e) { + e.printStackTrace(); + } - // Let JavaScript know that there are no more rows - this.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', " + result + ");"); - } + } while (cur.moveToNext()); + + result = fullresult.toString(); + } + + // Let JavaScript know that there are no more rows + this.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', " + result + ");"); + } } diff --git a/framework/src/org/apache/cordova/TempListener.java b/framework/src/org/apache/cordova/TempListener.java index 936343a0..0b5b002c 100755 --- a/framework/src/org/apache/cordova/TempListener.java +++ b/framework/src/org/apache/cordova/TempListener.java @@ -25,7 +25,6 @@ import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; - import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; @@ -33,80 +32,81 @@ import android.hardware.SensorManager; import android.content.Context; public class TempListener extends Plugin implements SensorEventListener { - - Sensor mSensor; - private SensorManager sensorManager; - - /** - * Constructor. - */ - public TempListener() { - } - /** - * Sets the context of the Command. This can then be used to do things like - * get file paths associated with the Activity. - * - * @param ctx The context of the main Activity. - */ - public void setContext(Context ctx) { - super.setContext(ctx); - this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE); - } + Sensor mSensor; + private SensorManager sensorManager; + + /** + * Constructor. + */ + public TempListener() { + } + + /** + * Sets the context of the Command. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param ctx The context of the main Activity. + */ + public void setContext(CordovaInterface ctx) { + super.setContext(ctx); + this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE); + } + + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public PluginResult execute(String action, JSONArray args, String callbackId) { + PluginResult.Status status = PluginResult.Status.OK; + String result = ""; + + if (action.equals("start")) { + this.start(); + } + else if (action.equals("stop")) { + this.stop(); + } + return new PluginResult(status, result); + } - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public PluginResult execute(String action, JSONArray args, String callbackId) { - PluginResult.Status status = PluginResult.Status.OK; - String result = ""; - - if (action.equals("start")) { - this.start(); - } - else if (action.equals("stop")) { - this.stop(); - } - return new PluginResult(status, result); - } - /** * Called by AccelBroker when listener is to be shut down. * Stop listener. */ public void onDestroy() { - this.stop(); + this.stop(); } //-------------------------------------------------------------------------- // LOCAL METHODS //-------------------------------------------------------------------------- - public void start() { - List list = this.sensorManager.getSensorList(Sensor.TYPE_TEMPERATURE); - if (list.size() > 0) { - this.mSensor = list.get(0); - this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL); - } - } - - public void stop() { - this.sensorManager.unregisterListener(this); - } - - public void onAccuracyChanged(Sensor sensor, int accuracy) { - // TODO Auto-generated method stub - } + public void start() { + @SuppressWarnings("deprecation") + List list = this.sensorManager.getSensorList(Sensor.TYPE_TEMPERATURE); + if (list.size() > 0) { + this.mSensor = list.get(0); + this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL); + } + } - public void onSensorChanged(SensorEvent event) { - // We want to know what temp this is. - float temp = event.values[0]; - this.sendJavascript("gotTemp(" + temp + ");"); - } + public void stop() { + this.sensorManager.unregisterListener(this); + } + + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // TODO Auto-generated method stub + } + + public void onSensorChanged(SensorEvent event) { + // We want to know what temp this is. + float temp = event.values[0]; + this.sendJavascript("gotTemp(" + temp + ");"); + } } diff --git a/framework/src/org/apache/cordova/api/CordovaInterface.java b/framework/src/org/apache/cordova/api/CordovaInterface.java index b25e972d..5d56039c 100755 --- a/framework/src/org/apache/cordova/api/CordovaInterface.java +++ b/framework/src/org/apache/cordova/api/CordovaInterface.java @@ -18,23 +18,22 @@ */ package org.apache.cordova.api; -import java.util.HashMap; +//import java.util.HashMap; import android.app.Activity; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentResolver; -import android.content.Context; +//import android.app.Service; +//import android.content.BroadcastReceiver; +//import android.content.ContentResolver; +//import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; -import android.content.res.AssetManager; -import android.content.res.Resources; -import android.database.Cursor; -import android.hardware.SensorManager; -import android.net.Uri; -import android.view.Menu; -import android.view.MenuItem; - +//import android.content.IntentFilter; +//import android.content.res.AssetManager; +//import android.content.res.Resources; +//import android.database.Cursor; +//import android.hardware.SensorManager; +//import android.net.Uri; +//import android.view.Menu; +//import android.view.MenuItem; /** * The Cordova activity abstract class that is extended by DroidGap. @@ -42,7 +41,6 @@ import android.view.MenuItem; */ public interface CordovaInterface { - /** * Launch an activity for which you would like a result when it finished. When this activity exits, * your onActivityResult() method will be called. @@ -52,22 +50,24 @@ public interface CordovaInterface { * @param requestCode The request code that is passed to callback to identify the activity */ abstract public void startActivityForResult(IPlugin command, Intent intent, int requestCode); - + /** * Set the plugin to be called when a sub-activity exits. * * @param plugin The plugin on which onActivityResult is to be called */ abstract public void setActivityResultCallback(IPlugin plugin); - + /** - * Causes the Activity to override the back button behaviour + * Causes the Activity to override the back button behavior. + * * @param override */ public abstract void bindBackButton(boolean override); /** - * A hook required to check if the Back Button is bound + * A hook required to check if the Back Button is bound. + * * @return */ public abstract boolean isBackButtonBound(); @@ -76,16 +76,20 @@ public interface CordovaInterface { * Hook in DroidGap for menu plugins * (This is in the Android SDK, do we need this on the Interface?) */ - - public abstract boolean onCreateOptionsMenu(Menu menu); - - public abstract boolean onPrepareOptionsMenu(Menu menu); - - public abstract boolean onOptionsItemSelected(MenuItem item); - - - - + + //public abstract boolean onCreateOptionsMenu(Menu menu); + + //public abstract boolean onPrepareOptionsMenu(Menu menu); + + //public abstract boolean onOptionsItemSelected(MenuItem item); + + /** + * Get the Android activity. + * + * @return + */ + public abstract Activity getActivity(); + /** * @deprecated * Add services to res/xml/plugins.xml instead. @@ -95,17 +99,17 @@ public interface CordovaInterface { * @param serviceType * @param className */ - @Deprecated - abstract public void addService(String serviceType, String className); - +// @Deprecated +// abstract public void addService(String serviceType, String className); + /** * @deprecated * Send JavaScript statement back to JavaScript. * * @param message */ - @Deprecated - abstract public void sendJavascript(String statement); +// @Deprecated +// abstract public void sendJavascript(String statement); /** * @deprecated @@ -113,9 +117,8 @@ public interface CordovaInterface { * * @param intent The intent to start */ - @Deprecated - abstract public void startActivity(Intent intent); - +// @Deprecated +// abstract public void startActivity(Intent intent); /** * @deprecated @@ -123,9 +126,9 @@ public interface CordovaInterface { * * @param url The URL to load. */ - @Deprecated - abstract public void loadUrl(String url); - +// @Deprecated +// abstract public void loadUrl(String url); + /** * @deprecated * Send a message to all plugins. @@ -133,66 +136,84 @@ public interface CordovaInterface { * @param id The message id * @param data The message data */ - @Deprecated - abstract public void postMessage(String id, Object data); - - @Deprecated - public abstract Resources getResources(); +// @Deprecated +// abstract public void postMessage(String id, Object data); - @Deprecated - public abstract String getPackageName(); +// @Deprecated +// public abstract Resources getResources(); - @Deprecated - public abstract Object getSystemService(String service); +// @Deprecated +// public abstract String getPackageName(); - @Deprecated - public abstract Context getContext(); - - @Deprecated - public abstract Context getBaseContext(); +// @Deprecated +// public abstract Object getSystemService(String service); - @Deprecated - public abstract Intent registerReceiver(BroadcastReceiver receiver, - IntentFilter intentFilter); +// @Deprecated +// public abstract Context getContext(); - @Deprecated - public abstract ContentResolver getContentResolver(); +// @Deprecated +// public abstract Context getBaseContext(); - @Deprecated - public abstract void unregisterReceiver(BroadcastReceiver receiver); +// @Deprecated +// public abstract Intent registerReceiver(BroadcastReceiver receiver, +// IntentFilter intentFilter); - @Deprecated - public abstract Cursor managedQuery(Uri uri, String[] projection, String selection, - String[] selectionArgs, String sortOrder); - - @Deprecated - public abstract void runOnUiThread(Runnable runnable); +// @Deprecated +// public abstract ContentResolver getContentResolver(); - @Deprecated - public abstract AssetManager getAssets(); +// @Deprecated +// public abstract void unregisterReceiver(BroadcastReceiver receiver); - @Deprecated - public abstract void clearCache(); +// @Deprecated +// public abstract Cursor managedQuery(Uri uri, String[] projection, String selection, +// String[] selectionArgs, String sortOrder); - @Deprecated - public abstract void clearHistory(); +// @Deprecated +// public abstract void runOnUiThread(Runnable runnable); - @Deprecated - public abstract boolean backHistory(); +// @Deprecated +// public abstract AssetManager getAssets(); + +// @Deprecated +// public abstract void clearCache(); + +// @Deprecated +// public abstract void clearHistory(); + +// @Deprecated +// public abstract boolean backHistory(); //public abstract void addWhiteListEntry(String origin, boolean subdomains); @Deprecated public abstract void cancelLoadUrl(); - @Deprecated - public abstract void showWebPage(String url, boolean openExternal, - boolean clearHistory, HashMap params); +// @Deprecated +// public abstract void showWebPage(String url, boolean openExternal, +// boolean clearHistory, HashMap params); - @Deprecated - public abstract Context getApplicationContext(); +// @Deprecated +// public abstract Context getApplicationContext(); + +// @Deprecated +// public abstract boolean isUrlWhiteListed(String source); + + /** + * Called when a message is sent to plugin. + * + * @param id The message id + * @param data The message data + */ + public void onMessage(String id, Object data); + + /** + * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). + * The errorCode parameter corresponds to one of the ERROR_* constants. + * + * @param errorCode The error code corresponding to an ERROR_* value. + * @param description A String describing the error. + * @param failingUrl The url that failed to load. + */ + //public void onReceivedError(final int errorCode, final String description, final String failingUrl); - @Deprecated - public abstract boolean isUrlWhiteListed(String source); - } diff --git a/framework/src/org/apache/cordova/api/IPlugin.java b/framework/src/org/apache/cordova/api/IPlugin.java index 77579630..82d9bb7a 100755 --- a/framework/src/org/apache/cordova/api/IPlugin.java +++ b/framework/src/org/apache/cordova/api/IPlugin.java @@ -21,9 +21,10 @@ package org.apache.cordova.api; import org.apache.cordova.CordovaWebView; import org.json.JSONArray; -import android.content.Context; +//import android.content.Context; import android.content.Intent; -import android.webkit.WebView; + +//import android.webkit.WebView; /** * Plugin interface must be implemented by any plugin classes. @@ -31,40 +32,40 @@ import android.webkit.WebView; * The execute method is called by the PluginManager. */ public interface IPlugin { - - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - PluginResult execute(String action, JSONArray args, String callbackId); - /** - * Identifies if action to be executed returns a value and should be run synchronously. - * - * @param action The action to execute - * @return T=returns value - */ - public boolean isSynch(String action); + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + PluginResult execute(String action, JSONArray args, String callbackId); - /** - * Sets the context of the Plugin. This can then be used to do things like - * get file paths associated with the Activity. - * - * @param ctx The context of the main Activity. - */ - void setContext(Context ctx); + /** + * Identifies if action to be executed returns a value and should be run synchronously. + * + * @param action The action to execute + * @return T=returns value + */ + public boolean isSynch(String action); - /** - * Sets the main View of the application, this is the WebView within which - * a Cordova app runs. - * - * @param webView The Cordova WebView - */ - void setView(CordovaWebView webView); + /** + * Sets the context of the Plugin. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param ctx The context of the main Activity. + */ + void setContext(CordovaInterface ctx); + + /** + * Sets the main View of the application, this is the WebView within which + * a Cordova app runs. + * + * @param webView The Cordova WebView + */ + void setView(CordovaWebView webView); /** * Called when the system is about to start resuming a previous activity. @@ -79,7 +80,7 @@ public interface IPlugin { * @param multitasking Flag indicating if multitasking is turned on for app */ void onResume(boolean multitasking); - + /** * Called when the activity receives a new intent. */ @@ -89,7 +90,7 @@ public interface IPlugin { * The final call you receive before your activity is destroyed. */ void onDestroy(); - + /** * Called when a message is sent to plugin. * diff --git a/framework/src/org/apache/cordova/api/Plugin.java b/framework/src/org/apache/cordova/api/Plugin.java index 36faa036..f7c18656 100755 --- a/framework/src/org/apache/cordova/api/Plugin.java +++ b/framework/src/org/apache/cordova/api/Plugin.java @@ -22,9 +22,10 @@ import org.apache.cordova.CordovaWebView; import org.json.JSONArray; import org.json.JSONObject; -import android.content.Context; +//import android.content.Context; import android.content.Intent; -import android.webkit.WebView; + +//import android.webkit.WebView; /** * Plugin interface must be implemented by any plugin classes. @@ -33,50 +34,50 @@ import android.webkit.WebView; */ public abstract class Plugin implements IPlugin { - public String id; + public String id; public CordovaWebView webView; // WebView object - public Context ctx; // CordovaActivity object + public CordovaInterface ctx; // CordovaActivity object - /** - * Executes the request and returns PluginResult. - * - * @param action The action to execute. - * @param args JSONArry of arguments for the plugin. - * @param callbackId The callback id used when calling back into JavaScript. - * @return A PluginResult object with a status and message. - */ - public abstract PluginResult execute(String action, JSONArray args, String callbackId); + /** + * Executes the request and returns PluginResult. + * + * @param action The action to execute. + * @param args JSONArry of arguments for the plugin. + * @param callbackId The callback id used when calling back into JavaScript. + * @return A PluginResult object with a status and message. + */ + public abstract PluginResult execute(String action, JSONArray args, String callbackId); - /** - * Identifies if action to be executed returns a value and should be run synchronously. - * - * @param action The action to execute - * @return T=returns value - */ - public boolean isSynch(String action) { - return false; - } + /** + * Identifies if action to be executed returns a value and should be run synchronously. + * + * @param action The action to execute + * @return T=returns value + */ + public boolean isSynch(String action) { + return false; + } - /** - * Sets the context of the Plugin. This can then be used to do things like - * get file paths associated with the Activity. - * - * @param ctx The context of the main Activity. - */ - public void setContext(Context ctx) { - this.ctx = ctx; - } + /** + * Sets the context of the Plugin. This can then be used to do things like + * get file paths associated with the Activity. + * + * @param ctx The context of the main Activity. + */ + public void setContext(CordovaInterface ctx) { + this.ctx = ctx; + } + + /** + * Sets the main View of the application, this is the WebView within which + * a Cordova app runs. + * + * @param webView The Cordova WebView + */ + public void setView(CordovaWebView webView) { + this.webView = webView; + } - /** - * Sets the main View of the application, this is the WebView within which - * a Cordova app runs. - * - * @param webView The Cordova WebView - */ - public void setView(CordovaWebView webView) { - this.webView = webView; - } - /** * Called when the system is about to start resuming a previous activity. * @@ -92,19 +93,19 @@ public abstract class Plugin implements IPlugin { */ public void onResume(boolean multitasking) { } - + /** * Called when the activity receives a new intent. */ public void onNewIntent(Intent intent) { } - + /** * The final call you receive before your activity is destroyed. */ public void onDestroy() { } - + /** * Called when a message is sent to plugin. * @@ -133,7 +134,7 @@ public abstract class Plugin implements IPlugin { * @return Return true to prevent the URL from loading. Default is false. */ public boolean onOverrideUrlLoading(String url) { - return false; + return false; } /** @@ -143,7 +144,7 @@ public abstract class Plugin implements IPlugin { * @param statement */ public void sendJavascript(String statement) { - webView.sendJavascript(statement); + this.webView.sendJavascript(statement); } /** @@ -154,10 +155,10 @@ public abstract class Plugin implements IPlugin { * call success(...) or error(...) * * @param pluginResult The result to return. - * @param callbackId The callback id used when calling back into JavaScript. + * @param callbackId The callback id used when calling back into JavaScript. */ public void success(PluginResult pluginResult, String callbackId) { - webView.sendJavascript(pluginResult.toSuccessCallbackString(callbackId)); + this.webView.sendJavascript(pluginResult.toSuccessCallbackString(callbackId)); } /** @@ -167,7 +168,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) { - webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId)); + this.webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId)); } /** @@ -177,17 +178,17 @@ public abstract class Plugin implements IPlugin { * @param callbackId The callback id used when calling back into JavaScript. */ public void success(String message, String callbackId) { - webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId)); + this.webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId)); } - + /** * Call the JavaScript error callback for this plugin. * * @param pluginResult The result to return. - * @param callbackId The callback id used when calling back into JavaScript. + * @param callbackId The callback id used when calling back into JavaScript. */ public void error(PluginResult pluginResult, String callbackId) { - webView.sendJavascript(pluginResult.toErrorCallbackString(callbackId)); + this.webView.sendJavascript(pluginResult.toErrorCallbackString(callbackId)); } /** @@ -197,7 +198,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) { - webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId)); + this.webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId)); } /** @@ -207,6 +208,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) { - webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId)); + this.webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId)); } } diff --git a/framework/src/org/apache/cordova/api/PluginEntry.java b/framework/src/org/apache/cordova/api/PluginEntry.java index 2c4cdb83..057c0050 100755 --- a/framework/src/org/apache/cordova/api/PluginEntry.java +++ b/framework/src/org/apache/cordova/api/PluginEntry.java @@ -20,8 +20,8 @@ package org.apache.cordova.api; import org.apache.cordova.CordovaWebView; -import android.content.Context; -import android.webkit.WebView; +//import android.content.Context; +//import android.webkit.WebView; /** * This class represents a service entry object. @@ -69,12 +69,12 @@ public class PluginEntry { * * @return The plugin object */ - @SuppressWarnings("unchecked") - public IPlugin createPlugin(CordovaWebView webView, Context ctx) { + public IPlugin createPlugin(CordovaWebView webView, CordovaInterface ctx) { if (this.plugin != null) { return this.plugin; } try { + @SuppressWarnings("rawtypes") Class c = getClassByName(this.pluginClass); if (isCordovaPlugin(c)) { this.plugin = (IPlugin) c.newInstance(); @@ -96,7 +96,7 @@ public class PluginEntry { * @return * @throws ClassNotFoundException */ - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") private Class getClassByName(final String clazz) throws ClassNotFoundException { Class c = null; if (clazz != null) { @@ -112,7 +112,7 @@ public class PluginEntry { * @param c The class to check the interfaces of. * @return Boolean indicating if the class implements org.apache.cordova.api.Plugin */ - @SuppressWarnings("unchecked") + @SuppressWarnings("rawtypes") private boolean isCordovaPlugin(Class c) { if (c != null) { return org.apache.cordova.api.Plugin.class.isAssignableFrom(c) || org.apache.cordova.api.IPlugin.class.isAssignableFrom(c); diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index d34fe920..c931477f 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -28,10 +28,8 @@ import org.json.JSONArray; import org.json.JSONException; import org.xmlpull.v1.XmlPullParserException; -import android.content.Context; import android.content.Intent; import android.content.res.XmlResourceParser; -import android.webkit.WebView; /** * PluginManager is exposed to JavaScript in the Cordova WebView. @@ -45,7 +43,7 @@ public class PluginManager { // List of service entries private final HashMap entries = new HashMap(); - private final Context ctx; + private final CordovaInterface ctx; private final CordovaWebView app; // Flag to track first time through @@ -61,26 +59,25 @@ public class PluginManager { * @param app * @param ctx */ - public PluginManager(CordovaWebView app, Context ctx) { + public PluginManager(CordovaWebView app, CordovaInterface ctx) { this.ctx = ctx; this.app = app; this.firstRun = true; } - - public PluginManager(WebView mApp, CordovaInterface mCtx) throws Exception { - this.ctx = mCtx.getContext(); - if(CordovaWebView.class.isInstance(mApp)) - { - this.app = (CordovaWebView) mApp; - } - else - { - //Throw an exception here - throw new Exception(); - } - } - + // Called by com.phonegap.api.PluginManager only +// public PluginManager(WebView mApp, CordovaInterface mCtx) throws Exception { +// this.ctx = mCtx; //mCtx.getContext(); +// if (CordovaWebView.class.isInstance(mApp)) +// { +// this.app = (CordovaWebView) mApp; +// } +// else +// { +// //Throw an exception here +// throw new Exception(); +// } +// } /** * Init when loading a new HTML page into webview. @@ -89,9 +86,9 @@ public class PluginManager { LOG.d(TAG, "init()"); // If first time, then load plugins from plugins.xml file - if (firstRun) { + if (this.firstRun) { this.loadPlugins(); - firstRun = false; + this.firstRun = false; } // Stop plugins on current HTML page and discard plugin objects @@ -109,11 +106,11 @@ public class PluginManager { * Load plugins from res/xml/plugins.xml */ public void loadPlugins() { - int id = ctx.getResources().getIdentifier("plugins", "xml", ctx.getPackageName()); + int id = this.ctx.getActivity().getResources().getIdentifier("plugins", "xml", this.ctx.getActivity().getPackageName()); if (id == 0) { - pluginConfigurationMissing(); + this.pluginConfigurationMissing(); } - XmlResourceParser xml = ctx.getResources().getXml(id); + XmlResourceParser xml = this.ctx.getActivity().getResources().getXml(id); int eventType = -1; String service = "", pluginClass = ""; boolean onload = false; @@ -184,14 +181,13 @@ public class PluginManager { * * @return JSON encoded string with a response message and status. */ - @SuppressWarnings("unchecked") public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) { PluginResult cr = null; boolean runAsync = async; try { final JSONArray args = new JSONArray(jsonArgs); final IPlugin plugin = this.getPlugin(service); - final Context ctx = this.ctx; + //final CordovaInterface ctx = this.ctx; if (plugin != null) { runAsync = async && !plugin.isSynch(action); if (runAsync) { @@ -257,7 +253,7 @@ public class PluginManager { * @return IPlugin or null */ private IPlugin getPlugin(String service) { - PluginEntry entry = entries.get(service); + PluginEntry entry = this.entries.get(service); if (entry == null) { return null; } @@ -334,6 +330,7 @@ public class PluginManager { * @param data The message data */ public void postMessage(String id, Object data) { + this.ctx.onMessage(id, data); for (PluginEntry entry : this.entries.values()) { if (entry.plugin != null) { entry.plugin.onMessage(id, data); diff --git a/framework/src/org/apache/cordova/api/PluginResult.java b/framework/src/org/apache/cordova/api/PluginResult.java index c3ef6d6a..8f5f7fdb 100755 --- a/framework/src/org/apache/cordova/api/PluginResult.java +++ b/framework/src/org/apache/cordova/api/PluginResult.java @@ -21,99 +21,99 @@ package org.apache.cordova.api; import org.json.JSONArray; import org.json.JSONObject; -import android.util.Log; +//import android.util.Log; public class PluginResult { - private final int status; - private final String message; - private boolean keepCallback = false; - - public PluginResult(Status status) { - this.status = status.ordinal(); - this.message = "'" + PluginResult.StatusMessages[this.status] + "'"; - } - - public PluginResult(Status status, String message) { - this.status = status.ordinal(); - this.message = JSONObject.quote(message); - } + private final int status; + private final String message; + private boolean keepCallback = false; - public PluginResult(Status status, JSONArray message) { - this.status = status.ordinal(); - this.message = message.toString(); - } + public PluginResult(Status status) { + this.status = status.ordinal(); + this.message = "'" + PluginResult.StatusMessages[this.status] + "'"; + } - public PluginResult(Status status, JSONObject message) { - this.status = status.ordinal(); - this.message = message.toString(); - } + public PluginResult(Status status, String message) { + this.status = status.ordinal(); + this.message = JSONObject.quote(message); + } - public PluginResult(Status status, int i) { - this.status = status.ordinal(); - this.message = ""+i; - } + public PluginResult(Status status, JSONArray message) { + this.status = status.ordinal(); + this.message = message.toString(); + } - public PluginResult(Status status, float f) { - this.status = status.ordinal(); - this.message = ""+f; - } + public PluginResult(Status status, JSONObject message) { + this.status = status.ordinal(); + this.message = message.toString(); + } - public PluginResult(Status status, boolean b) { - this.status = status.ordinal(); - this.message = ""+b; - } - - public void setKeepCallback(boolean b) { - this.keepCallback = b; - } - - public int getStatus() { - return status; - } + public PluginResult(Status status, int i) { + this.status = status.ordinal(); + this.message = "" + i; + } - public String getMessage() { - return message; - } - - public boolean getKeepCallback() { - return this.keepCallback; - } - - public String getJSONString() { - return "{status:" + this.status + ",message:" + this.message + ",keepCallback:" + this.keepCallback + "}"; - } - - public String toSuccessCallbackString(String callbackId) { - return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");"; - } - - public String toErrorCallbackString(String callbackId) { - return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");"; - } - - public static String[] StatusMessages = new String[] { - "No result", - "OK", - "Class not found", - "Illegal access", - "Instantiation error", - "Malformed url", - "IO error", - "Invalid action", - "JSON error", - "Error" - }; - - public enum Status { - NO_RESULT, - OK, - CLASS_NOT_FOUND_EXCEPTION, - ILLEGAL_ACCESS_EXCEPTION, - INSTANTIATION_EXCEPTION, - MALFORMED_URL_EXCEPTION, - IO_EXCEPTION, - INVALID_ACTION, - JSON_EXCEPTION, - ERROR - } + public PluginResult(Status status, float f) { + this.status = status.ordinal(); + this.message = "" + f; + } + + public PluginResult(Status status, boolean b) { + this.status = status.ordinal(); + this.message = "" + b; + } + + public void setKeepCallback(boolean b) { + this.keepCallback = b; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } + + public boolean getKeepCallback() { + return this.keepCallback; + } + + public String getJSONString() { + return "{status:" + this.status + ",message:" + this.message + ",keepCallback:" + this.keepCallback + "}"; + } + + public String toSuccessCallbackString(String callbackId) { + return "cordova.callbackSuccess('" + callbackId + "'," + this.getJSONString() + ");"; + } + + public String toErrorCallbackString(String callbackId) { + return "cordova.callbackError('" + callbackId + "', " + this.getJSONString() + ");"; + } + + public static String[] StatusMessages = new String[] { + "No result", + "OK", + "Class not found", + "Illegal access", + "Instantiation error", + "Malformed url", + "IO error", + "Invalid action", + "JSON error", + "Error" + }; + + public enum Status { + NO_RESULT, + OK, + CLASS_NOT_FOUND_EXCEPTION, + ILLEGAL_ACCESS_EXCEPTION, + INSTANTIATION_EXCEPTION, + MALFORMED_URL_EXCEPTION, + IO_EXCEPTION, + INVALID_ACTION, + JSON_EXCEPTION, + ERROR + } } From 3829df665fa67f9ccac2c7f08fa1a6e1f194f600 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 22:06:33 -0500 Subject: [PATCH 02/10] Formating and removal of commented code. --- framework/src/org/apache/cordova/App.java | 12 +- .../src/org/apache/cordova/AudioHandler.java | 5 +- .../org/apache/cordova/CallbackServer.java | 573 +++++++++--------- framework/src/org/apache/cordova/Capture.java | 2 - .../org/apache/cordova/CordovaException.java | 5 + .../org/apache/cordova/CordovaWebView.java | 29 +- .../apache/cordova/CordovaWebViewClient.java | 9 - .../org/apache/cordova/DirectoryManager.java | 197 +++--- .../src/org/apache/cordova/DroidGap.java | 270 +-------- .../src/org/apache/cordova/FileTransfer.java | 1 + .../src/org/apache/cordova/FileUtils.java | 5 +- .../src/org/apache/cordova/HttpHandler.java | 4 +- .../apache/cordova/api/CordovaInterface.java | 140 ----- .../src/org/apache/cordova/api/IPlugin.java | 2 - .../src/org/apache/cordova/api/Plugin.java | 6 +- .../org/apache/cordova/api/PluginManager.java | 14 - 16 files changed, 403 insertions(+), 871 deletions(-) diff --git a/framework/src/org/apache/cordova/App.java b/framework/src/org/apache/cordova/App.java index 75460d52..7df0aa94 100755 --- a/framework/src/org/apache/cordova/App.java +++ b/framework/src/org/apache/cordova/App.java @@ -25,11 +25,6 @@ import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - -//import android.webkit.WebView; -import android.app.Activity; -import android.view.View; - import java.util.HashMap; /** @@ -59,12 +54,6 @@ public class App extends Plugin { webView.postMessage("spinner", "stop"); } }); -// final CordovaWebView wv = this.webView; -// ((Activity) this.ctx).runOnUiThread(new Runnable() { -// public void run() { -// wv.setVisibility(View.VISIBLE); -// } -// }); } else if (action.equals("loadUrl")) { this.loadUrl(args.getString(0), args.optJSONObject(1)); @@ -168,6 +157,7 @@ public class App extends Plugin { /** * Cancel loadUrl before it has been loaded (Only works on a CordovaInterface class) */ + @Deprecated public void cancelLoadUrl() { this.ctx.cancelLoadUrl(); } diff --git a/framework/src/org/apache/cordova/AudioHandler.java b/framework/src/org/apache/cordova/AudioHandler.java index 644640c9..8acb52c0 100755 --- a/framework/src/org/apache/cordova/AudioHandler.java +++ b/framework/src/org/apache/cordova/AudioHandler.java @@ -22,16 +22,13 @@ import android.content.Context; import android.media.AudioManager; import java.util.ArrayList; -import org.apache.cordova.api.LOG; +//import org.apache.cordova.api.LOG; import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; - import java.util.HashMap; -//import java.util.Map.Entry; - /** * This class called by CordovaActivity to play and record audio. * The file can be local or over a network using http. diff --git a/framework/src/org/apache/cordova/CallbackServer.java b/framework/src/org/apache/cordova/CallbackServer.java index fbf5a98b..f5337227 100755 --- a/framework/src/org/apache/cordova/CallbackServer.java +++ b/framework/src/org/apache/cordova/CallbackServer.java @@ -25,11 +25,8 @@ import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.ServerSocket; import java.net.Socket; -import java.net.URLEncoder; import java.util.LinkedList; -import android.util.Log; - /** * 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 @@ -55,307 +52,309 @@ import android.util.Log; * 3. The client repeats #1 in loop. */ public class CallbackServer implements Runnable { - - private static final String LOG_TAG = "CallbackServer"; - /** - * The list of JavaScript statements to be sent to JavaScript. - */ - private LinkedList javascript; - - /** - * The port to listen on. - */ - private int port; - - /** - * The server thread. - */ - private Thread serverThread; - - /** - * Indicates the server is running. - */ - private boolean active; - - /** - * Indicates that the JavaScript statements list is empty - */ - private boolean empty; - - /** - * 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.empty = true; - this.port = 0; - this.javascript = new LinkedList(); - } - - /** - * 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) { - //Log.d(LOG_TAG, "CallbackServer.start("+url+")"); - this.active = false; - this.empty = true; - this.port = 0; - this.javascript = new LinkedList(); + @SuppressWarnings("unused") + private static final String LOG_TAG = "CallbackServer"; + + /** + * The list of JavaScript statements to be sent to JavaScript. + */ + private LinkedList javascript; + + /** + * The port to listen on. + */ + private int port; + + /** + * The server thread. + */ + private Thread serverThread; + + /** + * Indicates the server is running. + */ + private boolean active; + + /** + * Indicates that the JavaScript statements list is empty + */ + private boolean empty; + + /** + * 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.empty = true; + this.port = 0; + this.javascript = new LinkedList(); + } + + /** + * 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 + */ + @SuppressWarnings("deprecation") + public void init(String url) { + //Log.d(LOG_TAG, "CallbackServer.start("+url+")"); + this.active = false; + this.empty = true; + this.port = 0; + this.javascript = new LinkedList(); + + // 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(); + } + } - // 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(); - } - } - /** * Re-init when loading a new HTML page into webview. * * @param url The URL of the Cordova app being loaded */ - public void reinit(String url) { - this.stopServer(); - this.init(url); - } - - /** - * 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(); - } + public void reinit(String url) { + this.stopServer(); + this.init(url); + } - /** - * Restart the server on a new thread. - */ - public void restartServer() { - - // Stop server - this.stopServer(); - - // Start server again - this.startServer(); - } + /** + * Return if polling is being used instead of XHR. + * + * @return + */ + public boolean usePolling() { + return this.usePolling; + } - /** - * 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; - ServerSocket 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); + /** + * Get the port that this server is running on. + * + * @return + */ + public int getPort() { + return this.port; + } - 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"); + /** + * Get the security token that this server requires when calling getJavascript(). + * + * @return + */ + public String getToken() { + return this.token; + } - // 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.empty) { - try { - this.wait(10000); // prevent timeout from happening - //Log.d(LOG_TAG, "CallbackServer>>> break <<<"); - break; - } - catch (Exception e) { } - } - } + /** + * Start the server on a new thread. + */ + public void startServer() { + //Log.d(LOG_TAG, "CallbackServer.startServer()"); + this.active = false; - // If server is still running - if (this.active) { + // Start server on new thread + this.serverThread = new Thread(this); + this.serverThread.start(); + } - // If no data, then send 404 back to client before it times out - if (this.empty) { - //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"; - String js = this.getJavascript(); - if (js != null) { - response += encode(js, "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; + /** + * Restart the server on a new thread. + */ + public void restartServer() { - // Break out of server wait - synchronized (this) { - this.notify(); - } - } - } + // 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; + ServerSocket 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"); + + // 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.empty) { + 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 (this.empty) { + //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"; + String js = this.getJavascript(); + if (js != null) { + response += encode(js, "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; + + // Break out of server wait + synchronized (this) { + this.notify(); + } + } + } /** * Destroy */ public void destroy() { - this.stopServer(); + this.stopServer(); } - /** - * Get the number of JavaScript statements. - * - * @return int - */ - public int getSize() { - synchronized(this) { - int size = this.javascript.size(); - return size; - } - } - - /** - * Get the next JavaScript statement and remove from list. - * - * @return String - */ - public String getJavascript() { - synchronized(this) { - if (this.javascript.size() == 0) { - return null; - } - String statement = this.javascript.remove(0); - if (this.javascript.size() == 0) { - this.empty = true; - } - return statement; - } - } - - /** - * Add a JavaScript statement to the list. - * - * @param statement - */ - public void sendJavascript(String statement) { - synchronized (this) { - this.javascript.add(statement); - this.empty = false; - this.notify(); - } - } - - /* The Following code has been modified from original implementation of URLEncoder */ - - /* start */ - - /* + /** + * Get the number of JavaScript statements. + * + * @return int + */ + public int getSize() { + synchronized (this) { + int size = this.javascript.size(); + return size; + } + } + + /** + * Get the next JavaScript statement and remove from list. + * + * @return String + */ + public String getJavascript() { + synchronized (this) { + if (this.javascript.size() == 0) { + return null; + } + String statement = this.javascript.remove(0); + if (this.javascript.size() == 0) { + this.empty = true; + } + return statement; + } + } + + /** + * Add a JavaScript statement to the list. + * + * @param statement + */ + public void sendJavascript(String statement) { + synchronized (this) { + this.javascript.add(statement); + this.empty = false; + 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. @@ -371,7 +370,7 @@ public class CallbackServer implements Runnable { * See the License for the specific language governing permissions and * limitations under the License. */ - static final String digits = "0123456789ABCDEF"; + static final String digits = "0123456789ABCDEF"; /** * This will encode the return value to JavaScript. We revert the encoding for @@ -382,13 +381,13 @@ public class CallbackServer implements Runnable { * @param enc encoding type * @return encoded string */ - public static String encode(String s, String enc) throws UnsupportedEncodingException { + 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; @@ -426,6 +425,6 @@ public class CallbackServer implements Runnable { buf.append(digits.charAt(bytes[j] & 0xf)); } } - + /* end */ } diff --git a/framework/src/org/apache/cordova/Capture.java b/framework/src/org/apache/cordova/Capture.java index bfb5307d..ca81b005 100644 --- a/framework/src/org/apache/cordova/Capture.java +++ b/framework/src/org/apache/cordova/Capture.java @@ -22,7 +22,6 @@ import java.io.File; import java.io.IOException; import java.io.OutputStream; -import org.apache.cordova.api.CordovaInterface; import org.apache.cordova.api.LOG; import org.apache.cordova.api.Plugin; import org.apache.cordova.api.PluginResult; @@ -32,7 +31,6 @@ import org.json.JSONObject; import android.app.Activity; import android.content.ContentValues; -//import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; diff --git a/framework/src/org/apache/cordova/CordovaException.java b/framework/src/org/apache/cordova/CordovaException.java index da024970..01db3c45 100755 --- a/framework/src/org/apache/cordova/CordovaException.java +++ b/framework/src/org/apache/cordova/CordovaException.java @@ -20,4 +20,9 @@ package org.apache.cordova; public class CordovaException extends Exception { + + /** + * + */ + private static final long serialVersionUID = 1373339564758328799L; } diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 594b7bf8..a6f152ea 100644 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -22,7 +22,6 @@ package org.apache.cordova; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; -import java.util.Hashtable; import java.util.Iterator; import java.util.Stack; import java.util.regex.Matcher; @@ -47,9 +46,6 @@ public class CordovaWebView extends WebView { public static final String TAG = "CordovaWebView"; - /** The authorization tokens. */ - //private Hashtable authenticationTokens = new Hashtable(); - /** The whitelist **/ private ArrayList whiteList = new ArrayList(); private HashMap whiteListCache = new HashMap(); @@ -59,6 +55,7 @@ public class CordovaWebView extends WebView { /** Actvities and other important classes **/ private CordovaInterface mCtx; CordovaWebViewClient viewClient; + @SuppressWarnings("unused") private CordovaChromeClient chromeClient; //This is for the polyfil history @@ -71,12 +68,6 @@ public class CordovaWebView extends WebView { // Flag to track that a loadUrl timeout occurred int loadUrlTimeout = 0; - // LoadUrl timeout value in msec (default of 20 sec) - //protected int loadUrlTimeoutValue = 20000; - - //preferences read from cordova.xml - //protected PreferenceSet preferences; - /** * Constructor. * @@ -85,7 +76,6 @@ public class CordovaWebView extends WebView { public CordovaWebView(CordovaInterface context) { super(context.getActivity()); this.mCtx = context; - //preferences = new PreferenceSet(); this.loadConfiguration(); this.setup(); } @@ -99,7 +89,6 @@ public class CordovaWebView extends WebView { public CordovaWebView(CordovaInterface context, AttributeSet attrs) { super(context.getActivity(), attrs); this.mCtx = context; - //preferences = new PreferenceSet(); this.loadConfiguration(); this.setup(); } @@ -114,7 +103,6 @@ public class CordovaWebView extends WebView { public CordovaWebView(CordovaInterface context, AttributeSet attrs, int defStyle) { super(context.getActivity(), attrs, defStyle); this.mCtx = context; - //preferences = new PreferenceSet(); this.loadConfiguration(); this.setup(); } @@ -130,7 +118,6 @@ public class CordovaWebView extends WebView { public CordovaWebView(CordovaInterface context, AttributeSet attrs, int defStyle, boolean privateBrowsing) { super(context.getActivity(), attrs, defStyle, privateBrowsing); this.mCtx = context; - //preferences = new PreferenceSet(); this.loadConfiguration(); this.setup(); } @@ -138,6 +125,7 @@ public class CordovaWebView extends WebView { /** * Initialize webview. */ + @SuppressWarnings("deprecation") private void setup() { this.setInitialScale(0); @@ -158,10 +146,6 @@ public class CordovaWebView extends WebView { String databasePath = this.mCtx.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath(); settings.setDatabasePath(databasePath); - //Setup the WebChromeClient and WebViewClient - //setWebViewClient(new CordovaWebViewClient(mCtx, this)); - //setWebChromeClient(new CordovaChromeClient(mCtx, this)); - // Enable DOM storage settings.setDomStorageEnabled(true); @@ -588,16 +572,8 @@ public class CordovaWebView extends WebView { String name = xml.getAttributeValue(null, "name"); String value = xml.getAttributeValue(null, "value"); - // TODO @bc Is preferences needed? Just use Intent.putExtra? - //String readonlyString = xml.getAttributeValue(null, "readonly"); - - //boolean readonly = (readonlyString != null && - // readonlyString.equals("true")); - LOG.i("CordovaLog", "Found preference for %s=%s", name, value); - //preferences.add(new PreferenceNode(name, value, readonly)); - // Save preferences in Intent this.mCtx.getActivity().getIntent().putExtra(name, value); } @@ -612,7 +588,6 @@ public class CordovaWebView extends WebView { } // Init preferences - //this.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true"); if ("true".equals(this.getProperty("useBrowserHistory", "true"))) { this.useBrowserHistory = true; } diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 21af0000..227e251b 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -25,8 +25,6 @@ import org.apache.cordova.api.LOG; import org.json.JSONException; import org.json.JSONObject; -//import android.app.Activity; -//import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -34,7 +32,6 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.graphics.Bitmap; import android.net.Uri; import android.net.http.SslError; -//import android.util.Log; import android.view.View; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; @@ -274,7 +271,6 @@ public class CordovaWebViewClient extends WebViewClient { Thread.sleep(2000); ctx.getActivity().runOnUiThread(new Runnable() { public void run() { - //appView.setVisibility(View.VISIBLE); appView.postMessage("spinner", "stop"); } }); @@ -290,7 +286,6 @@ public class CordovaWebViewClient extends WebViewClient { if (this.appView.callbackServer != null) { this.appView.callbackServer.destroy(); } - //this.ctx.endActivity(); this.ctx.getActivity().finish(); } } @@ -311,11 +306,7 @@ public class CordovaWebViewClient extends WebViewClient { // Clear timeout flag this.appView.loadUrlTimeout++; - // Stop "app loading" spinner if showing - //this.ctx.spinnerStop(); - // Handle error - //this.ctx.onReceivedError(errorCode, description, failingUrl); JSONObject data = new JSONObject(); try { data.put("errorCode", errorCode); diff --git a/framework/src/org/apache/cordova/DirectoryManager.java b/framework/src/org/apache/cordova/DirectoryManager.java index 7b8e03f8..c888d423 100644 --- a/framework/src/org/apache/cordova/DirectoryManager.java +++ b/framework/src/org/apache/cordova/DirectoryManager.java @@ -31,107 +31,108 @@ import android.os.StatFs; * It is used by the FileUtils class. */ public class DirectoryManager { - - private static final String LOG_TAG = "DirectoryManager"; - /** - * Determine if a file or directory exists. - * - * @param name The name of the file to check. - * @return T=exists, F=not found - */ - protected static boolean testFileExists(String name) { - boolean status; - - // If SD card exists - if ((testSaveLocationExists()) && (!name.equals(""))) { - File path = Environment.getExternalStorageDirectory(); + @SuppressWarnings("unused") + private static final String LOG_TAG = "DirectoryManager"; + + /** + * Determine if a file or directory exists. + * + * @param name The name of the file to check. + * @return T=exists, F=not found + */ + protected static boolean testFileExists(String name) { + boolean status; + + // If SD card exists + if ((testSaveLocationExists()) && (!name.equals(""))) { + File path = Environment.getExternalStorageDirectory(); File newPath = constructFilePaths(path.toString(), name); status = newPath.exists(); - } - // If no SD card - else{ - status = false; - } - return status; - } - - /** - * Get the free disk space - * - * @return Size in KB or -1 if not available - */ - protected static long getFreeDiskSpace(boolean checkInternal) { - String status = Environment.getExternalStorageState(); - long freeSpace = 0; - - // If SD card exists - if (status.equals(Environment.MEDIA_MOUNTED)) { - freeSpace = freeSpaceCalculation(Environment.getExternalStorageDirectory().getPath()); - } - else if (checkInternal) { - freeSpace = freeSpaceCalculation("/"); - } - // If no SD card and we haven't been asked to check the internal directory then return -1 - else { - return -1; - } - - return freeSpace; - } - - /** - * Given a path return the number of free KB - * - * @param path to the file system - * @return free space in KB - */ - private static long freeSpaceCalculation(String path) { + } + // If no SD card + else { + status = false; + } + return status; + } + + /** + * Get the free disk space + * + * @return Size in KB or -1 if not available + */ + protected static long getFreeDiskSpace(boolean checkInternal) { + String status = Environment.getExternalStorageState(); + long freeSpace = 0; + + // If SD card exists + if (status.equals(Environment.MEDIA_MOUNTED)) { + freeSpace = freeSpaceCalculation(Environment.getExternalStorageDirectory().getPath()); + } + else if (checkInternal) { + freeSpace = freeSpaceCalculation("/"); + } + // If no SD card and we haven't been asked to check the internal directory then return -1 + else { + return -1; + } + + return freeSpace; + } + + /** + * Given a path return the number of free KB + * + * @param path to the file system + * @return free space in KB + */ + private static long freeSpaceCalculation(String path) { StatFs stat = new StatFs(path); long blockSize = stat.getBlockSize(); long availableBlocks = stat.getAvailableBlocks(); - return availableBlocks*blockSize/1024; - } - - /** - * Determine if SD card exists. - * - * @return T=exists, F=not found - */ - protected static boolean testSaveLocationExists() { - String sDCardStatus = Environment.getExternalStorageState(); - boolean status; - - // If SD card is mounted - if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) { - status = true; - } - - // If no SD card - else { - status = false; - } - return status; - } - - /** - * Create a new file object from two file paths. - * - * @param file1 Base file path - * @param file2 Remaining file path - * @return File object - */ - private static File constructFilePaths (String file1, String file2) { - File newPath; - if (file2.startsWith(file1)) { - newPath = new File(file2); - } - else { - newPath = new File(file1+"/"+file2); - } - return newPath; - } - + return availableBlocks * blockSize / 1024; + } + + /** + * Determine if SD card exists. + * + * @return T=exists, F=not found + */ + protected static boolean testSaveLocationExists() { + String sDCardStatus = Environment.getExternalStorageState(); + boolean status; + + // If SD card is mounted + if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) { + status = true; + } + + // If no SD card + else { + status = false; + } + return status; + } + + /** + * Create a new file object from two file paths. + * + * @param file1 Base file path + * @param file2 Remaining file path + * @return File object + */ + private static File constructFilePaths(String file1, String file2) { + File newPath; + if (file2.startsWith(file1)) { + newPath = new File(file2); + } + else { + newPath = new File(file1 + "/" + file2); + } + return newPath; + } + /** * Determine if we can use the SD Card to store the temporary file. If not then use * the internal cache directory. @@ -140,12 +141,12 @@ public class DirectoryManager { */ protected static String getTempDirectoryPath(Context ctx) { File cache = null; - + // SD Card Mounted if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { - cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + + cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache/"); - } + } // Use internal storage else { cache = ctx.getCacheDir(); diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 5be0fa9c..a1c3adba 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -18,24 +18,13 @@ */ package org.apache.cordova; -//import java.io.IOException; -//import java.util.ArrayList; import java.util.HashMap; -//import java.util.Hashtable; -//import java.util.Iterator; -//import java.util.Stack; -//import java.util.regex.Matcher; -//import java.util.regex.Pattern; -//import org.apache.cordova.PreferenceNode; -//import org.apache.cordova.PreferenceSet; import org.apache.cordova.api.IPlugin; import org.apache.cordova.api.LOG; import org.apache.cordova.api.CordovaInterface; import org.json.JSONException; import org.json.JSONObject; -//import org.apache.cordova.api.PluginManager; -//import org.xmlpull.v1.XmlPullParserException; import android.app.Activity; import android.app.AlertDialog; @@ -45,10 +34,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; -//import android.content.res.XmlResourceParser; import android.graphics.Color; import android.media.AudioManager; -//import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.Display; @@ -59,10 +46,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -//import android.webkit.WebChromeClient; -//import android.webkit.WebSettings; -//import android.webkit.WebSettings.LayoutAlgorithm; -//import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; @@ -161,11 +144,7 @@ public class DroidGap extends Activity implements CordovaInterface { // The initial URL for our app // ie http://server/path/index.html#abc?query - private String url = null; - //private Stack urls = new Stack(); - - // Url was specified from extras (activity was started programmatically) - //private String initUrl = null; + //private String url = null; private static int ACTIVITY_STARTING = 0; private static int ACTIVITY_RUNNING = 1; @@ -181,9 +160,6 @@ public class DroidGap extends Activity implements CordovaInterface { protected IPlugin activityResultCallback = null; protected boolean activityResultKeepRunning; - // Flag indicates that a loadUrl timeout occurred - //int loadUrlTimeout = 0; - // Default background color for activity // (this is not the color for the webview, which is set in HTML) private int backgroundColor = Color.BLACK; @@ -197,20 +173,11 @@ public class DroidGap extends Activity implements CordovaInterface { protected int splashscreen = 0; protected int splashscreenTime = 0; - // LoadUrl timeout value in msec (default of 20 sec) - //protected int loadUrlTimeoutValue = 20000; - // Keep app running when pause is received. (default = true) // If true, then the JavaScript and native code continue to run in the background // when another application (activity) is started. protected boolean keepRunning = true; - // Store the useBrowserHistory preference until we actually need it. - //private boolean useBrowserHistory = false; - - // preferences read from cordova.xml - //protected PreferenceSet preferences; - /** * Sets the authentication token. * @@ -298,14 +265,6 @@ public class DroidGap extends Activity implements CordovaInterface { root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0F)); - // If url was passed in to intent, then init webview, which will load the url -// Bundle bundle = this.getIntent().getExtras(); -// if (bundle != null) { -// String url = bundle.getString("url"); -// if (url != null) { -// this.initUrl = url; -// } -// } // Setup the hardware volume controls to handle volume control setVolumeControlStream(AudioManager.STREAM_MUSIC); } @@ -346,15 +305,6 @@ public class DroidGap extends Activity implements CordovaInterface { webViewClient.setWebView(this.appView); webChromeClient.setWebView(this.appView); - // Load Cordova configuration: - // white list of allowed URLs - // debug setting - //this.loadConfiguration(); - //Now we can check the preference - //this.appView.useBrowserHistory = preferences.prefMatches("useBrowserHistory", "true"); - - // - this.appView.setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, @@ -369,30 +319,6 @@ public class DroidGap extends Activity implements CordovaInterface { this.cancelLoadUrl = false; } - /** - * Look at activity parameters and process them. - * This must be called from the main UI thread. - */ - //private void handleActivityParameters() { - - // If backgroundColor - //this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK); - //LOG.e(TAG, "Setting background color=" + this.backgroundColor); - //this.root.setBackgroundColor(this.backgroundColor); - - // If spashscreen - //this.splashscreen = this.getIntegerProperty("splashscreen", 0); - - // If loadUrlTimeoutValue - //int timeout = this.getIntegerProperty("loadUrlTimeoutValue", 0); - //if (timeout > 0) { - // this.loadUrlTimeoutValue = timeout; - //} - - // If keepRunning - //this.keepRunning = this.getBooleanProperty("keepRunning", true); - //} - /** * Load the url into the webview. * @@ -405,9 +331,6 @@ public class DroidGap extends Activity implements CordovaInterface { this.init(); } - // Handle activity parameters - //this.handleActivityParameters(); - // If backgroundColor this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK); LOG.e(TAG, "Setting background color=" + this.backgroundColor); @@ -420,15 +343,6 @@ public class DroidGap extends Activity implements CordovaInterface { this.loadSpinner(); this.appView.loadUrl(url); - -// // If first page of app, then set URL to load to be the one passed in -// if (this.initUrl == null || (this.urls.size() > 0)) { -// this.loadUrlIntoView(url); -// } -// // Otherwise use the URL specified in the activity's extras bundle -// else { -// this.loadUrlIntoView(this.initUrl); -// } } /* @@ -464,71 +378,6 @@ public class DroidGap extends Activity implements CordovaInterface { } } - /** - * Load the url into the webview. - * - * @param url - */ -// private void loadUrlIntoView(final String url) { -// if (!url.startsWith("javascript:")) { -// LOG.d(TAG, "DroidGap.loadUrl(%s)", url); -// } -// -// if (!url.startsWith("javascript:")) { -// LOG.d(TAG, "DroidGap: url=%s baseUrl=%s", url, baseUrl); -// } -// -// // Load URL on UI thread -// final DroidGap me = this; -// -//// final Runnable loadError = new Runnable() { -//// public void run() { -//// me.appView.stopLoading(); -//// LOG.e(TAG, "DroidGap: TIMEOUT ERROR! - calling webViewClient"); -//// appView.viewClient.onReceivedError(me.appView, -6, "The connection to the server was unsuccessful.", url); -//// } -//// }; -// -// this.runOnUiThread(new Runnable() { -// public void run() { -// -// // Init web view if not already done -// if (me.appView == null) { -// me.init(); -// } -// -// // Handle activity parameters (TODO: Somehow abstract this) -// me.handleActivityParameters(); -// -// // Then load the spinner -// me.loadSpinner(); -// -//// // Create a timeout timer for loadUrl -//// final int currentLoadUrlTimeout = me.loadUrlTimeout; -//// Runnable runnable = new Runnable() { -//// public void run() { -//// try { -//// synchronized (this) { -//// wait(me.loadUrlTimeoutValue); -//// } -//// } catch (InterruptedException e) { -//// e.printStackTrace(); -//// } -//// -//// // If timeout, then stop loading and handle error -//// if (me.loadUrlTimeout == currentLoadUrlTimeout) { -//// me.runOnUiThread(loadError); -//// -//// } -//// } -//// }; -//// Thread thread = new Thread(runnable); -//// thread.start(); -// me.appView.loadUrl(url); -// } -// }); -// } - /** * Load the url into the webview after waiting for period of time. * This is used to display the splashscreen for certain amount of time. @@ -545,45 +394,8 @@ public class DroidGap extends Activity implements CordovaInterface { this.splashscreenTime = time; this.appView.loadUrl(url, time); - -// // If first page of app, then set URL to load to be the one passed in -// if (this.initUrl == null || (this.urls.size() > 0)) { -// this.loadUrlIntoView(url, time); -// } -// // Otherwise use the URL specified in the activity's extras bundle -// else { -// this.loadUrlIntoView(this.initUrl); -// } } - /** - * Load the url into the webview after waiting for period of time. - * This is used to display the splashscreen for certain amount of time. - * - * @param url - * @param time The number of ms to wait before showing webview - */ -// private void loadUrlIntoView(final String url, final int time) { -// -// // Clear cancel flag -// this.cancelLoadUrl = false; -// -// // If not first page of app, then load immediately -// if (this.urls.size() > 0) { -// this.loadUrlIntoView(url); -// } -// -// if (!url.startsWith("javascript:")) { -// LOG.d(TAG, "DroidGap.loadUrl(%s, %d)", url, time); -// } -// -// this.handleActivityParameters(); -// if (this.splashscreen != 0) { -// this.showSplashScreen(time); -// } -// this.loadUrlIntoView(url); -// } - /** * Cancel loadUrl before it has been loaded. */ @@ -607,13 +419,6 @@ public class DroidGap extends Activity implements CordovaInterface { */ public void clearHistory() { this.appView.clearHistory(); -// this.urls.clear(); -// this.appView.clearHistory(); -// -// // Leave current url on history stack -// if (this.url != null) { -// this.urls.push(this.url); -// } } /** @@ -939,7 +744,6 @@ public class DroidGap extends Activity implements CordovaInterface { * End this activity by calling finish for activity */ public void endActivity() { - //this.activityState = ACTIVITY_EXITING; this.finish(); } @@ -998,23 +802,6 @@ public class DroidGap extends Activity implements CordovaInterface { return false; } - /** - * Any calls to Activity.startActivityForResult must use method below, so - * the result can be routed to them correctly. - * - * This is done to eliminate the need to modify DroidGap.java to receive activity results. - * - * @param intent The intent to start - * @param requestCode Identifies who to send the result to - * - * @throws RuntimeException - */ - //@Override - //public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException { - // LOG.d(TAG, "DroidGap.startActivityForResult(intent,%d)", requestCode); - // super.startActivityForResult(intent, requestCode); - //} - /** * Launch an activity for which you would like a result when it finished. When this activity exits, * your onActivityResult() method will be called. @@ -1129,61 +916,6 @@ public class DroidGap extends Activity implements CordovaInterface { }); } - /** - * Load Cordova configuration from res/xml/cordova.xml. - * Approved list of URLs that can be loaded into DroidGap - * - * Log level: ERROR, WARN, INFO, DEBUG, VERBOSE (default=ERROR) - * - */ -// private void loadConfiguration() { -// int id = getResources().getIdentifier("cordova", "xml", getPackageName()); -// if (id == 0) { -// LOG.i("CordovaLog", "cordova.xml missing. Ignoring..."); -// return; -// } -// XmlResourceParser xml = getResources().getXml(id); -// int eventType = -1; -// while (eventType != XmlResourceParser.END_DOCUMENT) { -// if (eventType == XmlResourceParser.START_TAG) { -// String strNode = xml.getName(); -// if (strNode.equals("access")) { -// String origin = xml.getAttributeValue(null, "origin"); -// String subdomains = xml.getAttributeValue(null, "subdomains"); -// if (origin != null) { -// appView.addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0)); -// } -// } -// else if (strNode.equals("log")) { -// String level = xml.getAttributeValue(null, "level"); -// LOG.i("CordovaLog", "Found log level %s", level); -// if (level != null) { -// LOG.setLogLevel(level); -// } -// } -// else if (strNode.equals("preference")) { -// String name = xml.getAttributeValue(null, "name"); -// String value = xml.getAttributeValue(null, "value"); -// String readonlyString = xml.getAttributeValue(null, "readonly"); -// -// boolean readonly = (readonlyString != null && -// readonlyString.equals("true")); -// -// LOG.i("CordovaLog", "Found preference for %s", name); -// -// preferences.add(new PreferenceNode(name, value, readonly)); -// } -// } -// try { -// eventType = xml.next(); -// } catch (XmlPullParserException e) { -// e.printStackTrace(); -// } catch (IOException e) { -// e.printStackTrace(); -// } -// } -// } - /** * Determine if URL is in approved list of URLs to load. * diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java index c3456e0b..e006230f 100644 --- a/framework/src/org/apache/cordova/FileTransfer.java +++ b/framework/src/org/apache/cordova/FileTransfer.java @@ -217,6 +217,7 @@ public class FileTransfer extends Plugin { * @param params key:value pairs of user-defined parameters * @return FileUploadResult containing result of upload request */ + @SuppressWarnings("deprecation") public FileUploadResult upload(String file, String server, final String fileKey, final String fileName, final String mimeType, JSONObject params, boolean trustEveryone, boolean chunkedMode) throws IOException, SSLException { // Create return object diff --git a/framework/src/org/apache/cordova/FileUtils.java b/framework/src/org/apache/cordova/FileUtils.java index 5a73c800..df6b37f8 100755 --- a/framework/src/org/apache/cordova/FileUtils.java +++ b/framework/src/org/apache/cordova/FileUtils.java @@ -52,6 +52,7 @@ import android.webkit.MimeTypeMap; * Only files on the SD card can be accessed. */ public class FileUtils extends Plugin { + @SuppressWarnings("unused") private static final String LOG_TAG = "FileUtils"; private static final String _DATA = "_data"; // The column name where the file path is stored @@ -222,7 +223,7 @@ public class FileUtils extends Plugin { * @param filePath the path to check */ private void notifyDelete(String filePath) { - int result = this.ctx.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + this.ctx.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, MediaStore.Images.Media.DATA + " = ?", new String[] { filePath }); } @@ -237,6 +238,7 @@ public class FileUtils extends Plugin { * @throws IOException if the user can't read the file * @throws JSONException */ + @SuppressWarnings("deprecation") private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException { String decoded = URLDecoder.decode(url, "UTF-8"); @@ -1039,6 +1041,7 @@ public class FileUtils extends Plugin { * @param ctx) the current applicaiton context * @return the full path to the file */ + @SuppressWarnings("deprecation") protected static String getRealPathFromURI(Uri contentUri, CordovaInterface ctx) { String[] proj = { _DATA }; Cursor cursor = ctx.getActivity().managedQuery(contentUri, proj, null, null, null); diff --git a/framework/src/org/apache/cordova/HttpHandler.java b/framework/src/org/apache/cordova/HttpHandler.java index 4799e311..b005e5dd 100755 --- a/framework/src/org/apache/cordova/HttpHandler.java +++ b/framework/src/org/apache/cordova/HttpHandler.java @@ -70,7 +70,7 @@ public class HttpHandler { * writes a HTTP entity to the specified filename and location on disk */ { - int i = 0; + //int i = 0; String FilePath = "/sdcard/" + file; InputStream in = entity.getContent(); byte buff[] = new byte[1024]; @@ -81,7 +81,7 @@ public class HttpHandler { if (numread <= 0) break; out.write(buff, 0, numread); - i++; + //i++; } while (true); out.flush(); out.close(); diff --git a/framework/src/org/apache/cordova/api/CordovaInterface.java b/framework/src/org/apache/cordova/api/CordovaInterface.java index 5d56039c..c4a1925e 100755 --- a/framework/src/org/apache/cordova/api/CordovaInterface.java +++ b/framework/src/org/apache/cordova/api/CordovaInterface.java @@ -18,22 +18,8 @@ */ package org.apache.cordova.api; -//import java.util.HashMap; - import android.app.Activity; -//import android.app.Service; -//import android.content.BroadcastReceiver; -//import android.content.ContentResolver; -//import android.content.Context; import android.content.Intent; -//import android.content.IntentFilter; -//import android.content.res.AssetManager; -//import android.content.res.Resources; -//import android.database.Cursor; -//import android.hardware.SensorManager; -//import android.net.Uri; -//import android.view.Menu; -//import android.view.MenuItem; /** * The Cordova activity abstract class that is extended by DroidGap. @@ -72,17 +58,6 @@ public interface CordovaInterface { */ public abstract boolean isBackButtonBound(); - /* - * Hook in DroidGap for menu plugins - * (This is in the Android SDK, do we need this on the Interface?) - */ - - //public abstract boolean onCreateOptionsMenu(Menu menu); - - //public abstract boolean onPrepareOptionsMenu(Menu menu); - - //public abstract boolean onOptionsItemSelected(MenuItem item); - /** * Get the Android activity. * @@ -90,114 +65,9 @@ public interface CordovaInterface { */ public abstract Activity getActivity(); - /** - * @deprecated - * Add services to res/xml/plugins.xml instead. - * - * Add a class that implements a service. - * - * @param serviceType - * @param className - */ -// @Deprecated -// abstract public void addService(String serviceType, String className); - - /** - * @deprecated - * Send JavaScript statement back to JavaScript. - * - * @param message - */ -// @Deprecated -// abstract public void sendJavascript(String statement); - - /** - * @deprecated - * Launch an activity for which you would not like a result when it finished. - * - * @param intent The intent to start - */ -// @Deprecated -// abstract public void startActivity(Intent intent); - - /** - * @deprecated - * Load the specified URL in the Cordova webview. - * - * @param url The URL to load. - */ -// @Deprecated -// abstract public void loadUrl(String url); - - /** - * @deprecated - * Send a message to all plugins. - * - * @param id The message id - * @param data The message data - */ -// @Deprecated -// abstract public void postMessage(String id, Object data); - -// @Deprecated -// public abstract Resources getResources(); - -// @Deprecated -// public abstract String getPackageName(); - -// @Deprecated -// public abstract Object getSystemService(String service); - -// @Deprecated -// public abstract Context getContext(); - -// @Deprecated -// public abstract Context getBaseContext(); - -// @Deprecated -// public abstract Intent registerReceiver(BroadcastReceiver receiver, -// IntentFilter intentFilter); - -// @Deprecated -// public abstract ContentResolver getContentResolver(); - -// @Deprecated -// public abstract void unregisterReceiver(BroadcastReceiver receiver); - -// @Deprecated -// public abstract Cursor managedQuery(Uri uri, String[] projection, String selection, -// String[] selectionArgs, String sortOrder); - -// @Deprecated -// public abstract void runOnUiThread(Runnable runnable); - -// @Deprecated -// public abstract AssetManager getAssets(); - -// @Deprecated -// public abstract void clearCache(); - -// @Deprecated -// public abstract void clearHistory(); - -// @Deprecated -// public abstract boolean backHistory(); - - //public abstract void addWhiteListEntry(String origin, boolean subdomains); - @Deprecated public abstract void cancelLoadUrl(); -// @Deprecated -// public abstract void showWebPage(String url, boolean openExternal, -// boolean clearHistory, HashMap params); - -// @Deprecated -// public abstract Context getApplicationContext(); - -// @Deprecated -// public abstract boolean isUrlWhiteListed(String source); - /** * Called when a message is sent to plugin. * @@ -206,14 +76,4 @@ public interface CordovaInterface { */ public void onMessage(String id, Object data); - /** - * Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable). - * The errorCode parameter corresponds to one of the ERROR_* constants. - * - * @param errorCode The error code corresponding to an ERROR_* value. - * @param description A String describing the error. - * @param failingUrl The url that failed to load. - */ - //public void onReceivedError(final int errorCode, final String description, final String failingUrl); - } diff --git a/framework/src/org/apache/cordova/api/IPlugin.java b/framework/src/org/apache/cordova/api/IPlugin.java index 82d9bb7a..173a304e 100755 --- a/framework/src/org/apache/cordova/api/IPlugin.java +++ b/framework/src/org/apache/cordova/api/IPlugin.java @@ -24,8 +24,6 @@ import org.json.JSONArray; //import android.content.Context; import android.content.Intent; -//import android.webkit.WebView; - /** * Plugin interface must be implemented by any plugin classes. * diff --git a/framework/src/org/apache/cordova/api/Plugin.java b/framework/src/org/apache/cordova/api/Plugin.java index f7c18656..2e6d462d 100755 --- a/framework/src/org/apache/cordova/api/Plugin.java +++ b/framework/src/org/apache/cordova/api/Plugin.java @@ -21,12 +21,8 @@ package org.apache.cordova.api; import org.apache.cordova.CordovaWebView; import org.json.JSONArray; import org.json.JSONObject; - -//import android.content.Context; import android.content.Intent; -//import android.webkit.WebView; - /** * Plugin interface must be implemented by any plugin classes. * @@ -36,7 +32,7 @@ public abstract class Plugin implements IPlugin { public String id; public CordovaWebView webView; // WebView object - public CordovaInterface ctx; // CordovaActivity object + public CordovaInterface ctx; // CordovaActivity object /** * Executes the request and returns PluginResult. diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index c931477f..fa40e078 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -65,20 +65,6 @@ public class PluginManager { this.firstRun = true; } - // Called by com.phonegap.api.PluginManager only -// public PluginManager(WebView mApp, CordovaInterface mCtx) throws Exception { -// this.ctx = mCtx; //mCtx.getContext(); -// if (CordovaWebView.class.isInstance(mApp)) -// { -// this.app = (CordovaWebView) mApp; -// } -// else -// { -// //Throw an exception here -// throw new Exception(); -// } -// } - /** * Init when loading a new HTML page into webview. */ From e6aa64760e2cb6f289c6ba7fed0df0e26a86d7fe Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 22:18:01 -0500 Subject: [PATCH 03/10] Updates. --- framework/src/org/apache/cordova/DroidGap.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index a1c3adba..842895d2 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -251,8 +251,14 @@ public class DroidGap extends Activity implements CordovaInterface { getWindow().requestFeature(Window.FEATURE_NO_TITLE); + // TODO @bc - What about fullscreen? + //if (preferences.prefMatches("fullscreen", "true")) { + // getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + // WindowManager.LayoutParams.FLAG_FULLSCREEN); + //} else { getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN, WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN); + //} // This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket! Display display = getWindowManager().getDefaultDisplay(); @@ -400,6 +406,7 @@ public class DroidGap extends Activity implements CordovaInterface { * Cancel loadUrl before it has been loaded. */ // TODO NO-OP + @Deprecated public void cancelLoadUrl() { this.cancelLoadUrl = true; } @@ -639,9 +646,7 @@ public class DroidGap extends Activity implements CordovaInterface { } // Resume JavaScript timers (including setInterval) - if (this.appView.pluginManager != null) { - this.appView.resumeTimers(); - } + this.appView.resumeTimers(); } } From 13f92b2b76d723729e716c7aa81214803bf1ad47 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 22:51:36 -0500 Subject: [PATCH 04/10] Add exit message. --- framework/src/org/apache/cordova/App.java | 2 +- .../apache/cordova/CordovaWebViewClient.java | 3 +- .../src/org/apache/cordova/DroidGap.java | 44 ++++++++++--------- 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/framework/src/org/apache/cordova/App.java b/framework/src/org/apache/cordova/App.java index 7df0aa94..48eeb660 100755 --- a/framework/src/org/apache/cordova/App.java +++ b/framework/src/org/apache/cordova/App.java @@ -201,7 +201,7 @@ public class App extends Plugin { * Exit the Android application. */ public void exitApp() { - this.ctx.getActivity().finish(); + this.webView.postMessage("exit", null); } } diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index 227e251b..b118a90c 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -237,6 +237,7 @@ public class CordovaWebViewClient extends WebViewClient { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); + LOG.d(TAG, "onPageFinished(" + url + ")"); /** * Because of a timing issue we need to clear this history in onPageFinished as well as @@ -286,7 +287,7 @@ public class CordovaWebViewClient extends WebViewClient { if (this.appView.callbackServer != null) { this.appView.callbackServer.destroy(); } - this.ctx.getActivity().finish(); + appView.postMessage("exit", null); } } diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 842895d2..bccc8217 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -337,6 +337,7 @@ public class DroidGap extends Activity implements CordovaInterface { this.init(); } + // TODO @bc - background color doesn't work // If backgroundColor this.backgroundColor = this.getIntegerProperty("backgroundColor", Color.BLACK); LOG.e(TAG, "Setting background color=" + this.backgroundColor); @@ -655,6 +656,7 @@ public class DroidGap extends Activity implements CordovaInterface { * The final call you receive before your activity is destroyed. */ public void onDestroy() { + LOG.d(TAG, "onDestroy()"); super.onDestroy(); if (this.appView != null) { @@ -749,13 +751,6 @@ public class DroidGap extends Activity implements CordovaInterface { * End this activity by calling finish for activity */ public void endActivity() { - this.finish(); - } - - /** - * Finish for Activity has been called. - */ - public void finish() { this.activityState = ACTIVITY_EXITING; super.finish(); } @@ -902,21 +897,25 @@ public class DroidGap extends Activity implements CordovaInterface { final DroidGap me = this; me.runOnUiThread(new Runnable() { public void run() { - AlertDialog.Builder dlg = new AlertDialog.Builder(me); - dlg.setMessage(message); - dlg.setTitle(title); - dlg.setCancelable(false); - dlg.setPositiveButton(button, - new AlertDialog.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - if (exit) { - me.endActivity(); + try { + AlertDialog.Builder dlg = new AlertDialog.Builder(me); + dlg.setMessage(message); + dlg.setTitle(title); + dlg.setCancelable(false); + dlg.setPositiveButton(button, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + if (exit) { + me.endActivity(); + } } - } - }); - dlg.create(); - dlg.show(); + }); + dlg.create(); + dlg.show(); + } catch (Exception e) { + finish(); + } } }); } @@ -1077,6 +1076,9 @@ public class DroidGap extends Activity implements CordovaInterface { e.printStackTrace(); } } + else if ("exit".equals(id)) { + this.endActivity(); + } } } From 01abb3202544820f8ad821079e42fce7d2b6a711 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 23:18:26 -0500 Subject: [PATCH 05/10] Update --- .../src/org/apache/cordova/GeoBroker.java | 185 +++++++++--------- 1 file changed, 93 insertions(+), 92 deletions(-) diff --git a/framework/src/org/apache/cordova/GeoBroker.java b/framework/src/org/apache/cordova/GeoBroker.java index 6a150b35..18d405b8 100755 --- a/framework/src/org/apache/cordova/GeoBroker.java +++ b/framework/src/org/apache/cordova/GeoBroker.java @@ -38,7 +38,7 @@ public class GeoBroker extends Plugin { private GPSListener gpsListener; private NetworkListener networkListener; private LocationManager locationManager; - + /** * Constructor. */ @@ -54,65 +54,65 @@ public class GeoBroker extends Plugin { * @return A PluginResult object with a status and message. */ public PluginResult execute(String action, JSONArray args, String callbackId) { - if (this.locationManager == null) { - this.locationManager = (LocationManager) this.ctx.getSystemService(Context.LOCATION_SERVICE); - this.networkListener = new NetworkListener(this.locationManager, this); - this.gpsListener = new GPSListener(this.locationManager, this); - } + if (this.locationManager == null) { + this.locationManager = (LocationManager) this.ctx.getActivity().getSystemService(Context.LOCATION_SERVICE); + this.networkListener = new NetworkListener(this.locationManager, this); + this.gpsListener = new GPSListener(this.locationManager, this); + } PluginResult.Status status = PluginResult.Status.NO_RESULT; String message = ""; PluginResult result = new PluginResult(status, message); result.setKeepCallback(true); - + try { if (action.equals("getLocation")) { - boolean enableHighAccuracy = args.getBoolean(0); - int maximumAge = args.getInt(1); - Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER)); - // Check if we can use lastKnownLocation to get a quick reading and use less battery - if ((System.currentTimeMillis() - last.getTime()) <= maximumAge) { - result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last)); - } else { - this.getCurrentLocation(callbackId, enableHighAccuracy); - } + boolean enableHighAccuracy = args.getBoolean(0); + int maximumAge = args.getInt(1); + Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER)); + // Check if we can use lastKnownLocation to get a quick reading and use less battery + if ((System.currentTimeMillis() - last.getTime()) <= maximumAge) { + result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last)); + } else { + this.getCurrentLocation(callbackId, enableHighAccuracy); + } } else if (action.equals("addWatch")) { - String id = args.getString(0); - boolean enableHighAccuracy = args.getBoolean(1); - this.addWatch(id, callbackId, enableHighAccuracy); + String id = args.getString(0); + boolean enableHighAccuracy = args.getBoolean(1); + this.addWatch(id, callbackId, enableHighAccuracy); } else if (action.equals("clearWatch")) { - String id = args.getString(0); - this.clearWatch(id); + String id = args.getString(0); + this.clearWatch(id); } } catch (JSONException e) { - result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage()); + result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage()); } return result; } private void clearWatch(String id) { - this.gpsListener.clearWatch(id); - this.networkListener.clearWatch(id); - } - - private void getCurrentLocation(String callbackId, boolean enableHighAccuracy) { - if (enableHighAccuracy) { - this.gpsListener.addCallback(callbackId); - } else { - this.networkListener.addCallback(callbackId); - } - } - - private void addWatch(String timerId, String callbackId, boolean enableHighAccuracy) { - if (enableHighAccuracy) { - this.gpsListener.addWatch(timerId, callbackId); - } else { - this.networkListener.addWatch(timerId, callbackId); - } + this.gpsListener.clearWatch(id); + this.networkListener.clearWatch(id); } - /** + private void getCurrentLocation(String callbackId, boolean enableHighAccuracy) { + if (enableHighAccuracy) { + this.gpsListener.addCallback(callbackId); + } else { + this.networkListener.addCallback(callbackId); + } + } + + private void addWatch(String timerId, String callbackId, boolean enableHighAccuracy) { + if (enableHighAccuracy) { + this.gpsListener.addWatch(timerId, callbackId); + } else { + this.networkListener.addWatch(timerId, callbackId); + } + } + + /** * Identifies if action to be executed returns a value and should be run synchronously. * * @param action The action to execute @@ -122,65 +122,66 @@ public class GeoBroker extends Plugin { // Starting listeners is easier to run on main thread, so don't run async. return true; } - + /** * Called when the activity is to be shut down. * Stop listener. */ public void onDestroy() { - this.networkListener.destroy(); - this.gpsListener.destroy(); + this.networkListener.destroy(); + this.gpsListener.destroy(); this.networkListener = null; this.gpsListener = null; } public JSONObject returnLocationJSON(Location loc) { - JSONObject o = new JSONObject(); - - try { - o.put("latitude", loc.getLatitude()); - o.put("longitude", loc.getLongitude()); - o.put("altitude", (loc.hasAltitude() ? loc.getAltitude() : null)); - o.put("accuracy", loc.getAccuracy()); - o.put("heading", (loc.hasBearing() ? (loc.hasSpeed() ? loc.getBearing() : null) : null)); - o.put("speed", loc.getSpeed()); - o.put("timestamp", loc.getTime()); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - - return o; - } - public void win(Location loc, String callbackId) { - PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc)); - this.success(result, callbackId); - } - /** - * Location failed. Send error back to JavaScript. - * - * @param code The error code - * @param msg The error message - * @throws JSONException - */ - public void fail(int code, String msg, String callbackId) { - JSONObject obj = new JSONObject(); - String backup = null; - try { - obj.put("code", code); - obj.put("message", msg); - } catch (JSONException e) { - obj = null; - backup = "{'code':" + code + ",'message':'" + msg.replaceAll("'", "\'") + "'}"; - } - PluginResult result; - if (obj != null) { - result = new PluginResult(PluginResult.Status.ERROR, obj); - } else { - result = new PluginResult(PluginResult.Status.ERROR, backup); - } - - this.error(result, callbackId); - } + JSONObject o = new JSONObject(); + + try { + o.put("latitude", loc.getLatitude()); + o.put("longitude", loc.getLongitude()); + o.put("altitude", (loc.hasAltitude() ? loc.getAltitude() : null)); + o.put("accuracy", loc.getAccuracy()); + o.put("heading", (loc.hasBearing() ? (loc.hasSpeed() ? loc.getBearing() : null) : null)); + o.put("speed", loc.getSpeed()); + o.put("timestamp", loc.getTime()); + } catch (JSONException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return o; + } + + public void win(Location loc, String callbackId) { + PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc)); + this.success(result, callbackId); + } + + /** + * Location failed. Send error back to JavaScript. + * + * @param code The error code + * @param msg The error message + * @throws JSONException + */ + public void fail(int code, String msg, String callbackId) { + JSONObject obj = new JSONObject(); + String backup = null; + try { + obj.put("code", code); + obj.put("message", msg); + } catch (JSONException e) { + obj = null; + backup = "{'code':" + code + ",'message':'" + msg.replaceAll("'", "\'") + "'}"; + } + PluginResult result; + if (obj != null) { + result = new PluginResult(PluginResult.Status.ERROR, obj); + } else { + result = new PluginResult(PluginResult.Status.ERROR, backup); + } + + this.error(result, callbackId); + } } From e660768e4b604d0204e14716f0cebf5835a7562a Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 23:19:15 -0500 Subject: [PATCH 06/10] Removed. --- .../src/org/apache/cordova/GpsListener.java | 163 ------------------ 1 file changed, 163 deletions(-) delete mode 100755 framework/src/org/apache/cordova/GpsListener.java diff --git a/framework/src/org/apache/cordova/GpsListener.java b/framework/src/org/apache/cordova/GpsListener.java deleted file mode 100755 index 6cd85fb0..00000000 --- a/framework/src/org/apache/cordova/GpsListener.java +++ /dev/null @@ -1,163 +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 org.apache.cordova.api.CordovaInterface; - -import android.content.Context; -import android.location.Location; -import android.location.LocationManager; -import android.location.LocationListener; -import android.os.Bundle; - -/** - * This class handles requests for GPS location services. - * - */ -public class GpsListener implements LocationListener { - - private CordovaInterface mCtx; // CordovaActivity object - - private LocationManager mLocMan; // Location manager object - private GeoListener owner; // Geolistener object (parent) - private boolean hasData = false; // Flag indicates if location data is available in cLoc - private Location cLoc; // Last recieved location - private boolean running = false; // Flag indicates if listener is running - - /** - * Constructor. - * Automatically starts listening. - * - * @param ctx - * @param interval - * @param m - */ - public GpsListener(CordovaInterface ctx, int interval, GeoListener m) { - this.owner = m; - this.mCtx = ctx; - this.mLocMan = (LocationManager) this.mCtx.getActivity().getSystemService(Context.LOCATION_SERVICE); - this.running = false; - this.start(interval); - } - - /** - * Get last location. - * - * @return Location object - */ - public Location getLocation() { - this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER); - if (this.cLoc != null) { - this.hasData = true; - } - return this.cLoc; - } - - /** - * Called when the provider is disabled by the user. - * - * @param provider - */ - public void onProviderDisabled(String provider) { - this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS provider disabled."); - } - - /** - * Called when the provider is enabled by the user. - * - * @param provider - */ - public void onProviderEnabled(String provider) { - System.out.println("GpsListener: The provider " + provider + " is enabled"); - } - - /** - * Called when the provider status changes. This method is called when a - * provider is unable to fetch a location or if the provider has recently - * become available after a period of unavailability. - * - * @param provider - * @param status - * @param extras - */ - public void onStatusChanged(String provider, int status, Bundle extras) { - System.out.println("GpsListener: The status of the provider " + provider + " has changed"); - if (status == 0) { - System.out.println("GpsListener: " + provider + " is OUT OF SERVICE"); - this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS out of service."); - } - else if (status == 1) { - System.out.println("GpsListener: " + provider + " is TEMPORARILY_UNAVAILABLE"); - } - else { - System.out.println("GpsListener: " + provider + " is Available"); - } - } - - /** - * Called when the location has changed. - * - * @param location - */ - public void onLocationChanged(Location location) { - System.out.println("GpsListener: The location has been updated!"); - this.hasData = true; - this.cLoc = location; - this.owner.success(location); - } - - /** - * Determine if location data is available. - * - * @return - */ - public boolean hasLocation() { - return this.hasData; - } - - /** - * Start requesting location updates. - * - * @param interval - */ - public void start(int interval) { - if (!this.running) { - this.running = true; - this.mLocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 0, this); - this.getLocation(); - - // If GPS provider has data, then send now - if (this.hasData) { - this.owner.success(this.cLoc); - } - } - } - - /** - * Stop receiving location updates. - */ - public void stop() { - if (this.running) { - this.mLocMan.removeUpdates(this); - } - this.running = false; - } - -} From 0d32115c3fcf9e3136fc7ca21973f76eb5aecabc Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 23:24:08 -0500 Subject: [PATCH 07/10] Merging nightmare. --- .../org/apache/cordova/NetworkListener.java | 139 ++---------------- 1 file changed, 9 insertions(+), 130 deletions(-) diff --git a/framework/src/org/apache/cordova/NetworkListener.java b/framework/src/org/apache/cordova/NetworkListener.java index 7fa135fc..6eaa8ac1 100755 --- a/framework/src/org/apache/cordova/NetworkListener.java +++ b/framework/src/org/apache/cordova/NetworkListener.java @@ -16,138 +16,17 @@ specific language governing permissions and limitations under the License. */ + package org.apache.cordova; -import org.apache.cordova.api.CordovaInterface; - -import android.content.Context; -import android.location.Location; import android.location.LocationManager; -import android.location.LocationListener; -import android.os.Bundle; - -public class NetworkListener implements LocationListener { - - private CordovaInterface mCtx; // CordovaActivity object - - private LocationManager mLocMan; // Location manager object - private GeoListener owner; // Geolistener object (parent) - private boolean hasData = false; // Flag indicates if location data is available in cLoc - private Location cLoc; // Last recieved location - private boolean running = false; // Flag indicates if listener is running - - /** - * Constructor. - * Automatically starts listening. - * - * @param ctx - * @param interval - * @param m - */ - public NetworkListener(CordovaInterface ctx, int interval, GeoListener m) { - this.owner = m; - this.mCtx = ctx; - this.mLocMan = (LocationManager) this.mCtx.getActivity().getSystemService(Context.LOCATION_SERVICE); - this.running = false; - this.start(interval); - } - - /** - * Get last location. - * - * @return Location object - */ - public Location getLocation() { - this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER); - if (this.cLoc != null) { - this.hasData = true; - } - return this.cLoc; - } - - /** - * Called when the provider is disabled by the user. - * - * @param provider - */ - public void onProviderDisabled(String provider) { - System.out.println("NetworkListener: The provider " + provider + " is disabled"); - } - - /** - * Called when the provider is enabled by the user. - * - * @param provider - */ - public void onProviderEnabled(String provider) { - System.out.println("NetworkListener: The provider " + provider + " is enabled"); - } - - /** - * Called when the provider status changes. This method is called when a - * provider is unable to fetch a location or if the provider has recently - * become available after a period of unavailability. - * - * @param provider - * @param status - * @param extras - */ - public void onStatusChanged(String provider, int status, Bundle extras) { - System.out.println("NetworkListener: The status of the provider " + provider + " has changed"); - if (status == 0) { - System.out.println("NetworkListener: " + provider + " is OUT OF SERVICE"); - } - else if (status == 1) { - System.out.println("NetworkListener: " + provider + " is TEMPORARILY_UNAVAILABLE"); - } - else { - System.out.println("NetworkListener: " + provider + " is Available"); - } - } - - /** - * Called when the location has changed. - * - * @param location - */ - public void onLocationChanged(Location location) { - System.out.println("NetworkListener: The location has been updated!"); - this.hasData = true; - this.cLoc = location; - - // The GPS is the primary form of Geolocation in Cordova. - // Only fire the success variables if the GPS is down for some reason. - if (!this.owner.mGps.hasLocation()) { - this.owner.success(location); - } - } - - /** - * Start requesting location updates. - * - * @param interval - */ - public void start(int interval) { - if (!this.running) { - this.running = true; - this.mLocMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, 0, this); - this.getLocation(); - - // If Network provider has data but GPS provider doesn't, then send ours - if (this.hasData && !this.owner.mGps.hasLocation()) { - this.owner.success(this.cLoc); - } - } - } - - /** - * Stop receiving location updates. - */ - public void stop() { - if (this.running) { - this.mLocMan.removeUpdates(this); - } - this.running = false; - } +/** + * This class handles requests for GPS location services. + * + */ +public class NetworkListener extends CordovaLocationListener { + public NetworkListener(LocationManager locationManager, GeoBroker m) { + super(locationManager, m, "[Cordova NetworkListener]"); + } } From 441785b51deb59362f844c22bebe4ebf37c6d755 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Mon, 14 May 2012 23:25:10 -0500 Subject: [PATCH 08/10] Update for getActivity(). --- test/src/org/apache/cordova/test/ActivityPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/org/apache/cordova/test/ActivityPlugin.java b/test/src/org/apache/cordova/test/ActivityPlugin.java index 019d2ec3..553ef2ae 100755 --- a/test/src/org/apache/cordova/test/ActivityPlugin.java +++ b/test/src/org/apache/cordova/test/ActivityPlugin.java @@ -69,9 +69,9 @@ public class ActivityPlugin extends Plugin { public void startActivity(String className) { try { - Intent intent = new Intent().setClass(this.ctx, Class.forName(className)); + Intent intent = new Intent().setClass(this.ctx.getActivity(), Class.forName(className)); LOG.d(TAG, "Starting activity %s", className); - this.ctx.startActivity(intent); + this.ctx.getActivity().startActivity(intent); } catch (ClassNotFoundException e) { e.printStackTrace(); LOG.e(TAG, "Error starting activity %s", className); From 9f5f4973ae0845e1d1349f8c48f06424d5bd9728 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 15 May 2012 09:55:55 -0500 Subject: [PATCH 09/10] Add comments. --- .../apache/cordova/CordovaWebViewClient.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java index b118a90c..bda03301 100755 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/CordovaWebViewClient.java @@ -206,6 +206,15 @@ public class CordovaWebViewClient extends WebViewClient { } } + /** + * Notify the host application that a page has started loading. + * This method is called once for each main frame load so a page with iframes or framesets will call onPageStarted + * one time for the main frame. This also means that onPageStarted will not be called when the contents of an + * embedded frame changes, i.e. clicking a link whose target is an iframe. + * + * @param view The webview initiating the callback. + * @param url The url of the page. + */ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { // Clear history so history.back() doesn't do anything. @@ -230,6 +239,7 @@ public class CordovaWebViewClient extends WebViewClient { /** * Notify the host application that a page has finished loading. + * This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet. * * @param view The webview initiating the callback. * @param url The url of the page. @@ -319,6 +329,16 @@ public class CordovaWebViewClient extends WebViewClient { this.appView.postMessage("onReceivedError", data); } + /** + * Notify the host application that an SSL error occurred while loading a resource. + * The host application must call either handler.cancel() or handler.proceed(). + * Note that the decision may be retained for use in response to future SSL errors. + * The default behavior is to cancel the load. + * + * @param view The WebView that is initiating the callback. + * @param handler An SslErrorHandler object that will handle the user's response. + * @param error The SSL error object. + */ @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { @@ -341,6 +361,13 @@ public class CordovaWebViewClient extends WebViewClient { } } + /** + * Notify the host application to update its visited links database. + * + * @param view The WebView that is initiating the callback. + * @param url The url being visited. + * @param isReload True if this url is being reloaded. + */ @Override public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { /* From d683bd37441a9b19f9777ec4c60b74d4c3a67867 Mon Sep 17 00:00:00 2001 From: Bryce Curtis Date: Tue, 15 May 2012 10:00:02 -0500 Subject: [PATCH 10/10] Enable onMessage() to return a value. --- framework/src/org/apache/cordova/AudioHandler.java | 4 +++- framework/src/org/apache/cordova/DroidGap.java | 4 +++- .../org/apache/cordova/api/CordovaInterface.java | 3 ++- framework/src/org/apache/cordova/api/IPlugin.java | 3 ++- framework/src/org/apache/cordova/api/Plugin.java | 4 +++- .../src/org/apache/cordova/api/PluginManager.java | 14 +++++++++++--- 6 files changed, 24 insertions(+), 8 deletions(-) diff --git a/framework/src/org/apache/cordova/AudioHandler.java b/framework/src/org/apache/cordova/AudioHandler.java index 8acb52c0..1de3cfbe 100755 --- a/framework/src/org/apache/cordova/AudioHandler.java +++ b/framework/src/org/apache/cordova/AudioHandler.java @@ -140,8 +140,9 @@ public class AudioHandler extends Plugin { * * @param id The message id * @param data The message data + * @return Object to stop propagation or null */ - public void onMessage(String id, Object data) { + public Object onMessage(String id, Object data) { // If phone message if (id.equals("telephone")) { @@ -167,6 +168,7 @@ public class AudioHandler extends Plugin { this.pausedForPhone.clear(); } } + return null; } //-------------------------------------------------------------------------- diff --git a/framework/src/org/apache/cordova/DroidGap.java b/framework/src/org/apache/cordova/DroidGap.java index 21164e33..aeca772b 100755 --- a/framework/src/org/apache/cordova/DroidGap.java +++ b/framework/src/org/apache/cordova/DroidGap.java @@ -1050,8 +1050,9 @@ public class DroidGap extends Activity implements CordovaInterface { * * @param id The message id * @param data The message data + * @return Object or null */ - public void onMessage(String id, Object data) { + public Object onMessage(String id, Object data) { LOG.d(TAG, "onMessage(" + id + "," + data + ")"); if ("splashscreen".equals(id)) { if ("hide".equals(data.toString())) { @@ -1079,6 +1080,7 @@ public class DroidGap extends Activity implements CordovaInterface { else if ("exit".equals(id)) { this.endActivity(); } + return null; } } diff --git a/framework/src/org/apache/cordova/api/CordovaInterface.java b/framework/src/org/apache/cordova/api/CordovaInterface.java index c4a1925e..fa6e1f8a 100755 --- a/framework/src/org/apache/cordova/api/CordovaInterface.java +++ b/framework/src/org/apache/cordova/api/CordovaInterface.java @@ -73,7 +73,8 @@ public interface CordovaInterface { * * @param id The message id * @param data The message data + * @return Object or null */ - public void onMessage(String id, Object data); + public Object onMessage(String id, Object data); } diff --git a/framework/src/org/apache/cordova/api/IPlugin.java b/framework/src/org/apache/cordova/api/IPlugin.java index 173a304e..e410ac97 100755 --- a/framework/src/org/apache/cordova/api/IPlugin.java +++ b/framework/src/org/apache/cordova/api/IPlugin.java @@ -94,8 +94,9 @@ public interface IPlugin { * * @param id The message id * @param data The message data + * @return Object to stop propagation or null */ - public void onMessage(String id, Object data); + public Object onMessage(String id, Object data); /** * Called when an activity you launched exits, giving you the requestCode you started it with, diff --git a/framework/src/org/apache/cordova/api/Plugin.java b/framework/src/org/apache/cordova/api/Plugin.java index 2e6d462d..871a964f 100755 --- a/framework/src/org/apache/cordova/api/Plugin.java +++ b/framework/src/org/apache/cordova/api/Plugin.java @@ -107,8 +107,10 @@ public abstract class Plugin implements IPlugin { * * @param id The message id * @param data The message data + * @return Object to stop propagation or null */ - public void onMessage(String id, Object data) { + public Object onMessage(String id, Object data) { + return null; } /** diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index fa40e078..1714eb83 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -314,14 +314,22 @@ public class PluginManager { * * @param id The message id * @param data The message data + * @return */ - public void postMessage(String id, Object data) { - this.ctx.onMessage(id, data); + public Object postMessage(String id, Object data) { + Object obj = this.ctx.onMessage(id, data); + if (obj != null) { + return obj; + } for (PluginEntry entry : this.entries.values()) { if (entry.plugin != null) { - entry.plugin.onMessage(id, data); + obj = entry.plugin.onMessage(id, data); + if (obj != null) { + return obj; + } } } + return null; } /**