ripped out plugins

This commit is contained in:
Steven Gill 2013-05-16 17:53:11 -07:00
parent 227733d195
commit adcbd879c8
28 changed files with 0 additions and 10729 deletions

View File

@ -40,64 +40,9 @@
<preference name="InAppBrowserStorageEnabled" value="true" />
<preference name="disallowOverscroll" value="true" />
-->
<feature name="App">
<param name="android-package" value="org.apache.cordova.App"/>
</feature>
<feature name="Geolocation">
<param name="android-package" value="org.apache.cordova.GeoBroker"/>
</feature>
<feature name="Device">
<param name="android-package" value="org.apache.cordova.Device"/>
</feature>
<feature name="Accelerometer">
<param name="android-package" value="org.apache.cordova.AccelListener"/>
</feature>
<feature name="Compass">
<param name="android-package" value="org.apache.cordova.CompassListener"/>
</feature>
<feature name="Media">
<param name="android-package" value="org.apache.cordova.AudioHandler"/>
</feature>
<feature name="Camera">
<param name="android-package" value="org.apache.cordova.CameraLauncher"/>
</feature>
<feature name="Contacts">
<param name="android-package" value="org.apache.cordova.ContactManager"/>
</feature>
<feature name="File">
<param name="android-package" value="org.apache.cordova.FileUtils"/>
</feature>
<feature name="NetworkStatus">
<param name="android-package" value="org.apache.cordova.NetworkManager"/>
</feature>
<feature name="Notification">
<param name="android-package" value="org.apache.cordova.Notification"/>
</feature>
<feature name="Storage">
<param name="android-package" value="org.apache.cordova.Storage"/>
</feature>
<feature name="FileTransfer">
<param name="android-package" value="org.apache.cordova.FileTransfer"/>
</feature>
<feature name="Capture">
<param name="android-package" value="org.apache.cordova.Capture"/>
</feature>
<feature name="Battery">
<param name="android-package" value="org.apache.cordova.BatteryListener"/>
</feature>
<feature name="SplashScreen">
<param name="android-package" value="org.apache.cordova.SplashScreen"/>
</feature>
<feature name="Echo">
<param name="android-package" value="org.apache.cordova.Echo"/>
</feature>
<feature name="Globalization">
<param name="android-package" value="org.apache.cordova.Globalization"/>
</feature>
<feature name="InAppBrowser">
<param name="android-package" value="org.apache.cordova.InAppBrowser"/>
</feature>
<!-- Deprecated plugins element. Remove in 3.0 -->
<plugins>
</plugins>

View File

@ -1,285 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.List;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.Looper;
/**
* This class listens to the accelerometer sensor and stores the latest
* acceleration values x,y,z.
*/
public class AccelListener extends CordovaPlugin implements SensorEventListener {
public static int STOPPED = 0;
public static int STARTING = 1;
public static int RUNNING = 2;
public static int ERROR_FAILED_TO_START = 3;
private float x,y,z; // most recent acceleration values
private long timestamp; // time of most recent value
private int status; // status of listener
private int accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
private SensorManager sensorManager; // Sensor manager
private Sensor mSensor; // Acceleration sensor returned by sensor manager
private CallbackContext callbackContext; // Keeps track of the JS callback context.
/**
* Create an accelerometer listener.
*/
public AccelListener() {
this.x = 0;
this.y = 0;
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 cordova The context of the main Activity.
* @param webView The associated CordovaWebView.
*/
@Override
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
}
/**
* Executes the request.
*
* @param action The action to execute.
* @param args The exec() arguments.
* @param callbackId The callback id used when calling back into JavaScript.
* @return Whether the action was valid.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
if (action.equals("start")) {
this.callbackContext = callbackContext;
if (this.status != AccelListener.RUNNING) {
// If not running, then this is an async call, so don't worry about waiting
// We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
this.start();
}
}
else if (action.equals("stop")) {
if (this.status == AccelListener.RUNNING) {
this.stop();
}
} else {
// Unsupported action
return false;
}
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT, "");
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
return true;
}
/**
* Called by AccelBroker when listener is to be shut down.
* Stop listener.
*/
public void onDestroy() {
this.stop();
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
//
/**
* Start listening for acceleration sensor.
*
* @return status of listener
*/
private int start() {
// If already starting or running, then just return
if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
return this.status;
}
this.setStatus(AccelListener.STARTING);
// Get accelerometer from sensor manager
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
// If found, then register as listener
if ((list != null) && (list.size() > 0)) {
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_UI);
this.setStatus(AccelListener.STARTING);
} else {
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
return this.status;
}
// Set a timeout callback on the main thread.
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
public void run() {
AccelListener.this.timeout();
}
}, 2000);
return this.status;
}
/**
* Stop listening to acceleration sensor.
*/
private void stop() {
if (this.status != AccelListener.STOPPED) {
this.sensorManager.unregisterListener(this);
}
this.setStatus(AccelListener.STOPPED);
this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
}
/**
* Returns an error if the sensor hasn't started.
*
* Called two seconds after starting the listener.
*/
private void timeout() {
if (this.status == AccelListener.STARTING) {
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
}
}
/**
* Called when the accuracy of the sensor has changed.
*
* @param sensor
* @param accuracy
*/
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Only look at accelerometer events
if (sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
return;
}
// If not running, then just return
if (this.status == AccelListener.STOPPED) {
return;
}
this.accuracy = accuracy;
}
/**
* Sensor listener event.
*
* @param SensorEvent event
*/
public void onSensorChanged(SensorEvent event) {
// 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;
}
this.setStatus(AccelListener.RUNNING);
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
// 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.win();
}
}
/**
* Called when the view navigates.
*/
@Override
public void onReset() {
if (this.status == AccelListener.RUNNING) {
this.stop();
}
}
// Sends an error back to JS
private void fail(int code, String message) {
// Error object
JSONObject errorObj = new JSONObject();
try {
errorObj.put("code", code);
errorObj.put("message", message);
} catch (JSONException e) {
e.printStackTrace();
}
PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
err.setKeepCallback(true);
callbackContext.sendPluginResult(err);
}
private void win() {
// Success return object
PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
private void setStatus(int status) {
this.status = status;
}
private JSONObject getAccelerationJSON() {
JSONObject r = new JSONObject();
try {
r.put("x", this.x);
r.put("y", this.y);
r.put("z", this.z);
r.put("timestamp", this.timestamp);
} catch (JSONException e) {
e.printStackTrace();
}
return r;
}
}

View File

@ -1,221 +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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
/**
* This class exposes methods in DroidGap that can be called from JavaScript.
*/
public class App extends CordovaPlugin {
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackContext The callback context from which we were invoked.
* @return A PluginResult object with a status and message.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
try {
if (action.equals("clearCache")) {
this.clearCache();
}
else if (action.equals("show")) {
// This gets called from JavaScript onCordovaReady to show the webview.
// I recommend we change the name of the Message as spinner/stop is not
// indicative of what this actually does (shows the webview).
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
webView.postMessage("spinner", "stop");
}
});
}
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("overrideButton")) {
this.overrideButton(args.getString(0), args.getBoolean(1));
}
else if (action.equals("overrideBackbutton")) {
this.overrideBackbutton(args.getBoolean(0));
}
else if (action.equals("exitApp")) {
this.exitApp();
}
callbackContext.sendPluginResult(new PluginResult(status, result));
return true;
} catch (JSONException e) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
return false;
}
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Clear the resource cache.
*/
public void clearCache() {
this.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;
// If there are properties, then set them on the Activity
HashMap<String, Object> params = new HashMap<String, Object>();
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) {
}
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 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);
}
/**
* Clear page history for the app.
*/
public void clearHistory() {
this.webView.clearHistory();
}
/**
* Go to previous page displayed.
* This is the same as pressing the backbutton on Android device.
*/
public void backHistory() {
cordova.getActivity().runOnUiThread(new Runnable() {
public void run() {
webView.backHistory();
}
});
}
/**
* Override the default behavior of the Android back button.
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
*
* @param override T=override, F=cancel override
*/
public void overrideBackbutton(boolean override) {
LOG.i("App", "WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!");
webView.bindButton(override);
}
/**
* Override the default behavior of the Android volume buttons.
* If overridden, when the volume button is pressed, the "volume[up|down]button" JavaScript event will be fired.
*
* @param button volumeup, volumedown
* @param override T=override, F=cancel override
*/
public void overrideButton(String button, boolean override) {
LOG.i("DroidGap", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!");
webView.bindButton(button, override);
}
/**
* Return whether the Android back button is overridden by the user.
*
* @return boolean
*/
public boolean isBackbuttonOverridden() {
return webView.isBackButtonBound();
}
/**
* Exit the Android application.
*/
public void exitApp() {
this.webView.postMessage("exit", null);
}
}

View File

@ -1,362 +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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.DataResource;
import android.content.Context;
import android.media.AudioManager;
import java.util.ArrayList;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import java.util.HashMap;
/**
* This class called by CordovaActivity to play and record audio.
* The file can be local or over a network using http.
*
* Audio formats supported (tested):
* .mp3, .wav
*
* Local audio files must reside in one of two places:
* android_asset: file name must start with /android_asset/sound.mp3
* sdcard: file name is just sound.mp3
*/
public class AudioHandler extends CordovaPlugin {
public static String TAG = "AudioHandler";
HashMap<String, AudioPlayer> players; // Audio player object
ArrayList<AudioPlayer> pausedForPhone; // Audio players that were paused when phone call came in
/**
* Constructor.
*/
public AudioHandler() {
this.players = new HashMap<String, AudioPlayer>();
this.pausedForPhone = new ArrayList<AudioPlayer>();
}
public String getFilePath(String url, String source){
DataResource dataResource = DataResource.initiateNewDataRequestForUri(url, this.webView.pluginManager, cordova, source);
return dataResource.getRealFile().getPath();
}
/**
* Executes the request and returns PluginResult.
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackContext The callback context used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
if (action.equals("startRecordingAudio")) {
this.startRecordingAudio(args.getString(0), getFilePath(args.getString(1), "AudioHandler.startRecordingAudio"));
}
else if (action.equals("stopRecordingAudio")) {
this.stopRecordingAudio(args.getString(0));
}
else if (action.equals("startPlayingAudio")) {
this.startPlayingAudio(args.getString(0), getFilePath(args.getString(1), "AudioHandler.startPlayingAudio"));
}
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));
callbackContext.sendPluginResult(new PluginResult(status, f));
return true;
}
else if (action.equals("getDurationAudio")) {
float f = this.getDurationAudio(args.getString(0), args.getString(1));
callbackContext.sendPluginResult(new PluginResult(status, f));
return true;
}
else if (action.equals("create")) {
String id = args.getString(0);
String src = getFilePath(args.getString(1), "AudioHandler.create");
AudioPlayer audio = new AudioPlayer(this, id, src);
this.players.put(id, audio);
}
else if (action.equals("release")) {
boolean b = this.release(args.getString(0));
callbackContext.sendPluginResult(new PluginResult(status, b));
return true;
}
else { // Unrecognized action.
return false;
}
callbackContext.sendPluginResult(new PluginResult(status, result));
return true;
}
/**
* Stop all audio players and recorders.
*/
public void onDestroy() {
for (AudioPlayer audio : this.players.values()) {
audio.destroy();
}
this.players.clear();
}
/**
* Stop all audio players and recorders on navigate.
*/
@Override
public void onReset() {
onDestroy();
}
/**
* Called when a message is sent to plugin.
*
* @param id The message id
* @param data The message data
* @return Object to stop propagation or null
*/
public Object 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.STATE.MEDIA_RUNNING.ordinal()) {
this.pausedForPhone.add(audio);
audio.pausePlaying();
}
}
}
// If phone idle, then resume playing those players we paused
else if ("idle".equals(data)) {
for (AudioPlayer audio : this.pausedForPhone) {
audio.startPlaying(null);
}
this.pausedForPhone.clear();
}
}
return null;
}
//--------------------------------------------------------------------------
// 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
*/
public void startRecordingAudio(String id, String file) {
AudioPlayer audio = this.players.get(id);
if ( audio == null) {
audio = new AudioPlayer(this, id, file);
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
*/
public void stopRecordingAudio(String id) {
AudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.stopRecording();
}
}
/**
* Start or resume playing audio file.
* @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, file);
this.players.put(id, audio);
}
audio.startPlaying(file);
}
/**
* Seek to a location.
* @param id The id of the audio player
* @param milliseconds 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);
}
}
/**
* Pause playing.
* @param id The id of the audio player
*/
public void pausePlayingAudio(String id) {
AudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.pausePlaying();
}
}
/**
* Stop playing the audio file.
* @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);
}
}
/**
* Get current position of playback.
* @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;
}
/**
* Get the duration of the audio file.
* @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, file);
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.cordova.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.cordova.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;
}
}
/**
* Set the volume for an audio device
*
* @param id The id of the audio player
* @param volume Volume to adjust to 0.0f - 1.0f
*/
public void setVolume(String id, float volume) {
AudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.setVolume(volume);
} else {
System.out.println("AudioHandler.setVolume() Error: Unknown Audio Player " + id);
}
}
}

View File

@ -1,553 +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 android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaRecorder;
import android.os.Environment;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* This class implements the audio playback and recording capabilities used by Cordova.
* It is called by the AudioHandler Cordova class.
* Only one file can be played or recorded per class instance.
*
* Local audio files must reside in one of two places:
* android_asset: file name must start with /android_asset/sound.mp3
* sdcard: file name is just sound.mp3
*/
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
// AudioPlayer modes
public enum MODE { NONE, PLAY, RECORD };
// AudioPlayer states
public enum STATE { MEDIA_NONE,
MEDIA_STARTING,
MEDIA_RUNNING,
MEDIA_PAUSED,
MEDIA_STOPPED,
MEDIA_LOADING
};
private static final String LOG_TAG = "AudioPlayer";
// 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 MODE mode = MODE.NONE; // Playback or Recording mode
private STATE state = 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 player = null; // Audio player object
private boolean prepareOnly = true; // playback after file prepare flag
private int seekOnPrepared = 0; // seek to this location once media is prepared
/**
* Constructor.
*
* @param handler The audio handler object
* @param id The id of this audio player
*/
public AudioPlayer(AudioHandler handler, String id, String file) {
this.handler = handler;
this.id = id;
this.audioFile = file;
this.recorder = new MediaRecorder();
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.3gp";
} else {
this.tempFile = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording.3gp";
}
}
/**
* Destroy player and stop audio playing or recording.
*/
public void destroy() {
// Stop any play or record
if (this.player != null) {
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
this.player.stop();
this.setState(STATE.MEDIA_STOPPED);
}
this.player.release();
this.player = 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) {
switch (this.mode) {
case PLAY:
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
break;
case NONE:
this.audioFile = file;
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(STATE.MEDIA_RUNNING);
return;
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
break;
case RECORD:
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
this.handler.webView.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);
if (!file.startsWith("/")) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
file = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + file;
} else {
file = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/" + file;
}
}
String logMsg = "renaming " + this.tempFile + " to " + file;
Log.d(LOG_TAG, logMsg);
if (!f.renameTo(new File(file))) Log.e(LOG_TAG, "FAILED " + logMsg);
}
/**
* Stop recording and save to the file specified when recording started.
*/
public void stopRecording() {
if (this.recorder != null) {
try{
if (this.state == STATE.MEDIA_RUNNING) {
this.recorder.stop();
this.setState(STATE.MEDIA_STOPPED);
}
this.recorder.reset();
this.moveFile(this.audioFile);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
//==========================================================================
// Playback
//==========================================================================
/**
* Start or resume playing audio file.
*
* @param file The name of the audio file.
*/
public void startPlaying(String file) {
if (this.readyPlayer(file) && this.player != null) {
this.player.start();
this.setState(STATE.MEDIA_RUNNING);
this.seekOnPrepared = 0; //insures this is always reset
} else {
this.prepareOnly = false;
}
}
/**
* Seek or jump to a new time in the track.
*/
public void seekToPlaying(int milliseconds) {
if (this.readyPlayer(this.audioFile)) {
this.player.seekTo(milliseconds);
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
}
else {
this.seekOnPrepared = milliseconds;
}
}
/**
* Pause playing.
*/
public void pausePlaying() {
// If playing, then pause
if (this.state == STATE.MEDIA_RUNNING && this.player != null) {
this.player.pause();
this.setState(STATE.MEDIA_PAUSED);
}
else {
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state.ordinal());
this.handler.webView.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 == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
this.player.pause();
this.player.seekTo(0);
Log.d(LOG_TAG, "stopPlaying is calling stopped");
this.setState(STATE.MEDIA_STOPPED);
}
else {
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state.ordinal());
this.handler.webView.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 player The MediaPlayer that reached the end of the file
*/
public void onCompletion(MediaPlayer player) {
Log.d(LOG_TAG, "on completion is calling stopped");
this.setState(STATE.MEDIA_STOPPED);
}
/**
* Get current position of playback.
*
* @return position in msec or -1 if not playing
*/
public long getCurrentPosition() {
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
int curPos = this.player.getCurrentPosition();
this.handler.webView.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.
*
* @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) {
// 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.player != 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;
}
}
/**
* Callback to be invoked when the media source is ready for playback.
*
* @param player The MediaPlayer that is ready for playback
*/
public void onPrepared(MediaPlayer player) {
// Listen for playback completion
this.player.setOnCompletionListener(this);
// seek to any location received while not prepared
this.seekToPlaying(this.seekOnPrepared);
// If start playing after prepared
if (!this.prepareOnly) {
this.player.start();
this.setState(STATE.MEDIA_RUNNING);
this.seekOnPrepared = 0; //reset only when played
} else {
this.setState(STATE.MEDIA_STARTING);
}
// Save off duration
this.duration = getDurationInSeconds();
// reset prepare only flag
this.prepareOnly = true;
// Send status notification to JavaScript
this.handler.webView.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.player.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 player 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 player, int arg1, int arg2) {
Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2 + ")");
// TODO: Not sure if this needs to be sent?
this.player.stop();
this.player.release();
// Send error notification to JavaScript
this.handler.webView.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(STATE state) {
if (this.state != state) {
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state.ordinal() + ");");
}
this.state = state;
}
/**
* Set the mode and send it to JavaScript.
*
* @param state
*/
private void setMode(MODE mode) {
if (this.mode != mode) {
//mode is not part of the expected behavior, so no notification
//this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + mode + ");");
}
this.mode = mode;
}
/**
* Get the audio state.
*
* @return int
*/
public int getState() {
return this.state.ordinal();
}
/**
* Set the volume for audio player
*
* @param volume
*/
public void setVolume(float volume) {
this.player.setVolume(volume, volume);
}
/**
* attempts to put the player in play mode
* @return true if in playmode, false otherwise
*/
private boolean playMode() {
switch(this.mode) {
case NONE:
this.setMode(MODE.PLAY);
break;
case PLAY:
break;
case RECORD:
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
return false; //player is not ready
}
return true;
}
/**
* attempts to initialize the media player for playback
* @param file the file to play
* @return false if player not ready, reports if in wrong mode or state
*/
private boolean readyPlayer(String file) {
if (playMode()) {
switch (this.state) {
case MEDIA_NONE:
if (this.player == null) {
this.player = new MediaPlayer();
}
try {
this.loadAudioFile(file);
} catch (Exception e) {
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
}
return false;
case MEDIA_LOADING:
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING instead
Log.d(LOG_TAG, "AudioPlayer Loading: startPlaying() called during media preparation: " + STATE.MEDIA_STARTING.ordinal());
this.prepareOnly = false;
return false;
case MEDIA_STARTING:
case MEDIA_RUNNING:
case MEDIA_PAUSED:
return true;
case MEDIA_STOPPED:
//if we are readying the same file
if (this.audioFile.compareTo(file) == 0) {
//reset the audio file
player.seekTo(0);
player.pause();
return true;
} else {
//reset the player
this.player.reset();
try {
this.loadAudioFile(file);
} catch (Exception e) {
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
//if we had to prepare= the file, we won't be in the correct state for playback
return false;
}
default:
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: " + this.state);
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
}
return false;
}
/**
* load audio file
* @throws IOException
* @throws IllegalStateException
* @throws SecurityException
* @throws IllegalArgumentException
*/
private void loadAudioFile(String file) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
if (this.isStreaming(file)) {
this.player.setDataSource(file);
this.player.setAudioStreamType(AudioManager.STREAM_MUSIC);
//if it's a streaming file, play mode is implied
this.setMode(MODE.PLAY);
this.setState(STATE.MEDIA_STARTING);
this.player.setOnPreparedListener(this);
this.player.prepareAsync();
}
else {
if (file.startsWith("/android_asset/")) {
String f = file.substring(15);
android.content.res.AssetFileDescriptor fd = this.handler.cordova.getActivity().getAssets().openFd(f);
this.player.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
}
else {
File fp = new File(file);
if (fp.exists()) {
FileInputStream fileInputStream = new FileInputStream(file);
this.player.setDataSource(fileInputStream.getFD());
}
else {
this.player.setDataSource("/sdcard/" + file);
}
}
this.setState(STATE.MEDIA_STARTING);
this.player.setOnPreparedListener(this);
this.player.prepare();
// Get duration
this.duration = getDurationInSeconds();
}
}
}

View File

@ -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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
public class BatteryListener extends CordovaPlugin {
private static final String LOG_TAG = "BatteryManager";
BroadcastReceiver receiver;
private CallbackContext batteryCallbackContext = null;
/**
* Constructor.
*/
public BatteryListener() {
this.receiver = null;
}
/**
* Executes the request.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackContext The callback context used when calling back into JavaScript.
* @return True if the action was valid, false if not.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
if (action.equals("start")) {
if (this.batteryCallbackContext != null) {
callbackContext.error( "Battery listener already running.");
return true;
}
this.batteryCallbackContext = callbackContext;
// We need to listen to power events to update battery status
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);
}
};
cordova.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);
callbackContext.sendPluginResult(pluginResult);
return true;
}
else if (action.equals("stop")) {
removeBatteryListener();
this.sendUpdate(new JSONObject(), false); // release status callback in JS side
this.batteryCallbackContext = null;
callbackContext.success();
return true;
}
return false;
}
/**
* Stop battery receiver.
*/
public void onDestroy() {
removeBatteryListener();
}
/**
* Stop battery receiver.
*/
public void onReset() {
removeBatteryListener();
}
/**
* Stop the battery receiver and set it to null.
*/
private void removeBatteryListener() {
if (this.receiver != null) {
try {
this.cordova.getActivity().unregisterReceiver(this.receiver);
this.receiver = null;
} catch (Exception e) {
Log.e(LOG_TAG, "Error unregistering battery receiver: " + e.getMessage(), e);
}
}
}
/**
* Creates a JSONObject with the current battery information
*
* @param batteryIntent the current battery information
* @return a JSONObject containing the battery status information
*/
private JSONObject getBatteryInfo(Intent batteryIntent) {
JSONObject obj = new JSONObject();
try {
obj.put("level", batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_LEVEL, 0));
obj.put("isPlugged", batteryIntent.getIntExtra(android.os.BatteryManager.EXTRA_PLUGGED, -1) > 0 ? true : false);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return obj;
}
/**
* Updates the JavaScript side whenever the battery changes
*
* @param batteryIntent the current battery information
* @return
*/
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.batteryCallbackContext != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, info);
result.setKeepCallback(keepCallback);
this.batteryCallbackContext.sendPluginResult(result);
}
}
}

View File

@ -1,824 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.codec.binary.Base64;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.DataResource;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Bitmap.CompressFormat;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
/**
* This class launches the camera view, allows the user to take a picture, closes the camera view,
* and returns the captured image. When the camera view is closed, the screen displayed before
* the camera view was shown is redisplayed.
*/
public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient {
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 NATIVE_URI = 2; // On Android, this is the same as FILE_URI
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
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
private boolean correctOrientation; // Should the pictures orientation be corrected
//private boolean allowEdit; // Should we allow the user to crop the image. UNUSED.
public CallbackContext callbackContext;
private int numPics;
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
private Uri scanMe; // Uri of image to be added to content store
//This should never be null!
//private CordovaInterface cordova;
/**
* Constructor.
*/
public CameraLauncher() {
}
// 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.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackContext The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
this.callbackContext = callbackContext;
if (action.equals("takePicture")) {
int srcType = CAMERA;
int destType = FILE_URI;
this.saveToPhotoAlbum = false;
this.targetHeight = 0;
this.targetWidth = 0;
this.encodingType = JPEG;
this.mediaType = PICTURE;
this.mQuality = 80;
this.mQuality = args.getInt(0);
destType = args.getInt(1);
srcType = args.getInt(2);
this.targetWidth = args.getInt(3);
this.targetHeight = args.getInt(4);
this.encodingType = args.getInt(5);
this.mediaType = args.getInt(6);
//this.allowEdit = args.getBoolean(7); // This field is unused.
this.correctOrientation = args.getBoolean(8);
this.saveToPhotoAlbum = args.getBoolean(9);
// If the user specifies a 0 or smaller width/height
// make it -1 so later comparisons succeed
if (this.targetWidth < 1) {
this.targetWidth = -1;
}
if (this.targetHeight < 1) {
this.targetHeight = -1;
}
if (srcType == CAMERA) {
this.takePicture(destType, encodingType);
}
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
this.getImage(srcType, destType);
}
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
r.setKeepCallback(true);
callbackContext.sendPluginResult(r);
return true;
}
return false;
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Take a picture with the camera.
* When an image is captured or the camera view is cancelled, the result is returned
* in CordovaActivity.onActivityResult, which forwards the result to this.onActivityResult.
*
* The image can either be returned as a base64 string or a URI that points to the file.
* To display base64 string in an img tag, set the source to:
* img.src="data:image/jpeg;base64,"+result;
* or to display URI in an img tag
* img.src=result;
*
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
* @param returnType Set the type of image to return.
*/
public void takePicture(int returnType, int encodingType) {
// Save the number of images currently on disk for later
this.numPics = queryImgDB(whichContentStore()).getCount();
// Display camera
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
// Specify file so that large image is captured and returned
File photo = createCaptureFile(encodingType);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
this.imageUri = Uri.fromFile(photo);
if (this.cordova != null) {
this.cordova.startActivityForResult((CordovaPlugin) 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");
}
/**
* Create a file in the applications temporary directory based upon the supplied encoding.
*
* @param encodingType of the image to be taken
* @return a File object pointing to the temporary picture
*/
private File createCaptureFile(int encodingType) {
File photo = null;
if (encodingType == JPEG) {
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg");
} else if (encodingType == PNG) {
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png");
} else {
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
}
return photo;
}
/**
* Get image from photo library.
*
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
* @param srcType The album to get image from.
* @param returnType Set the type of image to return.
*/
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
public void getImage(int srcType, int returnType) {
Intent intent = new Intent();
String title = GET_PICTURE;
if (this.mediaType == PICTURE) {
intent.setType("image/*");
}
else if (this.mediaType == 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("*/*");
title = GET_All;
}
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
if (this.cordova != null) {
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
new String(title)), (srcType + 1) * 16 + returnType + 1);
}
}
/**
* Called when the camera view exits.
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @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 destType = (requestCode % 16) - 1;
int rotate = 0;
// If CAMERA
if (srcType == CAMERA) {
// If image available
if (resultCode == Activity.RESULT_OK) {
try {
// 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.cordova.getActivity()) + "/.Pic.jpg");
exif.readExifData();
rotate = exif.getOrientation();
}
} catch (IOException e) {
e.printStackTrace();
}
Bitmap bitmap = null;
Uri uri = null;
// If sending base64 image back
if (destType == DATA_URL) {
bitmap = getScaledBitmap(imageUri.toString());
if (bitmap == null) {
// Try to get the bitmap from intent.
bitmap = (Bitmap)intent.getExtras().get("data");
}
// Double-check the bitmap.
if (bitmap == null) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
this.failPicture("Unable to create bitmap!");
return;
}
if (rotate != 0 && this.correctOrientation) {
bitmap = getRotatedBitmap(rotate, bitmap, exif);
}
this.processPicture(bitmap);
checkForDuplicateImage(DATA_URL);
}
// If sending filename back
else if (destType == FILE_URI || destType == NATIVE_URI) {
if (this.saveToPhotoAlbum) {
Uri inputUri = getUriFromMediaStore();
//Just because we have a media URI doesn't mean we have a real file, we need to make it
DataResource dataResource = DataResource.initiateNewDataRequestForUri(inputUri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent");
File file = dataResource.getRealFile();
uri = Uri.fromFile(file);
} else {
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
}
if (uri == null) {
this.failPicture("Error capturing image - no media storage found.");
}
// If all this is true we shouldn't compress the image.
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
!this.correctOrientation) {
writeUncompressedImage(uri);
this.callbackContext.success(uri.toString());
} else {
bitmap = getScaledBitmap(imageUri.toString());
if (rotate != 0 && this.correctOrientation) {
bitmap = getRotatedBitmap(rotate, bitmap, exif);
}
// Add compressed version of captured image to returned media store Uri
DataResource dataResource = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent");
OutputStream os = dataResource.getOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
// Restore exif data to file
if (this.encodingType == JPEG) {
String exifPath;
if (this.saveToPhotoAlbum) {
exifPath = dataResource.getRealFile().getPath();
} else {
exifPath = uri.getPath();
}
exif.createOutFile(exifPath);
exif.writeExifData();
}
}
// Send Uri back to JavaScript for viewing image
this.callbackContext.success(uri.toString());
}
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
bitmap = null;
} catch (IOException e) {
e.printStackTrace();
this.failPicture("Error capturing image.");
}
}
// If cancelled
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Camera cancelled.");
}
// If something else
else {
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();
// 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.callbackContext.success(uri.toString());
}
else {
// This is a special case to just return the path as no scaling,
// rotating, nor compressing needs to be done
if (this.targetHeight == -1 && this.targetWidth == -1 &&
(destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation) {
this.callbackContext.success(uri.toString());
} else {
String uriString = uri.toString();
DataResource dataResource = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent");
// Get the path to the image. Makes loading so much easier.
String mimeType = dataResource.getMimeType();
// If we don't have a valid image so quit.
if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
this.failPicture("Unable to retrieve path to picture!");
return;
}
Bitmap bitmap = null;
try {
bitmap = getScaledBitmap(uriString);
} catch (IOException e) {
e.printStackTrace();
}
if (bitmap == null) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
this.failPicture("Unable to create bitmap!");
return;
}
if (this.correctOrientation) {
rotate = getImageOrientation(uri);
if (rotate != 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotate);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}
// If sending base64 image back
if (destType == DATA_URL) {
this.processPicture(bitmap);
}
// If sending filename back
else if (destType == FILE_URI || destType == NATIVE_URI) {
// Do we need to scale the returned file
if (this.targetHeight > 0 && this.targetWidth > 0) {
try {
// Create an ExifHelper to save the exif data that is lost during compression
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
// Some content: URIs do not map to file paths (e.g. picasa).
File realFile = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.CameraExitIntent").getRealFile();
String realPath = realFile != null? realFile.getPath() : null;
ExifHelper exif = new ExifHelper();
if (realPath != null && this.encodingType == JPEG) {
try {
exif.createInFile(realPath);
exif.readExifData();
rotate = exif.getOrientation();
} catch (IOException e) {
e.printStackTrace();
}
}
OutputStream os = new FileOutputStream(resizePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
// Restore exif data to file
if (realPath != null && this.encodingType == JPEG) {
exif.createOutFile(resizePath);
exif.writeExifData();
}
// 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.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
}
}
else {
this.callbackContext.success(uri.toString());
}
}
if (bitmap != null) {
bitmap.recycle();
bitmap = null;
}
System.gc();
}
}
}
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Selection cancelled.");
}
else {
this.failPicture("Selection did not complete!");
}
}
}
private int getImageOrientation(Uri uri) {
String[] cols = { MediaStore.Images.Media.ORIENTATION };
Cursor cursor = cordova.getActivity().getContentResolver().query(uri,
cols, null, null, null);
int rotate = 0;
if (cursor != null) {
cursor.moveToPosition(0);
rotate = cursor.getInt(0);
cursor.close();
}
return rotate;
}
/**
* Figure out if the bitmap should be rotated. For instance if the picture was taken in
* portrait mode
*
* @param rotate
* @param bitmap
* @return rotated bitmap
*/
private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
Matrix matrix = new Matrix();
if (rotate == 180) {
matrix.setRotate(rotate);
} else {
matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
}
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
exif.resetOrientation();
return bitmap;
}
/**
* In the special case where the default width, height and quality are unchanged
* we just write the file out to disk saving the expensive Bitmap.compress function.
*
* @param uri
* @throws FileNotFoundException
* @throws IOException
*/
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
IOException {
DataResource inputDataResource = DataResource.initiateNewDataRequestForUri(imageUri, webView.pluginManager, cordova, "CameraLauncher.writeUncompressedImage");
InputStream fis = inputDataResource.getInputStream();
DataResource outDataResource = DataResource.initiateNewDataRequestForUri(uri, webView.pluginManager, cordova, "CameraLauncher.writeUncompressedImage");
OutputStream os = outDataResource.getOutputStream();
if(fis == null) {
throw new FileNotFoundException("Could not get the input file");
} else if(os == null) {
throw new FileNotFoundException("Could not get the output file");
}
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
os.close();
fis.close();
}
/**
* Create entry in media store for image
*
* @return uri
*/
private Uri getUriFromMediaStore() {
ContentValues values = new ContentValues();
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri uri;
try {
uri = this.cordova.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.cordova.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.");
return null;
}
}
return uri;
}
/**
* Return a scaled bitmap based on the target width and height
*
* @param imagePath
* @return
* @throws IOException
*/
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
// If no new width or height were specified return the original bitmap
DataResource dataResource = DataResource.initiateNewDataRequestForUri(imageUrl, webView.pluginManager, cordova, "CameraLauncher.getScaledBitmap");
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
return BitmapFactory.decodeStream(dataResource.getInputStream());
}
// figure out the original width and height of the image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(dataResource.getInputStream(), null, options);
//CB-2292: WTF? Why is the width null?
if(options.outWidth == 0 || options.outHeight == 0)
{
return null;
}
// determine the correct aspect ratio
int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
// Load in the smallest bitmap possible that is closest to the size we want
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
Bitmap unscaledBitmap = BitmapFactory.decodeStream(dataResource.getInputStream(), null, options);
if (unscaledBitmap == null) {
return null;
}
return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
}
/**
* Maintain the aspect ratio so the resulting image does not look smooshed
*
* @param origWidth
* @param origHeight
* @return
*/
public int[] calculateAspectRatio(int origWidth, int origHeight) {
int newWidth = this.targetWidth;
int newHeight = this.targetHeight;
// If no new width or height were specified return the original bitmap
if (newWidth <= 0 && newHeight <= 0) {
newWidth = origWidth;
newHeight = origHeight;
}
// Only the width was specified
else if (newWidth > 0 && newHeight <= 0) {
newHeight = (newWidth * origHeight) / origWidth;
}
// only the height was specified
else if (newWidth <= 0 && newHeight > 0) {
newWidth = (newHeight * origWidth) / origHeight;
}
// If the user specified both a positive width and height
// (potentially different aspect ratio) then the width or height is
// scaled so that the image fits while maintaining aspect ratio.
// Alternatively, the specified width and height could have been
// kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
// would result in whitespace in the new image.
else {
double newRatio = newWidth / (double) newHeight;
double origRatio = origWidth / (double) origHeight;
if (origRatio > newRatio) {
newHeight = (newWidth * origHeight) / origWidth;
} else if (origRatio < newRatio) {
newWidth = (newHeight * origWidth) / origHeight;
}
}
int[] retval = new int[2];
retval[0] = newWidth;
retval[1] = newHeight;
return retval;
}
/**
* Figure out what ratio we can load our image into memory at while still being bigger than
* our desired width and height
*
* @param srcWidth
* @param srcHeight
* @param dstWidth
* @param dstHeight
* @return
*/
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
final float srcAspect = (float)srcWidth / (float)srcHeight;
final float dstAspect = (float)dstWidth / (float)dstHeight;
if (srcAspect > dstAspect) {
return srcWidth / dstWidth;
} else {
return srcHeight / dstHeight;
}
}
/**
* Creates a cursor that can be used to determine how many images we have.
*
* @return a cursor
*/
private Cursor queryImgDB(Uri contentStore) {
return this.cordova.getActivity().getContentResolver().query(
contentStore,
new String[] { MediaStore.Images.Media._ID },
null,
null,
null);
}
/**
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
* @param newImage
*/
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
if (bitmap != null) {
bitmap.recycle();
}
DataResource dataResource = DataResource.initiateNewDataRequestForUri(oldImage, webView.pluginManager, cordova, "CameraLauncher.cleanup");
File file = dataResource.getRealFile();
if(file != null) {
// Clean up initial camera-written image file.
file.delete();
checkForDuplicateImage(imageType);
// Scan for the gallery to update pic refs in gallery
if (this.saveToPhotoAlbum && newImage != null) {
this.scanForGallery(newImage);
}
System.gc();
}
}
/**
* 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
* increases by 2 we have a duplicate, when using a DATA_URL the number is 1.
*
* @param type FILE_URI or DATA_URL
*/
private void checkForDuplicateImage(int type) {
int diff = 1;
Uri contentStore = whichContentStore();
Cursor cursor = queryImgDB(contentStore);
int currentNumOfImages = cursor.getCount();
if (type == FILE_URI && this.saveToPhotoAlbum) {
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)));
if (diff == 2) {
id--;
}
Uri uri = Uri.parse(contentStore + "/" + id);
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
}
}
/**
* Determine if we are storing the images in internal or external storage
* @return Uri
*/
private Uri whichContentStore() {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else {
return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
}
}
/**
* Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript.
*
* @param 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[] output = Base64.encodeBase64(code);
String js_out = new String(output);
this.callbackContext.success(js_out);
js_out = null;
output = null;
code = null;
}
} catch (Exception e) {
this.failPicture("Error compressing image.");
}
jpeg_data = null;
}
/**
* Send error message to JavaScript.
*
* @param err
*/
public void failPicture(String err) {
this.callbackContext.error(err);
}
private void scanForGallery(Uri newImage) {
this.scanMe = newImage;
if(this.conn != null) {
this.conn.disconnect();
}
this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
conn.connect();
}
public void onMediaScannerConnected() {
try{
this.conn.scanFile(this.scanMe.toString(), "image/*");
} catch (java.lang.IllegalStateException e){
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
}
}
public void onScanCompleted(String path, Uri uri) {
this.conn.disconnect();
}
}

View File

@ -1,450 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import android.os.Build;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.DataResource;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
public class Capture extends CordovaPlugin {
private static final String VIDEO_3GPP = "video/3gpp";
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_NO_MEDIA_FILES = 3;
private CallbackContext callbackContext; // The callback context from which we were invoked.
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 int numPics; // Number of pictures before capture 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 boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
this.callbackContext = callbackContext;
this.limit = 1;
this.duration = 0.0f;
this.results = new JSONArray();
JSONObject options = args.optJSONObject(0);
if (options != null) {
limit = options.optLong("limit", 1);
duration = options.optDouble("duration", 0.0f);
}
if (action.equals("getFormatData")) {
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
callbackContext.success(obj);
return true;
}
else if (action.equals("captureAudio")) {
this.captureAudio();
}
else if (action.equals("captureImage")) {
this.captureImage();
}
else if (action.equals("captureVideo")) {
this.captureVideo(duration);
}
else {
return false;
}
return true;
}
/**
* Provides the media data file data depending on it's mime type
*
* @param filePath path to the file
* @param mimeType of the file
* @return a MediaFileData object
*/
private JSONObject getFormatData(String filePath, String mimeType) throws JSONException {
JSONObject obj = new JSONObject();
// setup defaults
obj.put("height", 0);
obj.put("width", 0);
obj.put("bitrate", 0);
obj.put("duration", 0);
obj.put("codecs", "");
// If the mimeType isn't set the rest will fail
// so let's see if we can determine it.
if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) {
DataResource dataResource = DataResource.initiateNewDataRequestForUri(filePath, webView.pluginManager, cordova, "Capture.dataResource");
mimeType = dataResource.getMimeType();
}
Log.d(LOG_TAG, "Mime type = " + mimeType);
if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
obj = getImageData(filePath, obj);
}
else if (mimeType.endsWith(AUDIO_3GPP)) {
obj = getAudioVideoData(filePath, obj, false);
}
else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
obj = getAudioVideoData(filePath, obj, true);
}
return obj;
}
/**
* Get the Image specific attributes
*
* @param filePath path to the file
* @param obj represents the Media File Data
* @return a JSONObject that represents the Media File Data
* @throws JSONException
*/
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
DataResource dataResource = DataResource.initiateNewDataRequestForUri(filePath, webView.pluginManager, cordova, "Capture.getImageData");
BitmapFactory.decodeFile(dataResource.getRealFile().getPath(), options);
obj.put("height", options.outHeight);
obj.put("width", options.outWidth);
return obj;
}
/**
* Get the Image specific attributes
*
* @param filePath path to the file
* @param obj represents the Media File Data
* @param video if true get video attributes as well
* @return a JSONObject that represents the Media File Data
* @throws JSONException
*/
private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
MediaPlayer player = new MediaPlayer();
try {
player.setDataSource(filePath);
player.prepare();
obj.put("duration", player.getDuration() / 1000);
if (video) {
obj.put("height", player.getVideoHeight());
obj.put("width", player.getVideoWidth());
}
} catch (IOException e) {
Log.d(LOG_TAG, "Error: loading video file");
}
return obj;
}
/**
* Sets up an intent to capture audio. Result handled by onActivityResult()
*/
private void captureAudio() {
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_AUDIO);
}
/**
* Sets up an intent to capture images. Result handled by onActivityResult()
*/
private void captureImage() {
// Save the number of images currently on disk for later
this.numPics = queryImgDB(whichContentStore()).getCount();
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(this.cordova.getActivity()), "Capture.jpg");
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_IMAGE);
}
/**
* Sets up an intent to capture video. Result handled by onActivityResult()
*/
private void captureVideo(double duration) {
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
if(Build.VERSION.SDK_INT > 8){
intent.putExtra("android.intent.extra.durationLimit", duration);
}
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
}
/**
* Called when the video view exits.
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
* @throws JSONException
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
// Result received okay
if (resultCode == Activity.RESULT_OK) {
// An audio clip was requested
if (requestCode == CAPTURE_AUDIO) {
// Get the uri of the audio clip
Uri data = intent.getData();
// create a file object from the uri
results.put(createMediaFile(data));
if (results.length() >= limit) {
// Send Uri back to JavaScript for listening to audio
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
} else {
// still need to capture more audio clips
captureAudio();
}
} else if (requestCode == CAPTURE_IMAGE) {
// For some reason if I try to do:
// Uri data = intent.getData();
// It crashes in the emulator and on my phone with a null pointer exception
// To work around it I had to grab the code from CameraLauncher.java
try {
// 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.cordova.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.cordova.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.");
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found."));
return;
}
}
FileInputStream fis = new FileInputStream(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
os.close();
fis.close();
// Add image to results
results.put(createMediaFile(uri));
checkForDuplicateImage();
if (results.length() >= limit) {
// Send Uri back to JavaScript for viewing image
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
} else {
// still need to capture more images
captureImage();
}
} catch (IOException e) {
e.printStackTrace();
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image."));
}
} else if (requestCode == CAPTURE_VIDEO) {
// Get the uri of the video clip
Uri data = intent.getData();
// create a file object from the uri
results.put(createMediaFile(data));
if (results.length() >= limit) {
// Send Uri back to JavaScript for viewing video
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
} else {
// still need to capture more video clips
captureVideo(duration);
}
}
}
// If canceled
else if (resultCode == Activity.RESULT_CANCELED) {
// If we have partial results send them back to the user
if (results.length() > 0) {
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
}
// user canceled the action
else {
this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Canceled."));
}
}
// If something else
else {
// If we have partial results send them back to the user
if (results.length() > 0) {
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
}
// something bad happened
else {
this.fail(createErrorObject(CAPTURE_NO_MEDIA_FILES, "Did not complete!"));
}
}
}
/**
* Creates a JSONObject that represents a File from the Uri
*
* @param data the Uri of the audio/image/video
* @return a JSONObject that represents a File
* @throws IOException
*/
private JSONObject createMediaFile(Uri data) {
DataResource dataResource = DataResource.initiateNewDataRequestForUri(data, webView.pluginManager, cordova, "Capture.createMediaFile");
File fp = dataResource.getRealFile();
JSONObject obj = new JSONObject();
try {
// File properties
obj.put("name", fp.getName());
obj.put("fullPath", "file://" + 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);
} else {
obj.put("type", VIDEO_3GPP);
}
} else {
obj.put("type", dataResource.getMimeType());
}
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 {
obj.put("code", code);
obj.put("message", message);
} catch (JSONException e) {
// This will never happen
}
return obj;
}
/**
* Send error message to JavaScript.
*
* @param err
*/
public void fail(JSONObject err) {
this.callbackContext.error(err);
}
/**
* Creates a cursor that can be used to determine how many images we have.
*
* @return a cursor
*/
private Cursor queryImgDB(Uri contentStore) {
return this.cordova.getActivity().getContentResolver().query(
contentStore,
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.
*/
private void checkForDuplicateImage() {
Uri contentStore = whichContentStore();
Cursor cursor = queryImgDB(contentStore);
int currentNumOfImages = cursor.getCount();
// delete the duplicate file if the difference is 2
if ((currentNumOfImages - numPics) == 2) {
cursor.moveToLast();
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
Uri uri = Uri.parse(contentStore + "/" + id);
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
}
}
/**
* Determine if we are storing the images in internal or external storage
* @return Uri
*/
private Uri whichContentStore() {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else {
return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
}
}
}

View File

@ -1,295 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.List;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
/**
* This class listens to the compass sensor and stores the latest heading value.
*/
public class CompassListener extends CordovaPlugin implements SensorEventListener {
public static int STOPPED = 0;
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
private CallbackContext callbackContext;
/**
* Constructor.
*/
public CompassListener() {
this.heading = 0;
this.timeStamp = 0;
this.setStatus(CompassListener.STOPPED);
}
/**
* Sets the context of the Command. This can then be used to do things like
* get file paths associated with the Activity.
*
* @param cordova The context of the main Activity.
* @param webView The CordovaWebView Cordova is running in.
*/
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.sensorManager = (SensorManager) cordova.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 callbackS=Context The callback id used when calling back into JavaScript.
* @return True if the action was valid.
* @throws JSONException
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("start")) {
this.start();
}
else if (action.equals("stop")) {
this.stop();
}
else if (action.equals("getStatus")) {
int i = this.getStatus();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i));
}
else if (action.equals("getHeading")) {
// If not running, then this is an async call, so don't worry about waiting
if (this.status != CompassListener.RUNNING) {
int r = this.start();
if (r == CompassListener.ERROR_FAILED_TO_START) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START));
return true;
}
// Set a timeout callback on the main thread.
Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() {
public void run() {
CompassListener.this.timeout();
}
}, 2000);
}
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, getCompassHeading()));
}
else if (action.equals("setTimeout")) {
this.setTimeout(args.getLong(0));
}
else if (action.equals("getTimeout")) {
long l = this.getTimeout();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l));
} else {
// Unsupported action
return false;
}
return true;
}
/**
* Called when listener is to be shut down and object is being destroyed.
*/
public void onDestroy() {
this.stop();
}
/**
* Called when app has navigated and JS listeners have been destroyed.
*/
public void onReset() {
this.stop();
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Start listening for compass sensor.
*
* @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<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
// If found, then register as listener
if (list != null && list.size() > 0) {
this.mSensor = list.get(0);
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
this.lastAccessTime = System.currentTimeMillis();
this.setStatus(CompassListener.STARTING);
}
// If error, then set status to error
else {
this.setStatus(CompassListener.ERROR_FAILED_TO_START);
}
return this.status;
}
/**
* Stop listening to compass sensor.
*/
public void stop() {
if (this.status != CompassListener.STOPPED) {
this.sensorManager.unregisterListener(this);
}
this.setStatus(CompassListener.STOPPED);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
/**
* Called after a delay to time out if the listener has not attached fast enough.
*/
private void timeout() {
if (this.status == CompassListener.STARTING) {
this.setStatus(CompassListener.ERROR_FAILED_TO_START);
if (this.callbackContext != null) {
this.callbackContext.error("Compass listener failed to start.");
}
}
}
/**
* Sensor listener event.
*
* @param SensorEvent event
*/
public void onSensorChanged(SensorEvent event) {
// We only care about the orientation as far as it refers to Magnetic North
float heading = event.values[0];
// Save heading
this.timeStamp = System.currentTimeMillis();
this.heading = heading;
this.setStatus(CompassListener.RUNNING);
// If heading hasn't been read for TIMEOUT time, then turn off compass sensor to save power
if ((this.timeStamp - this.lastAccessTime) > this.TIMEOUT) {
this.stop();
}
}
/**
* Get status of compass sensor.
*
* @return status
*/
public int getStatus() {
return this.status;
}
/**
* Get the most recent compass heading.
*
* @return heading
*/
public float getHeading() {
this.lastAccessTime = System.currentTimeMillis();
return this.heading;
}
/**
* Set the timeout to turn off compass sensor if getHeading() hasn't been called.
*
* @param timeout Timeout in msec.
*/
public void setTimeout(long timeout) {
this.TIMEOUT = timeout;
}
/**
* Get the timeout to turn off compass sensor if getHeading() hasn't been called.
*
* @return timeout in msec
*/
public long getTimeout() {
return this.TIMEOUT;
}
/**
* Set the status and send it to JavaScript.
* @param status
*/
private void setStatus(int status) {
this.status = status;
}
/**
* Create the CompassHeading JSON object to be returned to JavaScript
*
* @return a compass heading
*/
private JSONObject getCompassHeading() throws JSONException {
JSONObject obj = new JSONObject();
obj.put("magneticHeading", this.getHeading());
obj.put("trueHeading", this.getHeading());
// Since the magnetic and true heading are always the same our and accuracy
// is defined as the difference between true and magnetic always return zero
obj.put("headingAccuracy", 0);
obj.put("timestamp", this.timeStamp);
return obj;
}
}

View File

@ -1,198 +0,0 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cordova;
import java.util.HashMap;
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;
/**
* This abstract class defines SDK-independent API for communication with
* Contacts Provider. The actual implementation used by the application depends
* on the level of API available on the device. If the API level is Cupcake or
* Donut, we want to use the {@link ContactAccessorSdk3_4} class. If it is
* Eclair or higher, we want to use {@link ContactAccessorSdk5}.
*/
public abstract class ContactAccessor {
protected final String LOG_TAG = "ContactsAccessor";
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.
* @param key
* @param map created by running buildPopulationSet.
* @return true if the key data is required
*/
protected boolean isRequired(String key, HashMap<String,Boolean> 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<String,Boolean> buildPopulationSet(JSONArray fields) {
HashMap<String,Boolean> map = new HashMap<String,Boolean>();
String key;
try {
if (fields.length() == 1 && fields.getString(0).equals("*")) {
map.put("displayName", true);
map.put("name", true);
map.put("nickname", true);
map.put("phoneNumbers", true);
map.put("emails", true);
map.put("addresses", true);
map.put("ims", true);
map.put("organizations", true);
map.put("birthday", true);
map.put("note", true);
map.put("urls", true);
map.put("photos", true);
map.put("categories", true);
}
else {
for (int i=0; i<fields.length(); i++) {
key = fields.getString(i);
if (key.startsWith("displayName")) {
map.put("displayName", true);
}
else if (key.startsWith("name")) {
map.put("displayName", true);
map.put("name", true);
}
else if (key.startsWith("nickname")) {
map.put("nickname", true);
}
else if (key.startsWith("phoneNumbers")) {
map.put("phoneNumbers", true);
}
else if (key.startsWith("emails")) {
map.put("emails", true);
}
else if (key.startsWith("addresses")) {
map.put("addresses", true);
}
else if (key.startsWith("ims")) {
map.put("ims", true);
}
else if (key.startsWith("organizations")) {
map.put("organizations", true);
}
else if (key.startsWith("birthday")) {
map.put("birthday", true);
}
else if (key.startsWith("note")) {
map.put("note", true);
}
else if (key.startsWith("urls")) {
map.put("urls", true);
}
else if (key.startsWith("photos")) {
map.put("photos", true);
}
else if (key.startsWith("categories")) {
map.put("categories", true);
}
}
}
}
catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return map;
}
/**
* Convenience method to get a string from a JSON object. Saves a
* lot of try/catch writing.
* If the property is not found in the object null will be returned.
*
* @param obj contact object to search
* @param property to be looked up
* @return The value of the property
*/
protected String getJsonString(JSONObject obj, String property) {
String value = null;
try {
if (obj != null) {
value = obj.getString(property);
if (value.equals("null")) {
Log.d(LOG_TAG, property + " is string called 'null'");
value = null;
}
}
}
catch (JSONException e) {
Log.d(LOG_TAG, "Could not get = " + e.getMessage());
}
return value;
}
/**
* Handles adding a JSON Contact object into the database.
* @return TODO
*/
public abstract String save(JSONObject contact);
/**
* Handles searching through SDK-specific contacts API.
*/
public abstract JSONArray search(JSONArray filter, JSONObject options);
/**
* Handles searching through SDK-specific contacts API.
* @throws JSONException
*/
public abstract JSONObject getContactById(String id) throws JSONException;
/**
* Handles removing a contact from the database.
*/
public abstract boolean remove(String id);
/**
* A class that represents the where clause to be used in the database query
*/
class WhereOptions {
private String where;
private String[] whereArgs;
public void setWhere(String where) {
this.where = where;
}
public String getWhere() {
return where;
}
public void setWhereArgs(String[] whereArgs) {
this.whereArgs = whereArgs;
}
public String[] getWhereArgs() {
return whereArgs;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,122 +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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
public class ContactManager extends CordovaPlugin {
private ContactAccessor contactAccessor;
private static final String LOG_TAG = "Contact Query";
public static final int UNKNOWN_ERROR = 0;
public static final int INVALID_ARGUMENT_ERROR = 1;
public static final int TIMEOUT_ERROR = 2;
public static final int PENDING_OPERATION_ERROR = 3;
public static final int IO_ERROR = 4;
public static final int NOT_SUPPORTED_ERROR = 5;
public static final int PERMISSION_DENIED_ERROR = 20;
/**
* Constructor.
*/
public ContactManager() {
}
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArray of arguments for the plugin.
* @param callbackContext The callback context used when calling back into JavaScript.
* @return True if the action was valid, false otherwise.
*/
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
/**
* Check to see if we are on an Android 1.X device. If we are return an error as we
* do not support this as of Cordova 1.0.
*/
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ContactManager.NOT_SUPPORTED_ERROR));
return true;
}
/**
* Only create the contactAccessor after we check the Android version or the program will crash
* older phones.
*/
if (this.contactAccessor == null) {
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.cordova);
}
if (action.equals("search")) {
final JSONArray filter = args.getJSONArray(0);
final JSONObject options = args.getJSONObject(1);
this.cordova.getThreadPool().execute(new Runnable() {
public void run() {
JSONArray res = contactAccessor.search(filter, options);
callbackContext.success(res);
}
});
}
else if (action.equals("save")) {
final JSONObject contact = args.getJSONObject(0);
this.cordova.getThreadPool().execute(new Runnable() {
public void run() {
JSONObject res = null;
String id = contactAccessor.save(contact);
if (id != null) {
try {
res = contactAccessor.getContactById(id);
} catch (JSONException e) {
Log.e(LOG_TAG, "JSON fail.", e);
}
}
if (res != null) {
callbackContext.success(res);
} else {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
}
}
});
}
else if (action.equals("remove")) {
final String contactId = args.getString(0);
this.cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (contactAccessor.remove(contactId)) {
callbackContext.success();
} else {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
}
}
});
}
else {
return false;
}
return true;
}
}

View File

@ -1,48 +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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.json.JSONException;
public class Echo extends CordovaPlugin {
@Override
public boolean execute(String action, CordovaArgs args, final CallbackContext callbackContext) throws JSONException {
if ("echo".equals(action)) {
final String result = args.isNull(0) ? null : args.getString(0);
callbackContext.success(result);
return true;
} else if ("echoAsync".equals(action)) {
final String result = args.isNull(0) ? null : args.getString(0);
cordova.getThreadPool().execute(new Runnable() {
public void run() {
callbackContext.success(result);
}
});
return true;
} else if ("echoArrayBuffer".equals(action)) {
final byte[] result = args.getArrayBuffer(0);
callbackContext.success(result);
return true;
}
return false;
}
}

View File

@ -1,63 +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.json.JSONException;
import org.json.JSONObject;
/**
* Encapsulates in-progress status of uploading or downloading a file to a remote server.
*/
public class FileProgressResult {
private boolean lengthComputable = false; // declares whether total is known
private long loaded = 0; // bytes sent so far
private long total = 0; // bytes total, if known
public boolean getLengthComputable() {
return lengthComputable;
}
public void setLengthComputable(boolean computable) {
this.lengthComputable = computable;
}
public long getLoaded() {
return loaded;
}
public void setLoaded(long bytes) {
this.loaded = bytes;
}
public long getTotal() {
return total;
}
public void setTotal(long bytes) {
this.total = bytes;
}
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{loaded:" + loaded +
",total:" + total +
",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
}
}

View File

@ -1,964 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLDecoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.webkit.CookieManager;
import com.squareup.okhttp.OkHttpClient;
public class FileTransfer extends CordovaPlugin {
private static final String LOG_TAG = "FileTransfer";
private static final String LINE_START = "--";
private static final String LINE_END = "\r\n";
private static final String BOUNDARY = "+++++";
public static int FILE_NOT_FOUND_ERR = 1;
public static int INVALID_URL_ERR = 2;
public static int CONNECTION_ERR = 3;
public static int ABORTED_ERR = 4;
private static HashMap<String, RequestContext> activeRequests = new HashMap<String, RequestContext>();
private static final int MAX_BUFFER_SIZE = 16 * 1024;
private static OkHttpClient httpClient = new OkHttpClient();
private static final class RequestContext {
String source;
String target;
File targetFile;
CallbackContext callbackContext;
InputStream currentInputStream;
OutputStream currentOutputStream;
boolean aborted;
RequestContext(String source, String target, CallbackContext callbackContext) {
this.source = source;
this.target = target;
this.callbackContext = callbackContext;
}
void sendPluginResult(PluginResult pluginResult) {
synchronized (this) {
if (!aborted) {
callbackContext.sendPluginResult(pluginResult);
}
}
}
}
/**
* Adds an interface method to an InputStream to return the number of bytes
* read from the raw stream. This is used to track total progress against
* the HTTP Content-Length header value from the server.
*/
private static abstract class TrackingInputStream extends FilterInputStream {
public TrackingInputStream(final InputStream in) {
super(in);
}
public abstract long getTotalRawBytesRead();
}
private static class ExposedGZIPInputStream extends GZIPInputStream {
public ExposedGZIPInputStream(final InputStream in) throws IOException {
super(in);
}
public Inflater getInflater() {
return inf;
}
}
/**
* Provides raw bytes-read tracking for a GZIP input stream. Reports the
* total number of compressed bytes read from the input, rather than the
* number of uncompressed bytes.
*/
private static class TrackingGZIPInputStream extends TrackingInputStream {
private ExposedGZIPInputStream gzin;
public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException {
super(gzin);
this.gzin = gzin;
}
public long getTotalRawBytesRead() {
return gzin.getInflater().getBytesRead();
}
}
/**
* Provides simple total-bytes-read tracking for an existing InputStream
*/
private static class TrackingHTTPInputStream extends TrackingInputStream {
private long bytesRead = 0;
public TrackingHTTPInputStream(InputStream stream) {
super(stream);
}
private int updateBytesRead(int newBytesRead) {
if (newBytesRead != -1) {
bytesRead += newBytesRead;
}
return newBytesRead;
}
@Override
public int read() throws IOException {
return updateBytesRead(super.read());
}
@Override
public int read(byte[] buffer) throws IOException {
return updateBytesRead(super.read(buffer));
}
@Override
public int read(byte[] bytes, int offset, int count) throws IOException {
return updateBytesRead(super.read(bytes, offset, count));
}
public long getTotalRawBytesRead() {
return bytesRead;
}
}
/**
* Works around a bug on Android 2.3.
* http://code.google.com/p/android/issues/detail?id=14562
*/
private static final class DoneHandlerInputStream extends TrackingHTTPInputStream {
private boolean done;
public DoneHandlerInputStream(InputStream stream) {
super(stream);
}
@Override
public int read() throws IOException {
int result = done ? -1 : super.read();
done = (result == -1);
return result;
}
@Override
public int read(byte[] buffer) throws IOException {
int result = done ? -1 : super.read(buffer);
done = (result == -1);
return result;
}
@Override
public int read(byte[] bytes, int offset, int count) throws IOException {
int result = done ? -1 : super.read(bytes, offset, count);
done = (result == -1);
return result;
}
}
@Override
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
if (action.equals("upload") || action.equals("download")) {
String source = args.getString(0);
String target = args.getString(1);
if (action.equals("upload")) {
try {
source = URLDecoder.decode(source, "UTF-8");
upload(source, target, args, callbackContext);
} catch (UnsupportedEncodingException e) {
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION, "UTF-8 error."));
}
} else {
download(source, target, args, callbackContext);
}
return true;
} else if (action.equals("abort")) {
String objectId = args.getString(0);
abort(objectId);
callbackContext.success();
return true;
}
return false;
}
private static void addHeadersToRequest(URLConnection connection, JSONObject headers) {
try {
for (Iterator<?> iter = headers.keys(); iter.hasNext(); ) {
String headerKey = iter.next().toString();
JSONArray headerValues = headers.optJSONArray(headerKey);
if (headerValues == null) {
headerValues = new JSONArray();
headerValues.put(headers.getString(headerKey));
}
connection.setRequestProperty(headerKey, headerValues.getString(0));
for (int i = 1; i < headerValues.length(); ++i) {
connection.addRequestProperty(headerKey, headerValues.getString(i));
}
}
} catch (JSONException e1) {
// No headers to be manipulated!
}
}
/**
* Uploads the specified file to the server URL provided using an HTTP multipart request.
* @param source Full path of the file on the file system
* @param target URL of the server to receive the file
* @param args JSON Array of args
* @param callbackContext callback id for optional progress reports
*
* args[2] fileKey Name of file request parameter
* args[3] fileName File name to be used on server
* args[4] mimeType Describes file content type
* args[5] params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
private void upload(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
Log.d(LOG_TAG, "upload " + source + " to " + target);
// Setup the options
final String fileKey = getArgument(args, 2, "file");
final String fileName = getArgument(args, 3, "image.jpg");
final String mimeType = getArgument(args, 4, "image/jpeg");
final JSONObject params = args.optJSONObject(5) == null ? new JSONObject() : args.optJSONObject(5);
final boolean trustEveryone = args.optBoolean(6);
// Always use chunked mode unless set to false as per API
final boolean chunkedMode = args.optBoolean(7) || args.isNull(7);
// Look for headers on the params map for backwards compatibility with older Cordova versions.
final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8);
final String objectId = args.getString(9);
final String httpMethod = getArgument(args, 10, "POST");
Log.d(LOG_TAG, "fileKey: " + fileKey);
Log.d(LOG_TAG, "fileName: " + fileName);
Log.d(LOG_TAG, "mimeType: " + mimeType);
Log.d(LOG_TAG, "params: " + params);
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
Log.d(LOG_TAG, "headers: " + headers);
Log.d(LOG_TAG, "objectId: " + objectId);
Log.d(LOG_TAG, "httpMethod: " + httpMethod);
final URL url;
try {
url = new URL(target);
} catch (MalformedURLException e) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
Log.e(LOG_TAG, error.toString(), e);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final boolean useHttps = url.getProtocol().equals("https");
final RequestContext context = new RequestContext(source, target, callbackContext);
synchronized (activeRequests) {
activeRequests.put(objectId, context);
}
cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (context.aborted) {
return;
}
HttpURLConnection conn = null;
HostnameVerifier oldHostnameVerifier = null;
SSLSocketFactory oldSocketFactory = null;
int totalBytes = 0;
int fixedLength = -1;
try {
// Create return object
FileUploadResult result = new FileUploadResult();
FileProgressResult progress = new FileProgressResult();
//------------------ CLIENT REQUEST
// Open a HTTP connection to the URL based on protocol
if (useHttps) {
// Using standard HTTPS connection. Will not allow self signed certificate
if (!trustEveryone) {
conn = (HttpsURLConnection) httpClient.open(url);
}
// Use our HTTPS connection that blindly trusts everyone.
// This should only be used in debug environments
else {
// Setup the HTTPS connection class to trust everyone
HttpsURLConnection https = (HttpsURLConnection) httpClient.open(url);
oldSocketFactory = trustAllHosts(https);
// Save the current hostnameVerifier
oldHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
conn = https;
}
}
// Return a standard HTTP connection
else {
conn = httpClient.open(url);
}
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod(httpMethod);
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
// Set the cookies on the response
String cookie = CookieManager.getInstance().getCookie(target);
if (cookie != null) {
conn.setRequestProperty("Cookie", cookie);
}
// Handle the other headers
if (headers != null) {
addHeadersToRequest(conn, headers);
}
/*
* Store the non-file portions of the multipart data as a string, so that we can add it
* to the contentSize, since it is part of the body of the HTTP request.
*/
StringBuilder beforeData = new StringBuilder();
try {
for (Iterator<?> iter = params.keys(); iter.hasNext();) {
Object key = iter.next();
if(!String.valueOf(key).equals("headers"))
{
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
beforeData.append("Content-Disposition: form-data; name=\"").append(key.toString()).append('"');
beforeData.append(LINE_END).append(LINE_END);
beforeData.append(params.getString(key.toString()));
beforeData.append(LINE_END);
}
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
beforeData.append(LINE_START).append(BOUNDARY).append(LINE_END);
beforeData.append("Content-Disposition: form-data; name=\"").append(fileKey).append("\";");
beforeData.append(" filename=\"").append(fileName).append('"').append(LINE_END);
beforeData.append("Content-Type: ").append(mimeType).append(LINE_END).append(LINE_END);
byte[] beforeDataBytes = beforeData.toString().getBytes("UTF-8");
byte[] tailParamsBytes = (LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END).getBytes("UTF-8");
// Get a input stream of the file on the phone
InputStream sourceInputStream = getPathFromUri(source);
int stringLength = beforeDataBytes.length + tailParamsBytes.length;
if (sourceInputStream instanceof FileInputStream) {
fixedLength = (int) ((FileInputStream)sourceInputStream).getChannel().size() + stringLength;
progress.setLengthComputable(true);
progress.setTotal(fixedLength);
}
Log.d(LOG_TAG, "Content Length: " + fixedLength);
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
// http://code.google.com/p/android/issues/detail?id=3164
// It also causes OOM if HTTPS is used, even on newer devices.
boolean useChunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
useChunkedMode = useChunkedMode || (fixedLength == -1);
if (useChunkedMode) {
conn.setChunkedStreamingMode(MAX_BUFFER_SIZE);
// Although setChunkedStreamingMode sets this header, setting it explicitly here works
// around an OutOfMemoryException when using https.
conn.setRequestProperty("Transfer-Encoding", "chunked");
} else {
conn.setFixedLengthStreamingMode(fixedLength);
}
conn.connect();
OutputStream sendStream = null;
try {
sendStream = conn.getOutputStream();
synchronized (context) {
if (context.aborted) {
return;
}
context.currentOutputStream = sendStream;
}
//We don't want to change encoding, we just want this to write for all Unicode.
sendStream.write(beforeDataBytes);
totalBytes += beforeDataBytes.length;
// create a buffer of maximum size
int bytesAvailable = sourceInputStream.available();
int bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
byte[] buffer = new byte[bufferSize];
// read file and write it into form...
int bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
long prevBytesRead = 0;
while (bytesRead > 0) {
result.setBytesSent(totalBytes);
sendStream.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
if (totalBytes > prevBytesRead + 102400) {
prevBytesRead = totalBytes;
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
}
bytesAvailable = sourceInputStream.available();
bufferSize = Math.min(bytesAvailable, MAX_BUFFER_SIZE);
bytesRead = sourceInputStream.read(buffer, 0, bufferSize);
// Send a progress event.
progress.setLoaded(totalBytes);
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
progressResult.setKeepCallback(true);
context.sendPluginResult(progressResult);
}
// send multipart form data necessary after file data...
sendStream.write(tailParamsBytes);
totalBytes += tailParamsBytes.length;
sendStream.flush();
} finally {
safeClose(sourceInputStream);
safeClose(sendStream);
}
context.currentOutputStream = null;
Log.d(LOG_TAG, "Sent " + totalBytes + " of " + fixedLength);
//------------------ read the SERVER RESPONSE
String responseString;
int responseCode = conn.getResponseCode();
Log.d(LOG_TAG, "response code: " + responseCode);
Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields());
TrackingInputStream inStream = null;
try {
inStream = getInputStream(conn);
synchronized (context) {
if (context.aborted) {
return;
}
context.currentInputStream = inStream;
}
ByteArrayOutputStream out = new ByteArrayOutputStream(Math.max(1024, conn.getContentLength()));
byte[] buffer = new byte[1024];
int bytesRead = 0;
// write bytes to file
while ((bytesRead = inStream.read(buffer)) > 0) {
out.write(buffer, 0, bytesRead);
}
responseString = out.toString("UTF-8");
} finally {
context.currentInputStream = null;
safeClose(inStream);
}
Log.d(LOG_TAG, "got response from server");
Log.d(LOG_TAG, responseString.substring(0, Math.min(256, responseString.length())));
// send request and retrieve response
result.setResponseCode(responseCode);
result.setResponse(responseString);
context.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} catch (IOException e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), e);
Log.e(LOG_TAG, "Failed after uploading " + totalBytes + " of " + fixedLength + " bytes.");
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
context.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
} catch (Throwable t) {
// Shouldn't happen, but will
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.e(LOG_TAG, error.toString(), t);
context.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
} finally {
synchronized (activeRequests) {
activeRequests.remove(objectId);
}
if (conn != null) {
// Revert back to the proper verifier and socket factories
// Revert back to the proper verifier and socket factories
if (trustEveryone && useHttps) {
HttpsURLConnection https = (HttpsURLConnection) conn;
https.setHostnameVerifier(oldHostnameVerifier);
https.setSSLSocketFactory(oldSocketFactory);
}
}
}
}
});
}
private static void safeClose(Closeable stream) {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
private static TrackingInputStream getInputStream(URLConnection conn) throws IOException {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return new DoneHandlerInputStream(conn.getInputStream());
}
String encoding = conn.getContentEncoding();
if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream()));
}
return new TrackingHTTPInputStream(conn.getInputStream());
}
// always verify the host - don't check for certificate
private static final HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
// Create a trust manager that does not validate certificate chains
private static final TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
/**
* This function will install a trust manager that will blindly trust all SSL
* certificates. The reason this code is being added is to enable developers
* to do development using self signed SSL certificates on their web server.
*
* The standard HttpsURLConnection class will throw an exception on self
* signed certificates if this code is not run.
*/
private static SSLSocketFactory trustAllHosts(HttpsURLConnection connection) {
// Install the all-trusting trust manager
SSLSocketFactory oldFactory = connection.getSSLSocketFactory();
try {
// Install our all trusting manager
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLSocketFactory newFactory = sc.getSocketFactory();
connection.setSSLSocketFactory(newFactory);
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return oldFactory;
}
private static JSONObject createFileTransferError(int errorCode, String source, String target, URLConnection connection) {
int httpStatus = 0;
StringBuilder bodyBuilder = new StringBuilder();
String body = null;
if (connection != null) {
try {
if (connection instanceof HttpURLConnection) {
httpStatus = ((HttpURLConnection)connection).getResponseCode();
InputStream err = ((HttpURLConnection) connection).getErrorStream();
if(err != null)
{
BufferedReader reader = new BufferedReader(new InputStreamReader(err, "UTF-8"));
String line = reader.readLine();
while(line != null)
{
bodyBuilder.append(line);
line = reader.readLine();
if(line != null)
bodyBuilder.append('\n');
}
body = bodyBuilder.toString();
}
}
} catch (IOException e) {
Log.w(LOG_TAG, "Error getting HTTP status code from connection.", e);
}
}
return createFileTransferError(errorCode, source, target, body, httpStatus);
}
/**
* Create an error object based on the passed in errorCode
* @param errorCode the error
* @return JSONObject containing the error
*/
private static JSONObject createFileTransferError(int errorCode, String source, String target, String body, Integer httpStatus) {
JSONObject error = null;
try {
error = new JSONObject();
error.put("code", errorCode);
error.put("source", source);
error.put("target", target);
if(body != null)
{
error.put("body", body);
}
if (httpStatus != null) {
error.put("http_status", httpStatus);
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return error;
}
/**
* Convenience method to read a parameter from the list of JSON args.
* @param args the args passed to the Plugin
* @param position the position to retrieve the arg from
* @param defaultString the default to be used if the arg does not exist
* @return String with the retrieved value
*/
private static String getArgument(JSONArray args, int position, String defaultString) {
String arg = defaultString;
if (args.length() > position) {
arg = args.optString(position);
if (arg == null || "null".equals(arg)) {
arg = defaultString;
}
}
return arg;
}
/**
* Downloads a file form a given URL and saves it to the specified directory.
*
* @param source URL of the server to receive the file
* @param target Full path of the file on the file system
*/
private void download(final String source, final String target, JSONArray args, CallbackContext callbackContext) throws JSONException {
Log.d(LOG_TAG, "download " + source + " to " + target);
final boolean trustEveryone = args.optBoolean(2);
final String objectId = args.getString(3);
final JSONObject headers = args.optJSONObject(4);
final URL url;
try {
url = new URL(source);
} catch (MalformedURLException e) {
JSONObject error = createFileTransferError(INVALID_URL_ERR, source, target, null, 0);
Log.e(LOG_TAG, error.toString(), e);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final boolean useHttps = url.getProtocol().equals("https");
if (!Config.isUrlWhiteListed(source)) {
Log.w(LOG_TAG, "Source URL is not in white list: '" + source + "'");
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, null, 401);
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, error));
return;
}
final RequestContext context = new RequestContext(source, target, callbackContext);
synchronized (activeRequests) {
activeRequests.put(objectId, context);
}
cordova.getThreadPool().execute(new Runnable() {
public void run() {
if (context.aborted) {
return;
}
URLConnection connection = null;
HostnameVerifier oldHostnameVerifier = null;
SSLSocketFactory oldSocketFactory = null;
File file = null;
PluginResult result = null;
try {
file = getFileFromPath(target);
context.targetFile = file;
// create needed directories
file.getParentFile().mkdirs();
// connect to server
// Open a HTTP connection to the URL based on protocol
if (useHttps) {
// Using standard HTTPS connection. Will not allow self signed certificate
if (!trustEveryone) {
connection = (HttpsURLConnection) httpClient.open(url);
}
// Use our HTTPS connection that blindly trusts everyone.
// This should only be used in debug environments
else {
// Setup the HTTPS connection class to trust everyone
HttpsURLConnection https = (HttpsURLConnection) httpClient.open(url);
oldSocketFactory = trustAllHosts(https);
// Save the current hostnameVerifier
oldHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
connection = https;
}
}
// Return a standard HTTP connection
else {
connection = httpClient.open(url);
}
if (connection instanceof HttpURLConnection) {
((HttpURLConnection)connection).setRequestMethod("GET");
}
//Add cookie support
String cookie = CookieManager.getInstance().getCookie(source);
if(cookie != null)
{
connection.setRequestProperty("cookie", cookie);
}
// This must be explicitly set for gzip progress tracking to work.
connection.setRequestProperty("Accept-Encoding", "gzip");
// Handle the other headers
if (headers != null) {
addHeadersToRequest(connection, headers);
}
connection.connect();
Log.d(LOG_TAG, "Download file:" + url);
FileProgressResult progress = new FileProgressResult();
if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) {
// Only trust content-length header if we understand
// the encoding -- identity or gzip
progress.setLengthComputable(true);
progress.setTotal(connection.getContentLength());
}
FileOutputStream outputStream = null;
TrackingInputStream inputStream = null;
try {
inputStream = getInputStream(connection);
outputStream = new FileOutputStream(file);
synchronized (context) {
if (context.aborted) {
return;
}
context.currentInputStream = inputStream;
}
// write bytes to file
byte[] buffer = new byte[MAX_BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, bytesRead);
// Send a progress event.
progress.setLoaded(inputStream.getTotalRawBytesRead());
PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject());
progressResult.setKeepCallback(true);
context.sendPluginResult(progressResult);
}
} finally {
context.currentInputStream = null;
safeClose(inputStream);
safeClose(outputStream);
}
Log.d(LOG_TAG, "Saved file: " + target);
// create FileEntry object
JSONObject fileEntry = FileUtils.getEntry(file);
result = new PluginResult(PluginResult.Status.OK, fileEntry);
} catch (FileNotFoundException e) {
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IOException e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
} catch (Throwable e) {
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, connection);
Log.e(LOG_TAG, error.toString(), e);
result = new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} finally {
synchronized (activeRequests) {
activeRequests.remove(objectId);
}
if (connection != null) {
// Revert back to the proper verifier and socket factories
if (trustEveryone && useHttps) {
HttpsURLConnection https = (HttpsURLConnection) connection;
https.setHostnameVerifier(oldHostnameVerifier);
https.setSSLSocketFactory(oldSocketFactory);
}
}
if (result == null) {
result = new PluginResult(PluginResult.Status.ERROR, createFileTransferError(CONNECTION_ERR, source, target, connection));
}
// Remove incomplete download.
if (result.getStatus() != PluginResult.Status.OK.ordinal() && file != null) {
file.delete();
}
context.sendPluginResult(result);
}
}
});
}
/**
* Get an input stream based on file path or content:// uri
*
* @param path foo
* @return an input stream
* @throws FileNotFoundException
*/
private InputStream getPathFromUri(String path) throws FileNotFoundException {
if (path.startsWith("content:")) {
Uri uri = Uri.parse(path);
return cordova.getActivity().getContentResolver().openInputStream(uri);
}
else if (path.startsWith("file://")) {
int question = path.indexOf("?");
if (question == -1) {
return new FileInputStream(path.substring(7));
} else {
return new FileInputStream(path.substring(7, question));
}
}
else {
return new FileInputStream(path);
}
}
/**
* Get a File object from the passed in path
*
* @param path file path
* @return file object
*/
private File getFileFromPath(String path) throws FileNotFoundException {
File file;
String prefix = "file://";
if (path.startsWith(prefix)) {
file = new File(path.substring(prefix.length()));
} else {
file = new File(path);
}
if (file.getParent() == null) {
throw new FileNotFoundException();
}
return file;
}
/**
* Abort an ongoing upload or download.
*/
private void abort(String objectId) {
final RequestContext context;
synchronized (activeRequests) {
context = activeRequests.remove(objectId);
}
if (context != null) {
File file = context.targetFile;
if (file != null) {
file.delete();
}
// Trigger the abort callback immediately to minimize latency between it and abort() being called.
JSONObject error = createFileTransferError(ABORTED_ERR, context.source, context.target, null, -1);
synchronized (context) {
context.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, error));
context.aborted = true;
}
// Closing the streams can block, so execute on a background thread.
cordova.getThreadPool().execute(new Runnable() {
public void run() {
synchronized (context) {
safeClose(context.currentInputStream);
safeClose(context.currentOutputStream);
}
}
});
}
}
}

View File

@ -1,73 +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.json.JSONException;
import org.json.JSONObject;
/**
* Encapsulates the result and/or status of uploading a file to a remote server.
*/
public class FileUploadResult {
private long bytesSent = 0; // bytes sent
private int responseCode = -1; // HTTP response code
private String response = null; // HTTP response
private String objectId = null; // FileTransfer object id
public long getBytesSent() {
return bytesSent;
}
public void setBytesSent(long bytes) {
this.bytesSent = bytes;
}
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public String getObjectId() {
return objectId;
}
public void setObjectId(String objectId) {
this.objectId = objectId;
}
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{bytesSent:" + bytesSent +
",responseCode:" + responseCode +
",response:" + JSONObject.quote(response) +
",objectId:" + JSONObject.quote(objectId) + "}");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,50 +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 android.location.LocationManager;
/**
* This class handles requests for GPS location services.
*
*/
public class GPSListener extends CordovaLocationListener {
public GPSListener(LocationManager locationManager, GeoBroker m) {
super(locationManager, m, "[Cordova GPSListener]");
}
/**
* Start requesting location updates.
*
* @param interval
*/
@Override
protected void start() {
if (!this.running) {
if (this.locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
this.running = true;
this.locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 60000, 0, this);
} else {
this.fail(CordovaLocationListener.POSITION_UNAVAILABLE, "GPS provider is not available.");
}
}
}
}

View File

@ -1,206 +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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
/*
* This class is the interface to the Geolocation. It's bound to the geo object.
*
* This class only starts and stops various GeoListeners, which consist of a GPS and a Network Listener
*/
public class GeoBroker extends CordovaPlugin {
private GPSListener gpsListener;
private NetworkListener networkListener;
private LocationManager locationManager;
/**
* Constructor.
*/
public GeoBroker() {
}
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackContext The callback id used when calling back into JavaScript.
* @return True if the action was valid, or false if not.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (this.locationManager == null) {
this.locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
this.networkListener = new NetworkListener(this.locationManager, this);
this.gpsListener = new GPSListener(this.locationManager, this);
}
if ( locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ||
locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER )) {
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 (last != null && (System.currentTimeMillis() - last.getTime()) <= maximumAge) {
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
callbackContext.sendPluginResult(result);
} else {
this.getCurrentLocation(callbackContext, enableHighAccuracy, args.optInt(2, 60000));
}
}
else if (action.equals("addWatch")) {
String id = args.getString(0);
boolean enableHighAccuracy = args.getBoolean(1);
this.addWatch(id, callbackContext, enableHighAccuracy);
}
else if (action.equals("clearWatch")) {
String id = args.getString(0);
this.clearWatch(id);
}
else {
return false;
}
} else {
PluginResult.Status status = PluginResult.Status.NO_RESULT;
String message = "Location API is not available for this device.";
PluginResult result = new PluginResult(status, message);
callbackContext.sendPluginResult(result);
}
return true;
}
private void clearWatch(String id) {
this.gpsListener.clearWatch(id);
this.networkListener.clearWatch(id);
}
private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy, int timeout) {
if (enableHighAccuracy) {
this.gpsListener.addCallback(callbackContext, timeout);
} else {
this.networkListener.addCallback(callbackContext, timeout);
}
}
private void addWatch(String timerId, CallbackContext callbackContext, boolean enableHighAccuracy) {
if (enableHighAccuracy) {
this.gpsListener.addWatch(timerId, callbackContext);
} else {
this.networkListener.addWatch(timerId, callbackContext);
}
}
/**
* Called when the activity is to be shut down.
* Stop listener.
*/
public void onDestroy() {
if (this.networkListener != null) {
this.networkListener.destroy();
this.networkListener = null;
}
if (this.gpsListener != null) {
this.gpsListener.destroy();
this.gpsListener = null;
}
}
/**
* Called when the view navigates.
* Stop the listeners.
*/
public void onReset() {
this.onDestroy();
}
public JSONObject returnLocationJSON(Location loc) {
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("velocity", loc.getSpeed());
o.put("timestamp", loc.getTime());
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return o;
}
public void win(Location loc, CallbackContext callbackContext, boolean keepCallback) {
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
result.setKeepCallback(keepCallback);
callbackContext.sendPluginResult(result);
}
/**
* 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, CallbackContext callbackContext, boolean keepCallback) {
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);
}
result.setKeepCallback(keepCallback);
callbackContext.sendPluginResult(result);
}
public boolean isGlobalListener(CordovaLocationListener listener)
{
if (gpsListener != null && networkListener != null)
{
return gpsListener.equals(listener) || networkListener.equals(listener);
}
else
return false;
}
}

View File

@ -1,577 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Currency;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.TargetApi;
import android.text.format.Time;
/**
*
*/
public class Globalization extends CordovaPlugin {
//GlobalizationCommand Plugin Actions
public static final String GETLOCALENAME = "getLocaleName";
public static final String DATETOSTRING = "dateToString";
public static final String STRINGTODATE = "stringToDate";
public static final String GETDATEPATTERN = "getDatePattern";
public static final String GETDATENAMES = "getDateNames";
public static final String ISDAYLIGHTSAVINGSTIME = "isDayLightSavingsTime";
public static final String GETFIRSTDAYOFWEEK = "getFirstDayOfWeek";
public static final String NUMBERTOSTRING = "numberToString";
public static final String STRINGTONUMBER = "stringToNumber";
public static final String GETNUMBERPATTERN = "getNumberPattern";
public static final String GETCURRENCYPATTERN = "getCurrencyPattern";
public static final String GETPREFERREDLANGUAGE = "getPreferredLanguage";
//GlobalizationCommand Option Parameters
public static final String OPTIONS = "options";
public static final String FORMATLENGTH = "formatLength";
//public static final String SHORT = "short"; //default for dateToString format
public static final String MEDIUM = "medium";
public static final String LONG = "long";
public static final String FULL = "full";
public static final String SELECTOR = "selector";
//public static final String DATEANDTIME = "date and time"; //default for dateToString
public static final String DATE = "date";
public static final String TIME = "time";
public static final String DATESTRING = "dateString";
public static final String TYPE = "type";
public static final String ITEM = "item";
public static final String NARROW = "narrow";
public static final String WIDE = "wide";
public static final String MONTHS = "months";
public static final String DAYS = "days";
//public static final String DECMIAL = "wide"; //default for numberToString
public static final String NUMBER = "number";
public static final String NUMBERSTRING = "numberString";
public static final String PERCENT = "percent";
public static final String CURRENCY = "currency";
public static final String CURRENCYCODE = "currencyCode";
@Override
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) {
JSONObject obj = new JSONObject();
try{
if (action.equals(GETLOCALENAME)){
obj = getLocaleName();
}else if (action.equals(GETPREFERREDLANGUAGE)){
obj = getPreferredLanguage();
} else if (action.equalsIgnoreCase(DATETOSTRING)) {
obj = getDateToString(data);
}else if(action.equalsIgnoreCase(STRINGTODATE)){
obj = getStringtoDate(data);
}else if(action.equalsIgnoreCase(GETDATEPATTERN)){
obj = getDatePattern(data);
}else if(action.equalsIgnoreCase(GETDATENAMES)){
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.GINGERBREAD) {
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
} else {
obj = getDateNames(data);
}
}else if(action.equalsIgnoreCase(ISDAYLIGHTSAVINGSTIME)){
obj = getIsDayLightSavingsTime(data);
}else if(action.equalsIgnoreCase(GETFIRSTDAYOFWEEK)){
obj = getFirstDayOfWeek(data);
}else if(action.equalsIgnoreCase(NUMBERTOSTRING)){
obj = getNumberToString(data);
}else if(action.equalsIgnoreCase(STRINGTONUMBER)){
obj = getStringToNumber(data);
}else if(action.equalsIgnoreCase(GETNUMBERPATTERN)){
obj = getNumberPattern(data);
}else if(action.equalsIgnoreCase(GETCURRENCYPATTERN)){
obj = getCurrencyPattern(data);
}else {
return false;
}
callbackContext.success(obj);
}catch (GlobalizationError ge){
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ge.toJson()));
}catch (Exception e){
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
}
return true;
}
/*
* @Description: Returns the string identifier for the client's current locale setting
*
* @Return: JSONObject
* Object.value {String}: The locale identifier
*
* @throws: GlobalizationError.UNKNOWN_ERROR
*/
private JSONObject getLocaleName() throws GlobalizationError{
JSONObject obj = new JSONObject();
try{
obj.put("value",Locale.getDefault().toString());//get the locale from the Android Device
return obj;
}catch(Exception e){
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
}
}
/*
* @Description: Returns the string identifier for the client's current language
*
* @Return: JSONObject
* Object.value {String}: The language identifier
*
* @throws: GlobalizationError.UNKNOWN_ERROR
*/
private JSONObject getPreferredLanguage() throws GlobalizationError {
JSONObject obj = new JSONObject();
try {
obj.put("value", Locale.getDefault().getDisplayLanguage().toString());
return obj;
} catch (Exception e) {
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
}
}
/*
* @Description: Returns a date formatted as a string according to the client's user preferences and
* calendar using the time zone of the client.
*
* @Return: JSONObject
* Object.value {String}: The localized date string
*
* @throws: GlobalizationError.FORMATTING_ERROR
*/
private JSONObject getDateToString(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
try{
Date date = new Date((Long)options.getJSONObject(0).get(DATE));
//get formatting pattern from android device (Will only have device specific formatting for short form of date) or options supplied
JSONObject datePattern = getDatePattern(options);
SimpleDateFormat fmt = new SimpleDateFormat(datePattern.getString("pattern"));
//return formatted date
return obj.put("value",fmt.format(date));
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
}
}
/*
* @Description: Parses a date formatted as a string according to the client's user
* preferences and calendar using the time zone of the client and returns
* the corresponding date object
* @Return: JSONObject
* Object.year {Number}: The four digit year
* Object.month {Number}: The month from (0 - 11)
* Object.day {Number}: The day from (1 - 31)
* Object.hour {Number}: The hour from (0 - 23)
* Object.minute {Number}: The minute from (0 - 59)
* Object.second {Number}: The second from (0 - 59)
* Object.millisecond {Number}: The milliseconds (from 0 - 999), not available on all platforms
*
* @throws: GlobalizationError.PARSING_ERROR
*/
private JSONObject getStringtoDate(JSONArray options)throws GlobalizationError{
JSONObject obj = new JSONObject();
Date date;
try{
//get format pattern from android device (Will only have device specific formatting for short form of date) or options supplied
DateFormat fmt = new SimpleDateFormat(getDatePattern(options).getString("pattern"));
//attempt parsing string based on user preferences
date = fmt.parse(options.getJSONObject(0).get(DATESTRING).toString());
//set Android Time object
Time time = new Time();
time.set(date.getTime());
//return properties;
obj.put("year", time.year);
obj.put("month", time.month);
obj.put("day", time.monthDay);
obj.put("hour", time.hour);
obj.put("minute", time.minute);
obj.put("second", time.second);
obj.put("millisecond", new Long(0));
return obj;
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
}
}
/*
* @Description: Returns a pattern string for formatting and parsing dates according to the client's
* user preferences.
* @Return: JSONObject
*
* Object.pattern {String}: The date and time pattern for formatting and parsing dates.
* The patterns follow Unicode Technical Standard #35
* http://unicode.org/reports/tr35/tr35-4.html
* Object.timezone {String}: The abbreviated name of the time zone on the client
* Object.utc_offset {Number}: The current difference in seconds between the client's
* time zone and coordinated universal time.
* Object.dst_offset {Number}: The current daylight saving time offset in seconds
* between the client's non-daylight saving's time zone
* and the client's daylight saving's time zone.
*
* @throws: GlobalizationError.PATTERN_ERROR
*/
private JSONObject getDatePattern(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
try{
SimpleDateFormat fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getDateFormat(this.cordova.getActivity()); //default user preference for date
SimpleDateFormat fmtTime = (SimpleDateFormat)android.text.format.DateFormat.getTimeFormat(this.cordova.getActivity()); //default user preference for time
String fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern(); //default SHORT date/time format. ex. dd/MM/yyyy h:mm a
//get Date value + options (if available)
if (options.getJSONObject(0).length() > 1){
//options were included
//get formatLength option
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(FORMATLENGTH)){
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(FORMATLENGTH);
if (fmtOpt.equalsIgnoreCase(MEDIUM)){//medium
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getMediumDateFormat(this.cordova.getActivity());
}else if (fmtOpt.equalsIgnoreCase(LONG) || fmtOpt.equalsIgnoreCase(FULL)){ //long/full
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getLongDateFormat(this.cordova.getActivity());
}
}
//return pattern type
fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern();
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(SELECTOR)){
String selOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(SELECTOR);
if (selOpt.equalsIgnoreCase(DATE)){
fmt = fmtDate.toLocalizedPattern();
}else if (selOpt.equalsIgnoreCase(TIME)){
fmt = fmtTime.toLocalizedPattern();
}
}
}
//TimeZone from users device
//TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone(); //substitute method
TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
obj.put("pattern", fmt);
obj.put("timezone", tz.getDisplayName(tz.inDaylightTime(Calendar.getInstance().getTime()),TimeZone.SHORT));
obj.put("utc_offset", tz.getRawOffset()/1000);
obj.put("dst_offset", tz.getDSTSavings()/1000);
return obj;
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
}
}
/*
* @Description: Returns an array of either the names of the months or days of the week
* according to the client's user preferences and calendar
* @Return: JSONObject
* Object.value {Array{String}}: The array of names starting from either
* the first month in the year or the
* first day of the week.
*
* @throws: GlobalizationError.UNKNOWN_ERROR
*/
@TargetApi(9)
private JSONObject getDateNames(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
//String[] value;
JSONArray value = new JSONArray();
List<String> namesList = new ArrayList<String>();
final Map<String,Integer> namesMap; // final needed for sorting with anonymous comparator
try{
int type = 0; //default wide
int item = 0; //default months
//get options if available
if (options.getJSONObject(0).length() > 0){
//get type if available
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
if (t.equalsIgnoreCase(NARROW)){type++;} //DateUtils.LENGTH_MEDIUM
}
//get item if available
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(ITEM)){
String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(ITEM);
if (t.equalsIgnoreCase(DAYS)){item += 10;} //Days of week start at 1
}
}
//determine return value
int method = item + type;
if (method == 1) { //months and narrow
namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
} else if (method == 10) { //days and wide
namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault());
} else if (method == 11) { //days and narrow
namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault());
} else { //default: months and wide
namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
}
// save names as a list
for(String name : namesMap.keySet()) {
namesList.add(name);
}
// sort the list according to values in namesMap
Collections.sort(namesList, new Comparator<String>() {
public int compare(String arg0, String arg1) {
return namesMap.get(arg0).compareTo(namesMap.get(arg1));
}
});
// convert nameList into JSONArray of String objects
for (int i = 0; i < namesList.size(); i ++){
value.put(namesList.get(i));
}
//return array of names
return obj.put("value", value);
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
}
}
/*
* @Description: Returns whether daylight savings time is in effect for a given date using the client's
* time zone and calendar.
* @Return: JSONObject
* Object.dst {Boolean}: The value "true" indicates that daylight savings time is
* in effect for the given date and "false" indicate that it is not. *
*
* @throws: GlobalizationError.UNKNOWN_ERROR
*/
private JSONObject getIsDayLightSavingsTime(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
boolean dst = false;
try{
Date date = new Date((Long)options.getJSONObject(0).get(DATE));
//TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone();
TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
dst = tz.inDaylightTime(date); //get daylight savings data from date object and user timezone settings
return obj.put("dst",dst);
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
}
}
/*
* @Description: Returns the first day of the week according to the client's user preferences and calendar.
* The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
* @Return: JSONObject
* Object.value {Number}: The number of the first day of the week.
*
* @throws: GlobalizationError.UNKNOWN_ERROR
*/
private JSONObject getFirstDayOfWeek(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
try{
int value = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek(); //get first day of week based on user locale settings
return obj.put("value", value);
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
}
}
/*
* @Description: Returns a number formatted as a string according to the client's user preferences.
* @Return: JSONObject
* Object.value {String}: The formatted number string.
*
* @throws: GlobalizationError.FORMATTING_ERROR
*/
private JSONObject getNumberToString(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
String value = "";
try{
DecimalFormat fmt = getNumberFormatInstance(options);//returns Decimal/Currency/Percent instance
value = fmt.format(options.getJSONObject(0).get(NUMBER));
return obj.put("value", value);
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
}
}
/*
* @Description: Parses a number formatted as a string according to the client's user preferences and
* returns the corresponding number.
* @Return: JSONObject
* Object.value {Number}: The parsed number.
*
* @throws: GlobalizationError.PARSING_ERROR
*/
private JSONObject getStringToNumber(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
Number value;
try{
DecimalFormat fmt = getNumberFormatInstance(options); //returns Decimal/Currency/Percent instance
value = fmt.parse((String)options.getJSONObject(0).get(NUMBERSTRING));
return obj.put("value", value);
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
}
}
/*
* @Description: Returns a pattern string for formatting and parsing numbers according to the client's user
* preferences.
* @Return: JSONObject
* Object.pattern {String}: The number pattern for formatting and parsing numbers.
* The patterns follow Unicode Technical Standard #35.
* http://unicode.org/reports/tr35/tr35-4.html
* Object.symbol {String}: The symbol to be used when formatting and parsing
* e.g., percent or currency symbol.
* Object.fraction {Number}: The number of fractional digits to use when parsing and
* formatting numbers.
* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
* Object.positive {String}: The symbol to use for positive numbers when parsing and formatting.
* Object.negative: {String}: The symbol to use for negative numbers when parsing and formatting.
* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
*
* @throws: GlobalizationError.PATTERN_ERROR
*/
private JSONObject getNumberPattern(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
try{
//uses java.text.DecimalFormat to format value
DecimalFormat fmt = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault()); //default format
String symbol = String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator());
//get Date value + options (if available)
if (options.getJSONObject(0).length() > 0){
//options were included
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
if (fmtOpt.equalsIgnoreCase(CURRENCY)){
fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
symbol = fmt.getDecimalFormatSymbols().getCurrencySymbol();
}else if(fmtOpt.equalsIgnoreCase(PERCENT)){
fmt = (DecimalFormat) DecimalFormat.getPercentInstance(Locale.getDefault());
symbol = String.valueOf(fmt.getDecimalFormatSymbols().getPercent());
}
}
}
//return properties
obj.put("pattern", fmt.toPattern());
obj.put("symbol", symbol);
obj.put("fraction", fmt.getMinimumFractionDigits());
obj.put("rounding", new Integer(0));
obj.put("positive", fmt.getPositivePrefix());
obj.put("negative", fmt.getNegativePrefix());
obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
return obj;
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
}
}
/*
* @Description: Returns a pattern string for formatting and parsing currency values according to the client's
* user preferences and ISO 4217 currency code.
* @Return: JSONObject
* Object.pattern {String}: The currency pattern for formatting and parsing currency values.
* The patterns follow Unicode Technical Standard #35
* http://unicode.org/reports/tr35/tr35-4.html
* Object.code {String}: The ISO 4217 currency code for the pattern.
* Object.fraction {Number}: The number of fractional digits to use when parsing and
* formatting currency.
* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
*
* @throws: GlobalizationError.FORMATTING_ERROR
*/
private JSONObject getCurrencyPattern(JSONArray options) throws GlobalizationError{
JSONObject obj = new JSONObject();
try{
//get ISO 4217 currency code
String code = options.getJSONObject(0).getString(CURRENCYCODE);
//uses java.text.DecimalFormat to format value
DecimalFormat fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
//set currency format
Currency currency = Currency.getInstance(code);
fmt.setCurrency(currency);
//return properties
obj.put("pattern", fmt.toPattern());
obj.put("code", currency.getCurrencyCode());
obj.put("fraction", fmt.getMinimumFractionDigits());
obj.put("rounding", new Integer(0));
obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
return obj;
}catch(Exception ge){
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
}
}
/*
* @Description: Parses a JSONArray from user options and returns the correct Instance of Decimal/Percent/Currency.
* @Return: DecimalFormat : The Instance to use.
*
* @throws: JSONException
*/
private DecimalFormat getNumberFormatInstance(JSONArray options) throws JSONException{
DecimalFormat fmt = (DecimalFormat)DecimalFormat.getInstance(Locale.getDefault()); //default format
try{
if (options.getJSONObject(0).length() > 1){
//options were included
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
if (fmtOpt.equalsIgnoreCase(CURRENCY)){
fmt = (DecimalFormat)DecimalFormat.getCurrencyInstance(Locale.getDefault());
}else if(fmtOpt.equalsIgnoreCase(PERCENT)){
fmt = (DecimalFormat)DecimalFormat.getPercentInstance(Locale.getDefault());
}
}
}
}catch (JSONException je){}
return fmt;
}
}

View File

@ -1,108 +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.json.JSONException;
import org.json.JSONObject;
/**
* @description Exception class representing defined Globalization error codes
* @Globalization error codes:
* GlobalizationError.UNKNOWN_ERROR = 0;
* GlobalizationError.FORMATTING_ERROR = 1;
* GlobalizationError.PARSING_ERROR = 2;
* GlobalizationError.PATTERN_ERROR = 3;
*/
public class GlobalizationError extends Exception{
/**
*
*/
private static final long serialVersionUID = 1L;
public static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";
public static final String FORMATTING_ERROR = "FORMATTING_ERROR";
public static final String PARSING_ERROR = "PARSING_ERROR";
public static final String PATTERN_ERROR = "PATTERN_ERROR";
int error = 0; //default unknown error thrown
/**
* Default constructor
*/
public GlobalizationError() {}
/**
* Create an exception returning an error code
*
* @param s
*/
public GlobalizationError(String s) {
if (s.equalsIgnoreCase(FORMATTING_ERROR)){
error = 1;
}else if (s.equalsIgnoreCase(PARSING_ERROR)){
error = 2;
}else if (s.equalsIgnoreCase(PATTERN_ERROR)){
error = 3;
}
}
/**
* get error string based on error code
*
* @param String msg
*/
public String getErrorString(){
String msg = "";
switch (error){
case 0:
msg = UNKNOWN_ERROR;
break;
case 1:
msg = FORMATTING_ERROR;
break;
case 2:
msg = PARSING_ERROR;
break;
case 3:
msg = PATTERN_ERROR;
break;
}
return msg;
}
/**
* get error code
*
* @param String msg
*/
public int getErrorCode(){
return error;
}
/**
* get the json version of this object to return to javascript
* @return
*/
public JSONObject toJson() {
JSONObject obj = new JSONObject();
try {
obj.put("code", getErrorCode());
obj.put("message", getErrorString());
} catch (JSONException e) {
// never happens
}
return obj;
}
}

View File

@ -1,86 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
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();
}
}

View File

@ -1,813 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.util.HashMap;
import java.util.StringTokenizer;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.webkit.WebChromeClient;
import android.webkit.GeolocationPermissions.Callback;
import android.webkit.JsPromptResult;
import android.webkit.WebSettings;
import android.webkit.WebStorage;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@SuppressLint("SetJavaScriptEnabled")
public class InAppBrowser extends CordovaPlugin {
private static final String NULL = "null";
protected static final String LOG_TAG = "InAppBrowser";
private static final String SELF = "_self";
private static final String SYSTEM = "_system";
// private static final String BLANK = "_blank";
private static final String LOCATION = "location";
private static final String EXIT_EVENT = "exit";
private static final String LOAD_START_EVENT = "loadstart";
private static final String LOAD_STOP_EVENT = "loadstop";
private static final String LOAD_ERROR_EVENT = "loaderror";
private static final String CLOSE_BUTTON_CAPTION = "closebuttoncaption";
private long MAX_QUOTA = 100 * 1024 * 1024;
private Dialog dialog;
private WebView inAppWebView;
private EditText edittext;
private boolean showLocationBar = true;
private CallbackContext callbackContext;
private String buttonLabel = "Done";
/**
* 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 boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
try {
if (action.equals("open")) {
this.callbackContext = callbackContext;
String url = args.getString(0);
String target = args.optString(1);
if (target == null || target.equals("") || target.equals(NULL)) {
target = SELF;
}
HashMap<String, Boolean> features = parseFeature(args.optString(2));
Log.d(LOG_TAG, "target = " + target);
url = updateUrl(url);
String result = "";
// SELF
if (SELF.equals(target)) {
Log.d(LOG_TAG, "in self");
// load in webview
if (url.startsWith("file://") || url.startsWith("javascript:")
|| Config.isUrlWhiteListed(url)) {
this.webView.loadUrl(url);
}
//Load the dialer
else if (url.startsWith(WebView.SCHEME_TEL))
{
try {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(url));
this.cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
}
// load in InAppBrowser
else {
result = this.showWebPage(url, features);
}
}
// SYSTEM
else if (SYSTEM.equals(target)) {
Log.d(LOG_TAG, "in system");
result = this.openExternal(url);
}
// BLANK - or anything else
else {
Log.d(LOG_TAG, "in blank");
result = this.showWebPage(url, features);
}
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, result);
pluginResult.setKeepCallback(true);
this.callbackContext.sendPluginResult(pluginResult);
}
else if (action.equals("close")) {
closeDialog();
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK));
}
else if (action.equals("injectScriptCode")) {
String jsWrapper = null;
if (args.getBoolean(1)) {
jsWrapper = String.format("prompt(JSON.stringify([eval(%%s)]), 'gap-iab://%s')", callbackContext.getCallbackId());
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("injectScriptFile")) {
String jsWrapper;
if (args.getBoolean(1)) {
jsWrapper = String.format("(function(d) { var c = d.createElement('script'); c.src = %%s; c.onload = function() { prompt('', 'gap-iab://%s'); }; d.body.appendChild(c); })(document)", callbackContext.getCallbackId());
} else {
jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document)";
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("injectStyleCode")) {
String jsWrapper;
if (args.getBoolean(1)) {
jsWrapper = String.format("(function(d) { var c = d.createElement('style'); c.innerHTML = %%s; d.body.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
} else {
jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document)";
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else if (action.equals("injectStyleFile")) {
String jsWrapper;
if (args.getBoolean(1)) {
jsWrapper = String.format("(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %%s; d.head.appendChild(c); prompt('', 'gap-iab://%s');})(document)", callbackContext.getCallbackId());
} else {
jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document)";
}
injectDeferredObject(args.getString(0), jsWrapper);
}
else {
return false;
}
} catch (JSONException e) {
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
}
return true;
}
/**
* Inject an object (script or style) into the InAppBrowser WebView.
*
* This is a helper method for the inject{Script|Style}{Code|File} API calls, which
* provides a consistent method for injecting JavaScript code into the document.
*
* If a wrapper string is supplied, then the source string will be JSON-encoded (adding
* quotes) and wrapped using string formatting. (The wrapper string should have a single
* '%s' marker)
*
* @param source The source object (filename or script/style text) to inject into
* the document.
* @param jsWrapper A JavaScript string to wrap the source string in, so that the object
* is properly injected, or null if the source string is JavaScript text
* which should be executed directly.
*/
private void injectDeferredObject(String source, String jsWrapper) {
String scriptToInject;
if (jsWrapper != null) {
org.json.JSONArray jsonEsc = new org.json.JSONArray();
jsonEsc.put(source);
String jsonRepr = jsonEsc.toString();
String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1);
scriptToInject = String.format(jsWrapper, jsonSourceString);
} else {
scriptToInject = source;
}
// This action will have the side-effect of blurring the currently focused element
this.inAppWebView.loadUrl("javascript:" + scriptToInject);
}
/**
* Put the list of features into a hash map
*
* @param optString
* @return
*/
private HashMap<String, Boolean> parseFeature(String optString) {
if (optString.equals(NULL)) {
return null;
} else {
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
StringTokenizer features = new StringTokenizer(optString, ",");
StringTokenizer option;
while(features.hasMoreElements()) {
option = new StringTokenizer(features.nextToken(), "=");
if (option.hasMoreElements()) {
String key = option.nextToken();
if (key.equalsIgnoreCase(CLOSE_BUTTON_CAPTION)) {
this.buttonLabel = option.nextToken();
} else {
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
map.put(key, value);
}
}
}
return map;
}
}
/**
* Convert relative URL to full path
*
* @param url
* @return
*/
private String updateUrl(String url) {
Uri newUrl = Uri.parse(url);
if (newUrl.isRelative()) {
url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url;
}
return url;
}
/**
* Display a new browser with the specified URL.
*
* @param url The url to load.
* @param usePhoneGap Load url in PhoneGap webview
* @return "" if ok, or error message.
*/
public String openExternal(String url) {
try {
Intent intent = null;
intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
this.cordova.getActivity().startActivity(intent);
return "";
} catch (android.content.ActivityNotFoundException e) {
Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
return e.toString();
}
}
/**
* Closes the dialog
*/
private void closeDialog() {
try {
this.inAppWebView.loadUrl("about:blank");
JSONObject obj = new JSONObject();
obj.put("type", EXIT_EVENT);
sendUpdate(obj, false);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
if (dialog != null) {
dialog.dismiss();
}
}
/**
* Checks to see if it is possible to go back one page in history, then does so.
*/
private void goBack() {
if (this.inAppWebView.canGoBack()) {
this.inAppWebView.goBack();
}
}
/**
* Checks to see if it is possible to go forward one page in history, then does so.
*/
private void goForward() {
if (this.inAppWebView.canGoForward()) {
this.inAppWebView.goForward();
}
}
/**
* Navigate to the new page
*
* @param url to load
*/
private void navigate(String url) {
InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
if (!url.startsWith("http") && !url.startsWith("file:")) {
this.inAppWebView.loadUrl("http://" + url);
} else {
this.inAppWebView.loadUrl(url);
}
this.inAppWebView.requestFocus();
}
/**
* Should we show the location bar?
*
* @return boolean
*/
private boolean getShowLocationBar() {
return this.showLocationBar;
}
/**
* Display a new browser with the specified URL.
*
* @param url The url to load.
* @param jsonObject
*/
public String showWebPage(final String url, HashMap<String, Boolean> features) {
// Determine if we should hide the location bar.
showLocationBar = true;
if (features != null) {
Boolean show = features.get(LOCATION);
if (show != null) {
showLocationBar = show.booleanValue();
}
}
final CordovaWebView thatWebView = this.webView;
// Create dialog in new thread
Runnable runnable = new Runnable() {
/**
* Convert our DIP units to Pixels
*
* @return int
*/
private int dpToPixels(int dipValue) {
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
(float) dipValue,
cordova.getActivity().getResources().getDisplayMetrics()
);
return value;
}
public void run() {
// Let's create the main dialog
dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setCancelable(true);
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
public void onDismiss(DialogInterface dialog) {
try {
JSONObject obj = new JSONObject();
obj.put("type", EXIT_EVENT);
sendUpdate(obj, false);
} catch (JSONException e) {
Log.d(LOG_TAG, "Should never happen");
}
}
});
// Main container layout
LinearLayout main = new LinearLayout(cordova.getActivity());
main.setOrientation(LinearLayout.VERTICAL);
// Toolbar layout
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
toolbar.setHorizontalGravity(Gravity.LEFT);
toolbar.setVerticalGravity(Gravity.TOP);
// Action Button Container layout
RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());
actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
actionButtonContainer.setId(1);
// Back button
Button back = new Button(cordova.getActivity());
RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
back.setLayoutParams(backLayoutParams);
back.setContentDescription("Back Button");
back.setId(2);
back.setText("<");
back.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goBack();
}
});
// Forward button
Button forward = new Button(cordova.getActivity());
RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
forward.setLayoutParams(forwardLayoutParams);
forward.setContentDescription("Forward Button");
forward.setId(3);
forward.setText(">");
forward.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
goForward();
}
});
// Edit Text Box
edittext = new EditText(cordova.getActivity());
RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
edittext.setLayoutParams(textLayoutParams);
edittext.setId(4);
edittext.setSingleLine(true);
edittext.setText(url);
edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
edittext.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the event is a key-down event on the "enter" button
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
navigate(edittext.getText().toString());
return true;
}
return false;
}
});
// Close button
Button close = new Button(cordova.getActivity());
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
close.setLayoutParams(closeLayoutParams);
forward.setContentDescription("Close Button");
close.setId(5);
close.setText(buttonLabel);
close.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
closeDialog();
}
});
// WebView
inAppWebView = new WebView(cordova.getActivity());
inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
inAppWebView.setWebChromeClient(new InAppChromeClient(thatWebView));
WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
inAppWebView.setWebViewClient(client);
WebSettings settings = inAppWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setJavaScriptCanOpenWindowsAutomatically(true);
settings.setBuiltInZoomControls(true);
/**
* We need to be careful of this line as a future Android release may deprecate it out of existence.
* Can't replace it with the API 8 level call right now as our minimum SDK is 7 until May 2013
*/
// @TODO: replace with settings.setPluginState(android.webkit.WebSettings.PluginState.ON)
settings.setPluginsEnabled(true);
//Toggle whether this is enabled or not!
Bundle appSettings = cordova.getActivity().getIntent().getExtras();
boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true);
if(enableDatabase)
{
String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
settings.setDatabasePath(databasePath);
settings.setDatabaseEnabled(true);
}
settings.setDomStorageEnabled(true);
inAppWebView.loadUrl(url);
inAppWebView.setId(6);
inAppWebView.getSettings().setLoadWithOverviewMode(true);
inAppWebView.getSettings().setUseWideViewPort(true);
inAppWebView.requestFocus();
inAppWebView.requestFocusFromTouch();
// Add the back and forward buttons to our action button container layout
actionButtonContainer.addView(back);
actionButtonContainer.addView(forward);
// Add the views to our toolbar
toolbar.addView(actionButtonContainer);
toolbar.addView(edittext);
toolbar.addView(close);
// Don't add the toolbar if its been disabled
if (getShowLocationBar()) {
// Add our toolbar to our main view/layout
main.addView(toolbar);
}
// Add our webview to our main view/layout
main.addView(inAppWebView);
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(dialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
dialog.setContentView(main);
dialog.show();
dialog.getWindow().setAttributes(lp);
}
};
this.cordova.getActivity().runOnUiThread(runnable);
return "";
}
/**
* Create a new plugin success result and send it back to JavaScript
*
* @param obj a JSONObject contain event payload information
*/
private void sendUpdate(JSONObject obj, boolean keepCallback) {
sendUpdate(obj, keepCallback, PluginResult.Status.OK);
}
/**
* Create a new plugin result and send it back to JavaScript
*
* @param obj a JSONObject contain event payload information
* @param status the status code to return to the JavaScript environment
*/ private void sendUpdate(JSONObject obj, boolean keepCallback, PluginResult.Status status) {
PluginResult result = new PluginResult(status, obj);
result.setKeepCallback(keepCallback);
this.callbackContext.sendPluginResult(result);
}
public class InAppChromeClient extends WebChromeClient {
private CordovaWebView webView;
public InAppChromeClient(CordovaWebView webView) {
super();
this.webView = webView;
}
/**
* Handle database quota exceeded notification.
*
* @param url
* @param databaseIdentifier
* @param currentQuota
* @param estimatedSize
* @param totalUsedQuota
* @param quotaUpdater
*/
@Override
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
{
LOG.d(LOG_TAG, "onExceededDatabaseQuota estimatedSize: %d currentQuota: %d totalUsedQuota: %d", estimatedSize, currentQuota, totalUsedQuota);
if (estimatedSize < MAX_QUOTA)
{
//increase for 1Mb
long newQuota = estimatedSize;
LOG.d(LOG_TAG, "calling quotaUpdater.updateQuota newQuota: %d", newQuota);
quotaUpdater.updateQuota(newQuota);
}
else
{
// Set the quota to whatever it is and force an error
// TODO: get docs on how to handle this properly
quotaUpdater.updateQuota(currentQuota);
}
}
/**
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
*
* @param origin
* @param callback
*/
@Override
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
super.onGeolocationPermissionsShowPrompt(origin, callback);
callback.invoke(origin, true, false);
}
/**
* Tell the client to display a prompt dialog to the user.
* If the client returns true, WebView will assume that the client will
* handle the prompt dialog and call the appropriate JsPromptResult method.
*
* The prompt bridge provided for the InAppBrowser is capable of executing any
* oustanding callback belonging to the InAppBrowser plugin. Care has been
* taken that other callbacks cannot be triggered, and that no other code
* execution is possible.
*
* To trigger the bridge, the prompt default value should be of the form:
*
* gap-iab://<callbackId>
*
* where <callbackId> is the string id of the callback to trigger (something
* like "InAppBrowser0123456789")
*
* If present, the prompt message is expected to be a JSON-encoded value to
* pass to the callback. A JSON_EXCEPTION is returned if the JSON is invalid.
*
* @param view
* @param url
* @param message
* @param defaultValue
* @param result
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// See if the prompt string uses the 'gap-iab' protocol. If so, the remainder should be the id of a callback to execute.
if (defaultValue != null && defaultValue.startsWith("gap-iab://")) {
PluginResult scriptResult;
String scriptCallbackId = defaultValue.substring(10);
if (scriptCallbackId.startsWith("InAppBrowser")) {
if(message == null || message.length() == 0) {
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray());
} else {
try {
scriptResult = new PluginResult(PluginResult.Status.OK, new JSONArray(message));
} catch(JSONException e) {
scriptResult = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
}
}
this.webView.sendPluginResult(scriptResult, scriptCallbackId);
result.confirm("");
return true;
}
}
return false;
}
}
/**
* The webview client receives notifications about appView
*/
public class InAppBrowserClient extends WebViewClient {
EditText edittext;
CordovaWebView webView;
/**
* Constructor.
*
* @param mContext
* @param edittext
*/
public InAppBrowserClient(CordovaWebView webView, EditText mEditText) {
this.webView = webView;
this.edittext = mEditText;
}
/**
* Notify the host application that a page has started loading.
*
* @param view The webview initiating the callback.
* @param url The url of the page.
*/
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String newloc = "";
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
newloc = 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));
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error dialing " + url + ": " + e.toString());
}
}
else if (url.startsWith("geo:") || url.startsWith(WebView.SCHEME_MAILTO) || url.startsWith("market:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error with " + url + ": " + e.toString());
}
}
// If sms:5551212?body=This is the message
else if (url.startsWith("sms:")) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
// Get address
String address = null;
int parmIndex = url.indexOf('?');
if (parmIndex == -1) {
address = url.substring(4);
}
else {
address = url.substring(4, parmIndex);
// If body, then set sms body
Uri uri = Uri.parse(url);
String query = uri.getQuery();
if (query != null) {
if (query.startsWith("body=")) {
intent.putExtra("sms_body", query.substring(5));
}
}
}
intent.setData(Uri.parse("sms:" + address));
intent.putExtra("address", address);
intent.setType("vnd.android-dir/mms-sms");
cordova.getActivity().startActivity(intent);
} catch (android.content.ActivityNotFoundException e) {
LOG.e(LOG_TAG, "Error sending sms " + url + ":" + e.toString());
}
}
else {
newloc = "http://" + url;
}
if (!newloc.equals(edittext.getText().toString())) {
edittext.setText(newloc);
}
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_START_EVENT);
obj.put("url", newloc);
sendUpdate(obj, true);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
}
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_STOP_EVENT);
obj.put("url", url);
sendUpdate(obj, true);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
}
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
try {
JSONObject obj = new JSONObject();
obj.put("type", LOAD_ERROR_EVENT);
obj.put("url", failingUrl);
obj.put("code", errorCode);
obj.put("message", description);
sendUpdate(obj, true, PluginResult.Status.ERROR);
} catch (JSONException ex) {
Log.d(LOG_TAG, "Should never happen");
}
}
}
}

View File

@ -1,32 +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 android.location.LocationManager;
/**
* This class handles requests for GPS location services.
*
*/
public class NetworkListener extends CordovaLocationListener {
public NetworkListener(LocationManager locationManager, GeoBroker m) {
super(locationManager, m, "[Cordova NetworkListener]");
}
}

View File

@ -1,248 +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.CallbackContext;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
public class NetworkManager extends CordovaPlugin {
public static int NOT_REACHABLE = 0;
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
public static final String WIFI = "wifi";
public static final String WIMAX = "wimax";
// mobile
public static final String MOBILE = "mobile";
// 2G network types
public static final String GSM = "gsm";
public static final String GPRS = "gprs";
public static final String EDGE = "edge";
// 3G network types
public static final String CDMA = "cdma";
public static final String UMTS = "umts";
public static final String HSPA = "hspa";
public static final String HSUPA = "hsupa";
public static final String HSDPA = "hsdpa";
public static final String ONEXRTT = "1xrtt";
public static final String EHRPD = "ehrpd";
// 4G network types
public static final String LTE = "lte";
public static final String UMB = "umb";
public static final String HSPA_PLUS = "hspa+";
// return type
public static final String TYPE_UNKNOWN = "unknown";
public static final String TYPE_ETHERNET = "ethernet";
public static final String TYPE_WIFI = "wifi";
public static final String TYPE_2G = "2g";
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 CallbackContext connectionCallbackContext;
private boolean registered = false;
ConnectivityManager sockMan;
BroadcastReceiver receiver;
private String lastStatus = "";
/**
* Constructor.
*/
public NetworkManager() {
this.receiver = null;
}
/**
* Sets the context of the Command. This can then be used to do things like
* get file paths associated with the Activity.
*
* @param cordova The context of the main Activity.
* @param webView The CordovaWebView Cordova is running in.
*/
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
super.initialize(cordova, webView);
this.sockMan = (ConnectivityManager) cordova.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
this.connectionCallbackContext = null;
// We need to listen to connectivity events to update navigator.connection
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
if (this.receiver == null) {
this.receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// (The null check is for the ARM Emulator, please use Intel Emulator for better results)
if(NetworkManager.this.webView != null)
updateConnectionInfo(sockMan.getActiveNetworkInfo());
}
};
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
this.registered = true;
}
}
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackContext The callback id used when calling back into JavaScript.
* @return True if the action was valid, false otherwise.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
if (action.equals("getConnectionInfo")) {
this.connectionCallbackContext = callbackContext;
NetworkInfo info = sockMan.getActiveNetworkInfo();
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
pluginResult.setKeepCallback(true);
callbackContext.sendPluginResult(pluginResult);
return true;
}
return false;
}
/**
* Stop network receiver.
*/
public void onDestroy() {
if (this.receiver != null && this.registered) {
try {
this.cordova.getActivity().unregisterReceiver(this.receiver);
this.registered = false;
} catch (Exception e) {
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
}
}
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Updates the JavaScript side whenever the connection changes
*
* @param info the current active network info
* @return
*/
private void updateConnectionInfo(NetworkInfo info) {
// send update to javascript "navigator.network.connection"
// Jellybean sends its own info
String thisStatus = this.getConnectionInfo(info);
if(!thisStatus.equals(lastStatus))
{
sendUpdate(thisStatus);
lastStatus = thisStatus;
}
}
/**
* Get the latest network connection information
*
* @param info the current active network info
* @return a JSONObject that represents the network info
*/
private String getConnectionInfo(NetworkInfo info) {
String type = TYPE_NONE;
if (info != null) {
// If we are not connected to any network set type to none
if (!info.isConnected()) {
type = TYPE_NONE;
}
else {
type = getType(info);
}
}
Log.d("CordovaNetworkManager", "Connection Type: " + type);
return type;
}
/**
* Create a new plugin result and send it back to JavaScript
*
* @param connection the network info to set as navigator.connection
*/
private void sendUpdate(String type) {
if (connectionCallbackContext != null) {
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
result.setKeepCallback(true);
connectionCallbackContext.sendPluginResult(result);
}
webView.postMessage("networkconnection", type);
}
/**
* Determine the type of connection
*
* @param info the network info so we can determine connection type.
* @return the type of mobile network we are on
*/
private String getType(NetworkInfo info) {
if (info != null) {
String type = info.getTypeName();
if (type.toLowerCase().equals(WIFI)) {
return TYPE_WIFI;
}
else if (type.toLowerCase().equals(MOBILE)) {
type = info.getSubtypeName();
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) ||
type.toLowerCase().equals(ONEXRTT) ||
type.toLowerCase().equals(EHRPD) ||
type.toLowerCase().equals(HSUPA) ||
type.toLowerCase().equals(HSDPA) ||
type.toLowerCase().equals(HSPA)) {
return TYPE_3G;
}
else if (type.toLowerCase().equals(LTE) ||
type.toLowerCase().equals(UMB) ||
type.toLowerCase().equals(HSPA_PLUS)) {
return TYPE_4G;
}
}
}
else {
return TYPE_NONE;
}
return TYPE_UNKNOWN;
}
}

View File

@ -1,448 +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.CallbackContext;
import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.CordovaPlugin;
import org.apache.cordova.api.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Vibrator;
import android.widget.EditText;
/**
* This class provides access to notifications on the device.
*/
public class Notification extends CordovaPlugin {
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 JSONArray of arguments for the plugin.
* @param callbackContext The callback context used when calling back into JavaScript.
* @return True when the action was valid, false otherwise.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
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), callbackContext);
return true;
}
else if (action.equals("confirm")) {
this.confirm(args.getString(0), args.getString(1), args.getJSONArray(2), callbackContext);
return true;
}
else if (action.equals("prompt")) {
this.prompt(args.getString(0), args.getString(1), args.getJSONArray(2), args.getString(3), callbackContext);
return true;
}
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();
}
else {
return false;
}
// Only alert and confirm are async.
callbackContext.success();
return true;
}
//--------------------------------------------------------------------------
// 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.cordova.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) {
// Start the vibration, 0 defaults to half a second.
if (time == 0) {
time = 500;
}
Vibrator vibrator = (Vibrator) this.cordova.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 callbackContext The callback context
*/
public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) {
final CordovaInterface cordova = this.cordova;
Runnable runnable = new Runnable() {
public void run() {
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
dlg.setMessage(message);
dlg.setTitle(title);
dlg.setCancelable(true);
dlg.setPositiveButton(buttonLabel,
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
}
});
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
public void onCancel(DialogInterface dialog)
{
dialog.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
}
});
dlg.create();
dlg.show();
};
};
this.cordova.getActivity().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 callbackContext The callback context.
*/
public synchronized void confirm(final String message, final String title, final JSONArray buttonLabels, final CallbackContext callbackContext) {
final CordovaInterface cordova = this.cordova;
Runnable runnable = new Runnable() {
public void run() {
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
dlg.setMessage(message);
dlg.setTitle(title);
dlg.setCancelable(true);
// First button
if (buttonLabels.length() > 0) {
try {
dlg.setNegativeButton(buttonLabels.getString(0),
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1));
}
});
} catch (JSONException e) { }
}
// Second button
if (buttonLabels.length() > 1) {
try {
dlg.setNeutralButton(buttonLabels.getString(1),
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2));
}
});
} catch (JSONException e) { }
}
// Third button
if (buttonLabels.length() > 2) {
try {
dlg.setPositiveButton(buttonLabels.getString(2),
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3));
}
});
} catch (JSONException e) { }
}
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
public void onCancel(DialogInterface dialog)
{
dialog.dismiss();
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
}
});
dlg.create();
dlg.show();
};
};
this.cordova.getActivity().runOnUiThread(runnable);
}
/**
* Builds and shows a native Android prompt dialog with given title, message, buttons.
* This dialog only shows up to 3 buttons. Any labels after that will be ignored.
* The following results are returned to the JavaScript callback identified by callbackId:
* buttonIndex Index number of the button selected
* input1 The text entered in the prompt dialog box
*
* @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 callbackContext The callback context.
*/
public synchronized void prompt(final String message, final String title, final JSONArray buttonLabels, final String defaultText, final CallbackContext callbackContext) {
final CordovaInterface cordova = this.cordova;
final EditText promptInput = new EditText(cordova.getActivity());
promptInput.setHint(defaultText);
Runnable runnable = new Runnable() {
public void run() {
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
dlg.setMessage(message);
dlg.setTitle(title);
dlg.setCancelable(true);
dlg.setView(promptInput);
final JSONObject result = new JSONObject();
// First button
if (buttonLabels.length() > 0) {
try {
dlg.setNegativeButton(buttonLabels.getString(0),
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
try {
result.put("buttonIndex",1);
result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
} catch (JSONException e) { e.printStackTrace(); }
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
}
});
} catch (JSONException e) { }
}
// Second button
if (buttonLabels.length() > 1) {
try {
dlg.setNeutralButton(buttonLabels.getString(1),
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
try {
result.put("buttonIndex",2);
result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
} catch (JSONException e) { e.printStackTrace(); }
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
}
});
} catch (JSONException e) { }
}
// Third button
if (buttonLabels.length() > 2) {
try {
dlg.setPositiveButton(buttonLabels.getString(2),
new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
try {
result.put("buttonIndex",3);
result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
} catch (JSONException e) { e.printStackTrace(); }
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
}
});
} catch (JSONException e) { }
}
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
public void onCancel(DialogInterface dialog){
dialog.dismiss();
try {
result.put("buttonIndex",0);
result.put("input1", promptInput.getText().toString().trim().length()==0 ? defaultText : promptInput.getText());
} catch (JSONException e) { e.printStackTrace(); }
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result));
}
});
dlg.create();
dlg.show();
};
};
this.cordova.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 CordovaInterface cordova = this.cordova;
Runnable runnable = new Runnable() {
public void run() {
Notification.this.spinnerDialog = ProgressDialog.show(cordova.getActivity(), title, message, true, true,
new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
Notification.this.spinnerDialog = null;
}
});
}
};
this.cordova.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 cordova = this.cordova;
Runnable runnable = new Runnable() {
public void run() {
notification.progressDialog = new ProgressDialog(cordova.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.cordova.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;
}
}
}

View File

@ -1,43 +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.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.json.JSONArray;
public class SplashScreen extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
if (action.equals("hide")) {
this.webView.postMessage("splashscreen", "hide");
} else if (action.equals("show")){
this.webView.postMessage("splashscreen", "show");
}
else {
return false;
}
callbackContext.success();
return true;
}
}

View File

@ -1,244 +0,0 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package org.apache.cordova;
import java.io.File;
import org.apache.cordova.api.CallbackContext;
import org.apache.cordova.api.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.*;
/**
* This class implements the HTML5 database support to work around a bug for
* Android 3.0 devices. It is not used for other versions of Android, since
* HTML5 database is built in to the browser.
*/
public class Storage extends CordovaPlugin {
// 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
/**
* Constructor.
*/
public Storage() {
}
/**
* Executes the request and returns PluginResult.
*
* @param action
* The action to execute.
* @param args
* JSONArry of arguments for the plugin.
* @param callbackContext
* The callback context used when calling back into JavaScript.
* @return True if the action was valid, false otherwise.
*/
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
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));
}
else {
return false;
}
callbackContext.success();
return true;
}
/**
* Clean up and close database.
*/
@Override
public void onDestroy() {
if (this.myDb != null) {
this.myDb.close();
this.myDb = null;
}
}
/**
* Clean up on navigation/refresh.
*/
public void onReset() {
this.onDestroy();
}
// --------------------------------------------------------------------------
// LOCAL METHODS
// --------------------------------------------------------------------------
/**
* 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 database is open, then close it
if (this.myDb != null) {
this.myDb.close();
}
// If no database path, generate from application package
if (this.path == null) {
this.path = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
}
this.dbName = this.path + File.separator + db + ".db";
/*
* What is all this nonsense? Well the separator was incorrect so the db was showing up in the wrong
* directory. This bit of code fixes that issue and moves the db to the correct directory.
*/
File oldDbFile = new File(this.path + File.pathSeparator + db + ".db");
if (oldDbFile.exists()) {
File dbPath = new File(this.path);
File dbFile = new File(dbName);
dbPath.mkdirs();
oldDbFile.renameTo(dbFile);
}
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
}
/**
* 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.webView.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.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').failQuery('" + ex.getMessage() + "','" + tx_id + "');");
}
}
/**
* Checks to see the the query is a Data Definition command
*
* @param query to be executed
* @return true if it is a DDL command, false otherwise
*/
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;
}
/**
* Process query results.
*
* @param cur
* Cursor into query results
* @param tx_id
* Transaction id
*/
public void processResults(Cursor cur, String tx_id) {
String result = "[]";
// If query result has rows
if (cur.moveToFirst()) {
JSONArray fullresult = new JSONArray();
String key = "";
String value = "";
int colCount = cur.getColumnCount();
// 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);
} catch (JSONException e) {
e.printStackTrace();
}
} while (cur.moveToNext());
result = fullresult.toString();
}
// Let JavaScript know that there are no more rows
this.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', " + result + ");");
}
}