Compare commits

...

35 Commits

Author SHA1 Message Date
Joe Bowser 9d5fb0b201 Tagging 2.0.0rc1 2012-07-13 15:46:09 -07:00
Fil Maj e0a5fe4002 [CB-574] Added backbutton automated unit test for android. 2012-07-13 14:57:40 -07:00
macdonst f9d9a0a4bd Adding deprecation notice to LegacyContext 2012-07-13 15:36:56 -04:00
Joe Bowser 78f0c7b119 Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-android into audio 2012-07-12 14:41:56 -07:00
Fil Maj c6d8343de2 [CB-1035] Including newest JS built based on refactored common device module. 2012-07-12 13:37:08 -07:00
Anis Kadri 0ccd11e587 CB-1031 android create script fails 2012-07-11 14:00:42 -07:00
Joe Bowser b486711d68 Combining plugins.xml and cordova.xml to make config.xml 2012-07-11 11:23:31 -07:00
Fil Maj 2eb4c5e960 [CB-1022] Reverted nanoTime back to currentTimeMillis. Updated mobile-spec tests as well. This passes all accel tests. 2012-07-11 10:26:14 -07:00
Fil Maj 85aa740c98 [CB-481] Removed todo comment introduced by bryce, clarified what is going on 2012-07-11 09:35:29 -07:00
Joe Bowser 6415848383 Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-android into audio 2012-07-11 09:14:04 -07:00
macdonst beb9460538 CB-952: Android showSplashScreen crashes 2012-07-10 20:52:07 -04:00
Joe Bowser c030770be7 Working with Lorin's change 2012-07-10 14:37:26 -07:00
macdonst 0180342dff CB-993: Android plugin problems upgrading to 1.9.0 2012-07-10 16:26:52 -04:00
macdonst b97748d3dc CB-1005: Can not remove contact phonenumber values 2012-07-10 11:25:20 -04:00
Joe Bowser 9d4977db00 Fixing bug on ICS where the super.onKeyDown wasn't being called 2012-07-09 14:42:29 -07:00
macdonst f095284faa CB-1016: Zero width or height in getPicture throws java.lang.ArithmeticException 2012-07-07 22:19:55 -04:00
macdonst 401c2f42f9 Modify PluginResult(status) so it generates a JSON string that works with JSON.parse() 2012-07-06 17:39:04 -04:00
macdonst eb0348d47c CB-1014: Out of Memory error when getting image from photo library 2012-07-06 12:37:08 -04:00
macdonst 1f46240ba9 CB-999: When getting images from the PHOTOLIBRARY apply the correctOrientation fix 2012-07-05 16:04:47 -04:00
macdonst 14870726e0 CB-1008: Camera with targetHeight, targetWidth loses image aspect ratio 2012-07-05 15:32:55 -04:00
macdonst c7d6a2eecb CB-992: Camera tries to add temp photo to gallery 2012-07-05 14:02:20 -04:00
macdonst ce61eb2174 Implementing CordovaInterface.getContext in test folder classes 2012-07-03 11:36:04 -04:00
macdonst f3df21ef0a Fix mis-spelling in upgrade guide 2012-07-03 10:33:48 -04:00
macdonst 5eb554e008 CB-993: Android plugin problems upgrading to 1.9.0
The DroidGap.getContext() method causes an infinite loop and eventually a stack overflow error.
2012-07-02 16:37:14 -04:00
Lorin Beer 3ea72e5d21 added deleted tempfile setup 2012-06-28 16:17:00 -07:00
Lorin Beer 762854ad7a changed handling of stopRecording to reflect handling of create message 2012-06-28 15:53:47 -07:00
Lorin Beer 10465066ee Merge branches 'master' and 'dev' 2012-06-28 15:37:11 -07:00
Lorin Beer 0cf9f51816 use enums to track internal states instead of int. Fixed 'unknown state' bug with the addition of loading state. Mega commit, lost some history. 2012-06-28 15:36:28 -07:00
Lorin Beer 3d5e2340ca update to use ordinal instead of enum value 2012-06-28 15:29:23 -07:00
Lorin Beer d9e7984279 fixed seek behaviour, but introduces a bunch of new problems 2012-06-18 10:29:56 -07:00
Lorin Beer e5b9900d3b halfway through refactor 2012-06-17 23:59:13 -07:00
Lorin Beer fc3f1431b2 made internal status static variables final as well, specifically so that they can be used in switch statements 2012-06-17 22:56:22 -07:00
Lorin Beer c8bf2f4cb1 removed audio load code from startPlaying to a private function 2012-06-17 22:37:12 -07:00
Lorin Beer d16555ec4b added file requirement to constructor, all references to AudioPlayer constructor had direct access to file, so this caused no other changes 2012-06-17 22:19:33 -07:00
Lorin Beer 3c9415b1c2 added create message handler, updated AudioPlayer constructor usage 2012-06-17 22:18:09 -07:00
29 changed files with 1188 additions and 768 deletions
+1
View File
@@ -2,6 +2,7 @@
default.properties
gen
assets/www/cordova.js
framework/assets/www/.tmp*
local.properties
framework/proguard.cfg
framework/cordova.jar
+1 -1
View File
@@ -1 +1 @@
1.9.0
2.0.0rc1
+17 -4
View File
@@ -63,6 +63,19 @@ function on_error {
[ -d $PROJECT_PATH ] && rm -rf $PROJECT_PATH
}
function replace {
local pattern=$1
local filename=$2
# Mac OS X requires -i argument
if [ $OSTYPE = 'darwin11' ]
then
sed -i '' -e $pattern $filename
elif [ $OSTYPE = 'linux-gnu' ]
then
sed -i -e $pattern $filename
fi
}
# we do not want the script to silently fail
trap on_error ERR
trap on_exit EXIT
@@ -116,12 +129,12 @@ fi
# interpolate the activity name and package
cp $BUILD_PATH/bin/templates/project/Activity.java $ACTIVITY_PATH
sed -i '' -e "s/__ACTIVITY__/${ACTIVITY}/g" $ACTIVITY_PATH
sed -i '' -e "s/__ID__/${PACKAGE}/g" $ACTIVITY_PATH
replace "s/__ACTIVITY__/${ACTIVITY}/g" $ACTIVITY_PATH
replace "s/__ID__/${PACKAGE}/g" $ACTIVITY_PATH
cp $BUILD_PATH/bin/templates/project/AndroidManifest.xml $MANIFEST_PATH
sed -i '' -e "s/__ACTIVITY__/${ACTIVITY}/g" $MANIFEST_PATH
sed -i '' -e "s/__PACKAGE__/${PACKAGE}/g" $MANIFEST_PATH
replace "s/__ACTIVITY__/${ACTIVITY}/g" $MANIFEST_PATH
replace "s/__PACKAGE__/${PACKAGE}/g" $MANIFEST_PATH
# creating cordova folder and copying emulate/debug/log/launch scripts
mkdir $PROJECT_PATH/cordova
+1 -1
View File
@@ -23,7 +23,7 @@
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title">
<script type="text/javascript" charset="utf-8" src="cordova-1.9.0.js"></script>
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0rc1.js"></script>
<script type="text/javascript" charset="utf-8" src="main.js"></script>
</head>
+115 -96
View File
@@ -1,6 +1,6 @@
// commit 5647225e12e18e15aefc33b151430d9a3804d9ea
// commit fd00bff18daf29606d88263f7586f20cf9421861
// File generated at :: Fri Jun 29 2012 12:32:24 GMT-0400 (EDT)
// File generated at :: Fri Jul 13 2012 15:09:46 GMT-0700 (PDT)
/*
Licensed to the Apache Software Foundation (ASF) under one
@@ -713,7 +713,6 @@ channel.create('onDestroy');
// Channels that must fire before "deviceready" is fired.
channel.waitForInitialization('onCordovaReady');
channel.waitForInitialization('onCordovaInfoReady');
channel.waitForInitialization('onCordovaConnectionReady');
module.exports = channel;
@@ -848,6 +847,9 @@ module.exports = {
Coordinates: {
path: 'cordova/plugin/Coordinates'
},
device: {
path: 'cordova/plugin/device'
},
DirectoryEntry: {
path: 'cordova/plugin/DirectoryEntry'
},
@@ -1114,7 +1116,7 @@ module.exports = {
// Let native code know we are all done on the JS side.
// Native code will then un-hide the WebView.
channel.join(function() {
prompt("", "gap_init:");
exec(null, null, "App", "show", []);
}, [channel.onCordovaReady]);
},
objects: {
@@ -1135,9 +1137,6 @@ module.exports = {
}
}
},
device:{
path: "cordova/plugin/android/device"
},
File: { // exists natively on Android WebView, override
path: "cordova/plugin/File"
},
@@ -1152,6 +1151,9 @@ module.exports = {
}
},
merges: {
device: {
path: 'cordova/plugin/android/device'
},
navigator: {
children: {
notification: {
@@ -1284,7 +1286,10 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
popoverOptions = options.popoverOptions;
}
exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType, mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions]);
var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions];
exec(successCallback, errorCallback, "Camera", "takePicture", args);
};
cameraExport.cleanup = function(successCallback, errorCallback) {
@@ -1873,7 +1878,7 @@ var utils = require('cordova/utils'),
* {boolean} isDirectory always true (readonly)
* {DOMString} name of the directory, excluding the path leading to it (readonly)
* {DOMString} fullPath the absolute full path to the directory (readonly)
* {FileSystem} filesystem on which the directory resides (readonly)
* TODO: implement this!!! {FileSystem} filesystem on which the directory resides (readonly)
*/
var DirectoryEntry = function(name, fullPath) {
DirectoryEntry.__super__.constructor.apply(this, [false, true, name, fullPath]);
@@ -2598,13 +2603,12 @@ var DirectoryEntry = require('cordova/plugin/DirectoryEntry');
var FileSystem = function(name, root) {
this.name = name || null;
if (root) {
console.log('root.name ' + name);
console.log('root.root ' + root);
this.root = new DirectoryEntry(root.name, root.fullPath);
}
};
module.exports = FileSystem;
});
// file: lib/common/plugin/FileTransfer.js
@@ -3768,97 +3772,44 @@ module.exports = callback;
define("cordova/plugin/android/device", function(require, exports, module) {
var channel = require('cordova/channel'),
utils = require('cordova/utils'),
exec = require('cordova/exec');
exec = require('cordova/exec'),
app = require('cordova/plugin/android/app');
/**
* This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
* phone, etc.
* @constructor
*/
function Device() {
this.available = false;
this.platform = null;
this.version = null;
this.name = null;
this.uuid = null;
this.cordova = null;
module.exports = {
/*
* DEPRECATED
* This is only for Android.
*
* You must explicitly override the back button.
*/
overrideBackButton:function() {
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
app.overrideBackbutton(true);
},
var me = this;
/*
* DEPRECATED
* This is only for Android.
*
* This resets the back button to the default behaviour
*/
resetBackButton:function() {
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
app.overrideBackbutton(false);
},
channel.onCordovaReady.subscribeOnce(function() {
me.getInfo(function(info) {
me.available = true;
me.platform = info.platform;
me.version = info.version;
me.name = info.name;
me.uuid = info.uuid;
me.cordova = info.cordova;
channel.onCordovaInfoReady.fire();
},function(e) {
me.available = false;
utils.alert("[ERROR] Error initializing Cordova: " + e);
});
});
}
/**
* Get device info
*
* @param {Function} successCallback The function to call when the heading data is available
* @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
*/
Device.prototype.getInfo = function(successCallback, errorCallback) {
// successCallback required
if (typeof successCallback !== "function") {
console.log("Device Error: successCallback is not a function");
return;
/*
* DEPRECATED
* This is only for Android.
*
* This terminates the activity!
*/
exitApp:function() {
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
app.exitApp();
}
// errorCallback optional
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Device Error: errorCallback is not a function");
return;
}
// Get info
exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
};
/*
* DEPRECATED
* This is only for Android.
*
* You must explicitly override the back button.
*/
Device.prototype.overrideBackButton = function() {
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
navigator.app.overrideBackbutton(true);
};
/*
* DEPRECATED
* This is only for Android.
*
* This resets the back button to the default behaviour
*/
Device.prototype.resetBackButton = function() {
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
navigator.app.overrideBackbutton(false);
};
/*
* DEPRECATED
* This is only for Android.
*
* This terminates the activity!
*/
Device.prototype.exitApp = function() {
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
navigator.app.exitApp();
};
module.exports = new Device();
});
// file: lib/android/plugin/android/notification.js
@@ -4844,6 +4795,74 @@ module.exports = contacts;
});
// file: lib/common/plugin/device.js
define("cordova/plugin/device", function(require, exports, module) {
var channel = require('cordova/channel'),
utils = require('cordova/utils'),
exec = require('cordova/exec');
// Tell cordova channel to wait on the CordovaInfoReady event
channel.waitForInitialization('onCordovaInfoReady');
/**
* This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
* phone, etc.
* @constructor
*/
function Device() {
this.available = false;
this.platform = null;
this.version = null;
this.name = null;
this.uuid = null;
this.cordova = null;
var me = this;
channel.onCordovaReady.subscribeOnce(function() {
me.getInfo(function(info) {
me.available = true;
me.platform = info.platform;
me.version = info.version;
me.name = info.name;
me.uuid = info.uuid;
me.cordova = info.cordova;
channel.onCordovaInfoReady.fire();
},function(e) {
me.available = false;
utils.alert("[ERROR] Error initializing Cordova: " + e);
});
});
}
/**
* Get device info
*
* @param {Function} successCallback The function to call when the heading data is available
* @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
*/
Device.prototype.getInfo = function(successCallback, errorCallback) {
// successCallback required
if (typeof successCallback !== "function") {
console.log("Device Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Device Error: errorCallback is not a function");
return;
}
// Get info
exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
};
module.exports = new Device();
});
// file: lib/common/plugin/geolocation.js
define("cordova/plugin/geolocation", function(require, exports, module) {
var utils = require('cordova/utils'),
+1 -1
View File
@@ -19,7 +19,7 @@
<html>
<head>
<title></title>
<script src="cordova-1.9.0.js"></script>
<script src="cordova-2.0.0rc1.js"></script>
</head>
<body>
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<cordova>
<!--
access elements control the Android whitelist.
Domains are assumed blocked unless set otherwise
-->
<access origin="http://127.0.0.1*"/> <!-- allow local pages -->
<!-- <access origin="https://example.com" /> allow any secure requests to example.com -->
<!-- <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www -->
<!-- <access origin=".*"/> Allow all domains, suggested development use only -->
<log level="DEBUG"/>
<preference name="useBrowserHistory" value="false" />
<plugins>
<plugin name="App" value="org.apache.cordova.App"/>
<plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
<plugin name="Device" value="org.apache.cordova.Device"/>
<plugin name="Accelerometer" value="org.apache.cordova.AccelListener"/>
<plugin name="Compass" value="org.apache.cordova.CompassListener"/>
<plugin name="Media" value="org.apache.cordova.AudioHandler"/>
<plugin name="Camera" value="org.apache.cordova.CameraLauncher"/>
<plugin name="Contacts" value="org.apache.cordova.ContactManager"/>
<plugin name="File" value="org.apache.cordova.FileUtils"/>
<plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/>
<plugin name="Notification" value="org.apache.cordova.Notification"/>
<plugin name="Storage" value="org.apache.cordova.Storage"/>
<plugin name="Temperature" value="org.apache.cordova.TempListener"/>
<plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/>
<plugin name="Capture" value="org.apache.cordova.Capture"/>
<plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
</plugins>
</cordova>
@@ -215,7 +215,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
// Save time that event was received
this.timestamp = System.nanoTime();
this.timestamp = System.currentTimeMillis();
this.x = event.values[0];
this.y = event.values[1];
this.z = event.values[2];
+4 -1
View File
@@ -48,7 +48,10 @@ public class App extends Plugin {
if (action.equals("clearCache")) {
this.clearCache();
}
else if (action.equals("show")) { // TODO @bc - Not in master branch. When should this be called?
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");
+15 -10
View File
@@ -96,6 +96,12 @@ public class AudioHandler extends Plugin {
float f = this.getDurationAudio(args.getString(0), args.getString(1));
return new PluginResult(status, f);
}
else if (action.equals("create")) {
String id = args.getString(0);
String src = args.getString(1);
AudioPlayer audio = new AudioPlayer(this, id, src);
this.players.put(id, audio);
}
else if (action.equals("release")) {
boolean b = this.release(args.getString(0));
return new PluginResult(status, b);
@@ -149,7 +155,7 @@ public class AudioHandler extends Plugin {
// Get all audio players and pause them
for (AudioPlayer audio : this.players.values()) {
if (audio.getState() == AudioPlayer.MEDIA_RUNNING) {
if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) {
this.pausedForPhone.add(audio);
audio.pausePlaying();
}
@@ -192,13 +198,12 @@ public class AudioHandler extends Plugin {
* @param file The name of the file
*/
public void startRecordingAudio(String id, String file) {
// If already recording, then just return;
if (this.players.containsKey(id)) {
return;
}
AudioPlayer audio = new AudioPlayer(this, id);
this.players.put(id, audio);
audio.startRecording(file);
AudioPlayer audio = this.players.get(id);
if ( audio == null) {
audio = new AudioPlayer(this, id, file);
this.players.put(id, audio);
}
audio.startRecording(file);
}
/**
@@ -221,7 +226,7 @@ public class AudioHandler extends Plugin {
public void startPlayingAudio(String id, String file) {
AudioPlayer audio = this.players.get(id);
if (audio == null) {
audio = new AudioPlayer(this, id);
audio = new AudioPlayer(this, id, file);
this.players.put(id, audio);
}
audio.startPlaying(file);
@@ -292,7 +297,7 @@ public class AudioHandler extends Plugin {
// If not already open, then open the file
else {
audio = new AudioPlayer(this, id);
audio = new AudioPlayer(this, id, file);
this.players.put(id, audio);
return (audio.getDuration(file));
}
+243 -157
View File
@@ -31,6 +31,8 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.cordova.AudioPlayer.MODE;
/**
* This class implements the audio playback and recording capabilities used by Cordova.
* It is called by the AudioHandler Cordova class.
@@ -41,15 +43,20 @@ import java.io.IOException;
* sdcard: file name is just sound.mp3
*/
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
private static final String LOG_TAG = "AudioPlayer";
// AudioPlayer modes
public enum MODE { NONE, PLAY, RECORD };
// AudioPlayer states
public static int MEDIA_NONE = 0;
public static int MEDIA_STARTING = 1;
public static int MEDIA_RUNNING = 2;
public static int MEDIA_PAUSED = 3;
public static int MEDIA_STOPPED = 4;
public enum STATE { MEDIA_NONE,
MEDIA_LOADING,
MEDIA_STARTING,
MEDIA_RUNNING,
MEDIA_PAUSED,
MEDIA_STOPPED
};
private static final String LOG_TAG = "AudioPlayer";
// AudioPlayer message ids
private static int MEDIA_STATE = 1;
@@ -64,17 +71,20 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
private static int MEDIA_ERR_DECODE = 3;
private static int MEDIA_ERR_NONE_SUPPORTED = 4;
private AudioHandler handler; // The AudioHandler object
private String id; // The id of this player (used to identify Media object in JavaScript)
private int state = MEDIA_NONE; // State of recording or playback
private String audioFile = null; // File name to play or record to
private float duration = -1; // Duration of audio
private 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 MediaRecorder recorder = null; // Audio recording object
private String tempFile = null; // Temporary recording file name
private String audioFile = null; // File name to play or record to
private float duration = -1; // Duration of audio
private MediaRecorder recorder = null; // Audio recording object
private String tempFile = null; // Temporary recording file name
private MediaPlayer mPlayer = null; // Audio player object
private boolean prepareOnly = false;
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.
@@ -82,14 +92,17 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* @param handler The audio handler object
* @param id The id of this audio player
*/
public AudioPlayer(AudioHandler handler, String id) {
public AudioPlayer(AudioHandler handler, String id, String file) {
this.handler = handler;
this.id = id;
this.audioFile = file;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
} else {
this.tempFile = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording.mp3";
}
}
/**
@@ -97,13 +110,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
*/
public void destroy() {
// Stop any play or record
if (this.mPlayer != null) {
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
this.mPlayer.stop();
this.setState(MEDIA_STOPPED);
if (this.player != null) {
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
this.player.stop();
this.setState(STATE.MEDIA_STOPPED);
}
this.mPlayer.release();
this.mPlayer = null;
this.player.release();
this.player = null;
}
if (this.recorder != null) {
this.stopRecording();
@@ -118,31 +131,34 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* @param file The name of the file
*/
public void startRecording(String file) {
if (this.mPlayer != null) {
switch (this.mode) {
case PLAY:
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
}
// Make sure we're not already recording
else if (this.recorder == null) {
this.audioFile = file;
this.recorder = new MediaRecorder();
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
this.recorder.setOutputFile(this.tempFile);
try {
this.recorder.prepare();
this.recorder.start();
this.setState(MEDIA_RUNNING);
return;
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
break;
case NONE:
// Make sure we're not already recording
if (this.recorder == null) {
this.audioFile = file;
this.recorder = new MediaRecorder();
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
this.recorder.setOutputFile(this.tempFile);
try {
this.recorder.prepare();
this.recorder.start();
this.setState(STATE.MEDIA_RUNNING);
return;
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
}
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
}
else {
break;
case RECORD:
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
}
@@ -171,9 +187,9 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
public void stopRecording() {
if (this.recorder != null) {
try{
if (this.state == MEDIA_RUNNING) {
if (this.state == STATE.MEDIA_RUNNING) {
this.recorder.stop();
this.setState(MEDIA_STOPPED);
this.setState(STATE.MEDIA_STOPPED);
}
this.moveFile(this.audioFile);
}
@@ -183,81 +199,22 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
}
}
//==========================================================================
// Playback
//==========================================================================
/**
* Start or resume playing audio file.
*
* @param file The name of the audio file.
*/
public void startPlaying(String file) {
if (this.recorder != null) {
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
// If this is a new request to play audio, or stopped
else if ((this.mPlayer == null) || (this.state == MEDIA_STOPPED)) {
try {
// If stopped, then reset player
if (this.mPlayer != null) {
this.mPlayer.reset();
}
// Otherwise, create a new one
else {
this.mPlayer = new MediaPlayer();
}
this.audioFile = file;
// If streaming file
if (this.isStreaming(file)) {
this.mPlayer.setDataSource(file);
this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
this.setState(MEDIA_STARTING);
this.mPlayer.setOnPreparedListener(this);
this.mPlayer.prepareAsync();
}
// If local file
else {
if (file.startsWith("/android_asset/")) {
String f = file.substring(15);
android.content.res.AssetFileDescriptor fd = this.handler.cordova.getActivity().getAssets().openFd(f);
this.mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
}
else {
File fp = new File(file);
if (fp.exists()) {
FileInputStream fileInputStream = new FileInputStream(file);
this.mPlayer.setDataSource(fileInputStream.getFD());
}
else {
this.mPlayer.setDataSource("/sdcard/" + file);
}
}
this.setState(MEDIA_STARTING);
this.mPlayer.setOnPreparedListener(this);
this.mPlayer.prepare();
// Get duration
this.duration = getDurationInSeconds();
}
} catch (Exception e) {
e.printStackTrace();
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
}
// If we have already have created an audio player
else {
// If player has been paused, then resume playback
if ((this.state == MEDIA_PAUSED) || (this.state == MEDIA_STARTING)) {
this.mPlayer.start();
this.setState(MEDIA_RUNNING);
}
else {
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: " + this.state);
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
}
if (this.readyPlayer(file)) {
this.player.start();
this.setState(STATE.MEDIA_RUNNING);
} else {
//
this.prepareOnly = false;
}
}
@@ -265,11 +222,14 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* Seek or jump to a new time in the track.
*/
public void seekToPlaying(int milliseconds) {
if (this.mPlayer != null) {
this.mPlayer.seekTo(milliseconds);
if (this.readyPlayer(this.audioFile)) {
this.player.seekTo(milliseconds);
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
}
else {
this.seekOnPrepared = milliseconds;
}
}
/**
@@ -278,12 +238,12 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
public void pausePlaying() {
// If playing, then pause
if (this.state == MEDIA_RUNNING) {
this.mPlayer.pause();
this.setState(MEDIA_PAUSED);
if (this.state == STATE.MEDIA_RUNNING) {
this.player.pause();
this.setState(STATE.MEDIA_PAUSED);
}
else {
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state);
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state.ordinal());
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
}
}
@@ -292,12 +252,12 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* Stop playing the audio file.
*/
public void stopPlaying() {
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
this.mPlayer.stop();
this.setState(MEDIA_STOPPED);
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
this.player.stop();
this.setState(STATE.MEDIA_STOPPED);
}
else {
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state);
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state.ordinal());
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
}
}
@@ -305,10 +265,10 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
/**
* Callback to be invoked when playback of a media source has completed.
*
* @param mPlayer The MediaPlayer that reached the end of the file
* @param player The MediaPlayer that reached the end of the file
*/
public void onCompletion(MediaPlayer mPlayer) {
this.setState(MEDIA_STOPPED);
public void onCompletion(MediaPlayer player) {
this.setState(STATE.MEDIA_STOPPED);
}
/**
@@ -317,8 +277,8 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* @return position in msec or -1 if not playing
*/
public long getCurrentPosition() {
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
int curPos = this.mPlayer.getCurrentPosition();
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
int curPos = this.player.getCurrentPosition();
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
return curPos;
}
@@ -359,7 +319,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
}
// If audio file already loaded and started, then return duration
if (this.mPlayer != null) {
if (this.player != null) {
return this.duration;
}
@@ -377,29 +337,28 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
/**
* Callback to be invoked when the media source is ready for playback.
*
* @param mPlayer The MediaPlayer that is ready for playback
* @param player The MediaPlayer that is ready for playback
*/
public void onPrepared(MediaPlayer mPlayer) {
public void onPrepared(MediaPlayer player) {
// Listen for playback completion
this.mPlayer.setOnCompletionListener(this);
this.player.setOnCompletionListener(this);
// If start playing after prepared
if (!this.prepareOnly) {
// Start playing
this.mPlayer.start();
// Set player init flag
this.setState(MEDIA_RUNNING);
this.player.start();
this.setState(STATE.MEDIA_RUNNING);
} else {
this.setState(STATE.MEDIA_STARTING);
}
// Save off duration
this.duration = getDurationInSeconds();
this.prepareOnly = false;
// reset prepare only flag
this.prepareOnly = true;
// seek to any location received while not prepared
this.seekToPlaying(this.seekOnPrepared);
// reset seek location
this.seekOnPrepared = 0;
// Send status notification to JavaScript
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
}
/**
@@ -408,23 +367,23 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* @return length of clip in seconds
*/
private float getDurationInSeconds() {
return (this.mPlayer.getDuration() / 1000.0f);
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 mPlayer the MediaPlayer the error pertains to
* @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 mPlayer, int arg1, int arg2) {
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.mPlayer.stop();
this.mPlayer.release();
this.player.stop();
this.player.release();
// Send error notification to JavaScript
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":" + arg1 + "});");
@@ -436,21 +395,33 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
*
* @param state
*/
private void setState(int state) {
private void setState(STATE state) {
if (this.state != state) {
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state + ");");
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + this.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 behaviour, so no notification
//this.handler.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;
return this.state.ordinal();
}
/**
@@ -459,6 +430,121 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* @param volume
*/
public void setVolume(float volume) {
this.mPlayer.setVolume(volume, 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.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) {
e.printStackTrace();
}
return false;
case MEDIA_LOADING:
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING insntead
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) {
e.printStackTrace();
this.handler.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.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.ctx.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();
}
}
}
@@ -27,7 +27,6 @@ import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.codec.binary.Base64;
//import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.LOG;
import org.apache.cordova.api.Plugin;
import org.apache.cordova.api.PluginResult;
@@ -36,7 +35,6 @@ import org.json.JSONException;
import android.app.Activity;
import android.content.ContentValues;
//import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
@@ -90,6 +88,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
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;
@@ -143,6 +142,15 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
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 comparrisions succeed
if (this.targetWidth < 1) {
this.targetWidth = -1;
}
if (this.targetHeight < 1) {
this.targetHeight = -1;
}
if (srcType == CAMERA) {
this.takePicture(destType, encodingType);
}
@@ -280,6 +288,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
if (resultCode == Activity.RESULT_OK) {
try {
Bitmap bitmap = null;
Uri uri = null;
// If sending base64 image back
if (destType == DATA_URL) {
@@ -295,9 +304,8 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
// If sending filename back
else if (destType == FILE_URI) {
Uri uri;
if (!this.saveToPhotoAlbum) {
uri = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", (new File(FileUtils.stripFileProtocol(this.imageUri.toString()))).getName()));
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
} else {
uri = getUriFromMediaStore();
}
@@ -324,7 +332,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
os.close();
// Restore exif data to file
if (this.encodingType == JPEG) {
String exifPath;
if (this.saveToPhotoAlbum) {
@@ -338,16 +345,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
}
// Send Uri back to JavaScript for viewing image
if (saveToPhotoAlbum) {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
} else {
// If you don't want to save the file to the photo album you need to append a timestamp
// to the returned URI to do some cache busting.
this.success(new PluginResult(PluginResult.Status.OK, uri.toString() + "?" + System.currentTimeMillis()), this.callbackId);
}
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
this.cleanup(FILE_URI, this.imageUri, bitmap);
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
bitmap = null;
} catch (IOException e) {
@@ -378,73 +379,81 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
else {
// This is a special case to just return the path as no scaling,
// rotating or compression needs to be done
if (this.targetHeight == -1 && this.targetWidth == -1 &&
this.mQuality == 100 && destType == FILE_URI && !this.correctOrientation) {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
} else {
// Get the path to the image. Makes loading so much easier.
String imagePath = FileUtils.getRealPathFromURI(uri, this.cordova);
Bitmap bitmap = getScaledBitmap(imagePath);
// Get the path to the image. Makes loading so much easier.
String imagePath = FileUtils.getRealPathFromURI(uri, this.cordova);
Bitmap bitmap = getScaledBitmap(imagePath);
// If sending base64 image back
if (destType == DATA_URL) {
String[] cols = { MediaStore.Images.Media.ORIENTATION };
Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
cols,
null, null, null);
if (cursor != null) {
cursor.moveToPosition(0);
rotate = cursor.getInt(0);
cursor.close();
}
if (rotate != 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotate);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
this.processPicture(bitmap);
}
// If sending filename back
else if (destType == FILE_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";
ExifHelper exif = new ExifHelper();
try {
if (this.encodingType == JPEG) {
exif.createInFile(resizePath);
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 (this.encodingType == JPEG) {
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
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.success(new PluginResult(PluginResult.Status.OK, ("file://" + resizePath + "?" + System.currentTimeMillis())), this.callbackId);
} catch (Exception e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
if (this.correctOrientation) {
String[] cols = { MediaStore.Images.Media.ORIENTATION };
Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
cols, null, null, null);
if (cursor != null) {
cursor.moveToPosition(0);
rotate = cursor.getInt(0);
cursor.close();
}
if (rotate != 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotate);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
}
else {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
// If sending base64 image back
if (destType == DATA_URL) {
this.processPicture(bitmap);
}
// If sending filename back
else if (destType == FILE_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";
ExifHelper exif = new ExifHelper();
try {
if (this.encodingType == JPEG) {
exif.createInFile(resizePath);
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 (this.encodingType == JPEG) {
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
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.success(new PluginResult(PluginResult.Status.OK, ("file://" + resizePath + "?" + System.currentTimeMillis())), this.callbackId);
} catch (Exception e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
}
}
else {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
}
bitmap.recycle();
bitmap = null;
System.gc();
}
bitmap.recycle();
bitmap = null;
System.gc();
}
}
else if (resultCode == Activity.RESULT_CANCELED) {
@@ -533,20 +542,79 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
return BitmapFactory.decodeFile(imagePath);
}
Bitmap unscaledBitmap = decodeFile(imagePath,
this.targetWidth, this.targetHeight);
return Bitmap.createScaledBitmap(unscaledBitmap, this.targetWidth, this.targetHeight, true);
}
public static Bitmap decodeFile(String pathName, int dstWidth, int dstHeight) {
// figure out the original width and height of the image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(pathName, options);
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight);
return BitmapFactory.decodeFile(pathName, options);
}
BitmapFactory.decodeFile(imagePath, options);
// 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.decodeFile(imagePath, options);
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;
@@ -574,8 +642,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
/**
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
* @param newImage
*/
private void cleanup(int imageType, Uri oldImage, Bitmap bitmap) {
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
if (bitmap != null) {
bitmap.recycle();
}
@@ -585,7 +654,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
checkForDuplicateImage(imageType);
// Scan for the gallery to update pic refs in gallery
this.scanForGallery();
if (this.saveToPhotoAlbum && newImage != null) {
this.scanForGallery(newImage);
}
System.gc();
}
@@ -610,7 +681,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
if ((currentNumOfImages - numPics) == diff) {
cursor.moveToLast();
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
if (diff == 2) {
id--;
}
Uri uri = Uri.parse(contentStore + "/" + id);
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
}
@@ -660,18 +734,20 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
}
private void scanForGallery() {
if(this.conn!=null) this.conn.disconnect();
private void scanForGallery(Uri newImage) {
this.scanMe = newImage;
if(this.conn != null) {
this.conn.disconnect();
}
this.conn = new MediaScannerConnection(this.ctx.getActivity().getApplicationContext(), this);
conn.connect();
}
public void onMediaScannerConnected() {
try{
this.conn.scanFile(this.imageUri.toString(), "image/*");
this.conn.scanFile(this.scanMe.toString(), "image/*");
} catch (java.lang.IllegalStateException e){
e.printStackTrace();
LOG.d(LOG_TAG, "Can;t scan file in MediaScanner aftering taking picture");
LOG.e(LOG_TAG, "Can't scan file in MediaScanner aftering taking picture");
}
}
@@ -71,6 +71,7 @@ import android.webkit.WebView;
* social status updates (see {@link android.provider.ContactsContract.StatusUpdates}).
* </ul>
*/
public class ContactAccessorSdk5 extends ContactAccessor {
/**
@@ -128,12 +129,12 @@ public class ContactAccessorSdk5 extends ContactAccessor {
mView = view;
}
/**
* This method takes the fields required and search options in order to produce an
/**
* This method takes the fields required and search options in order to produce an
* array of contacts that matches the criteria provided.
* @param fields an array of items to be used as search criteria
* @param options that can be applied to contact searching
* @return an array of contacts
* @return an array of contacts
*/
@Override
public JSONArray search(JSONArray fields, JSONObject options) {
@@ -203,11 +204,11 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
/**
* A special search that finds one contact by id
*
* A special search that finds one contact by id
*
* @param id contact to find by id
* @return a JSONObject representing the contact
* @throws JSONException
* @throws JSONException
*/
public JSONObject getContactById(String id) throws JSONException {
// Do the id query
@@ -231,9 +232,9 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
}
/**
/**
* Creates an array of contacts from the cursor you pass in
*
*
* @param limit max number of contacts for the array
* @param populate whether or not you should populate a certain value
* @param c the cursor
@@ -269,7 +270,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
oldContactId = contactId;
}
// When the contact ID changes we need to push the Contact object
// When the contact ID changes we need to push the Contact object
// to the array of contacts and create new objects.
if (!oldContactId.equals(contactId)) {
// Populate the Contact object with it's arrays
@@ -355,7 +356,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
Log.e(LOG_TAG, e.getMessage(), e);
}
// Set the old contact ID
// Set the old contact ID
oldContactId = contactId;
}
@@ -405,15 +406,15 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
/**
* Create a new contact using a JSONObject to hold all the data.
* @param contact
* Create a new contact using a JSONObject to hold all the data.
* @param contact
* @param organizations array of organizations
* @param addresses array of addresses
* @param phones array of phones
* @param emails array of emails
* @param ims array of instant messenger addresses
* @param websites array of websites
* @param photos
* @param photos
* @return
*/
private JSONObject populateContact(JSONObject contact, JSONArray organizations,
@@ -447,7 +448,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
return contact;
}
/**
* Take the search criteria passed into the method and create a SQL WHERE clause.
* @param fields the properties to search against
@@ -458,9 +459,9 @@ public class ContactAccessorSdk5 extends ContactAccessor {
ArrayList<String> where = new ArrayList<String>();
ArrayList<String> whereArgs = new ArrayList<String>();
WhereOptions options = new WhereOptions();
/*
* Special case where the user wants all fields returned
*/
@@ -471,7 +472,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
options.setWhereArgs(new String[] { searchTerm });
return options;
} else {
// Get all contacts that match the filter but return all properties
// Get all contacts that match the filter but return all properties
where.add("(" + dbMap.get("displayName") + " LIKE ? )");
whereArgs.add(searchTerm);
where.add("(" + dbMap.get("name") + " LIKE ? AND "
@@ -579,8 +580,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
}
// else if (key.startsWith("birthday")) {
// where.add("(" + dbMap.get(key) + " LIKE ? AND "
// + ContactsContract.Data.MIMETYPE + " = ? )");
// where.add("(" + dbMap.get(key) + " LIKE ? AND "
// + ContactsContract.Data.MIMETYPE + " = ? )");
// }
else if (key.startsWith("note")) {
where.add("(" + dbMap.get(key) + " LIKE ? AND "
@@ -621,7 +622,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* If the user passes in the '*' wildcard character for search then they want all fields for each contact
*
*
* @param fields
* @return true if wildcard search requested, false otherwise
*/
@@ -802,7 +803,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Create a ContactField JSONObject
* @param contactId
* @param contactId
* @return a JSONObject representing a ContactField
*/
private JSONObject photoQuery(Cursor cursor, String contactId) {
@@ -823,7 +824,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
@Override
/**
* This method will save a contact object into the devices contacts database.
*
*
* @param contact the contact to be saved.
* @returns the id if the contact is successfully saved, null otherwise.
*/
@@ -878,7 +879,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Creates a new contact and stores it in the database
*
*
* @param id the raw contact id which is required for linking items to the contact
* @param contact the contact to be saved
* @param account the account to be saved under
@@ -944,29 +945,40 @@ public class ContactAccessorSdk5 extends ContactAccessor {
try {
phones = contact.getJSONArray("phoneNumbers");
if (phones != null) {
for (int i = 0; i < phones.length(); i++) {
JSONObject phone = (JSONObject) phones.get(i);
String phoneId = getJsonString(phone, "id");
// This is a new phone so do a DB insert
if (phoneId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")));
// Delete all the phones
if (phones.length() == 0) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a phone
else {
for (int i = 0; i < phones.length(); i++) {
JSONObject phone = (JSONObject) phones.get(i);
String phoneId = getJsonString(phone, "id");
// This is a new phone so do a DB insert
if (phoneId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing phone so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Phone._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { phoneId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"))
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")))
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing phone so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Phone._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { phoneId, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, getJsonString(phone, "value"))
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, getPhoneType(getJsonString(phone, "type")))
.build());
}
}
}
}
@@ -979,29 +991,40 @@ public class ContactAccessorSdk5 extends ContactAccessor {
try {
emails = contact.getJSONArray("emails");
if (emails != null) {
for (int i = 0; i < emails.length(); i++) {
JSONObject email = (JSONObject) emails.get(i);
String emailId = getJsonString(email, "id");
// This is a new email so do a DB insert
if (emailId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")));
// Delete all the emails
if (emails.length() == 0) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a email
else {
for (int i = 0; i < emails.length(); i++) {
JSONObject email = (JSONObject) emails.get(i);
String emailId = getJsonString(email, "id");
// This is a new email so do a DB insert
if (emailId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing email so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Email._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { emailId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"))
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")))
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing email so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Email._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { emailId, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"))
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")))
.build());
}
}
}
}
@@ -1014,39 +1037,50 @@ public class ContactAccessorSdk5 extends ContactAccessor {
try {
addresses = contact.getJSONArray("addresses");
if (addresses != null) {
for (int i = 0; i < addresses.length(); i++) {
JSONObject address = (JSONObject) addresses.get(i);
String addressId = getJsonString(address, "id");
// This is a new address so do a DB insert
if (addressId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"));
// Delete all the addresses
if (addresses.length() == 0) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a address
else {
for (int i = 0; i < addresses.length(); i++) {
JSONObject address = (JSONObject) addresses.get(i);
String addressId = getJsonString(address, "id");
// This is a new address so do a DB insert
if (addressId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"));
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing address so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.StructuredPostal._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { addressId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"))
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing address so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.StructuredPostal._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { addressId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.REGION, getJsonString(address, "region"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE, getJsonString(address, "postalCode"))
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, getJsonString(address, "country"))
.build());
}
}
}
}
@@ -1059,33 +1093,44 @@ public class ContactAccessorSdk5 extends ContactAccessor {
try {
organizations = contact.getJSONArray("organizations");
if (organizations != null) {
for (int i = 0; i < organizations.length(); i++) {
JSONObject org = (JSONObject) organizations.get(i);
String orgId = getJsonString(org, "id");
// This is a new organization so do a DB insert
if (orgId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")));
contentValues.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"));
contentValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"));
contentValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"));
// Delete all the organizations
if (organizations.length() == 0) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a organization
else {
for (int i = 0; i < organizations.length(); i++) {
JSONObject org = (JSONObject) organizations.get(i);
String orgId = getJsonString(org, "id");
// This is a new organization so do a DB insert
if (orgId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")));
contentValues.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"));
contentValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"));
contentValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing organization so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Organization._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { orgId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")))
.withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
.withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"))
.withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"))
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing organization so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Organization._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { orgId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")))
.withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
.withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"))
.withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"))
.build());
}
}
}
}
@@ -1098,29 +1143,40 @@ public class ContactAccessorSdk5 extends ContactAccessor {
try {
ims = contact.getJSONArray("ims");
if (ims != null) {
for (int i = 0; i < ims.length(); i++) {
JSONObject im = (JSONObject) ims.get(i);
String imId = getJsonString(im, "id");
// This is a new IM so do a DB insert
if (imId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")));
// Delete all the ims
if (ims.length() == 0) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a im
else {
for (int i = 0; i < ims.length(); i++) {
JSONObject im = (JSONObject) ims.get(i);
String imId = getJsonString(im, "id");
// This is a new IM so do a DB insert
if (imId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing IM so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Im._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { imId, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"))
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")))
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing IM so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Im._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { imId, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"))
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")))
.build());
}
}
}
}
@@ -1148,34 +1204,46 @@ public class ContactAccessorSdk5 extends ContactAccessor {
.build());
}
// Modify urls
// Modify urls
JSONArray websites = null;
try {
websites = contact.getJSONArray("websites");
websites = contact.getJSONArray("urls");
if (websites != null) {
for (int i = 0; i < websites.length(); i++) {
JSONObject website = (JSONObject) websites.get(i);
String websiteId = getJsonString(website, "id");
// This is a new website so do a DB insert
if (websiteId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")));
// Delete all the websites
if (websites.length() == 0) {
Log.d(LOG_TAG, "This means we should be deleting all the phone numbers.");
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a website
else {
for (int i = 0; i < websites.length(); i++) {
JSONObject website = (JSONObject) websites.get(i);
String websiteId = getJsonString(website, "id");
// This is a new website so do a DB insert
if (websiteId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"));
contentValues.put(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")));
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing website so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Website._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { websiteId, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"))
.withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")))
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing website so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Website._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { websiteId, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.CommonDataKinds.Website.DATA, getJsonString(website, "value"))
.withValue(ContactsContract.CommonDataKinds.Website.TYPE, getContactType(getJsonString(website, "type")))
.build());
}
}
}
}
@@ -1201,30 +1269,41 @@ public class ContactAccessorSdk5 extends ContactAccessor {
try {
photos = contact.getJSONArray("photos");
if (photos != null) {
for (int i = 0; i < photos.length(); i++) {
JSONObject photo = (JSONObject) photos.get(i);
String photoId = getJsonString(photo, "id");
byte[] bytes = getPhotoBytes(getJsonString(photo, "value"));
// This is a new photo so do a DB insert
if (photoId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
contentValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes);
// Delete all the photos
if (photos.length() == 0) {
ops.add(ContentProviderOperation.newDelete(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.Data.RAW_CONTACT_ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { "" + rawId, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE })
.build());
}
// Modify or add a photo
else {
for (int i = 0; i < photos.length(); i++) {
JSONObject photo = (JSONObject) photos.get(i);
String photoId = getJsonString(photo, "id");
byte[] bytes = getPhotoBytes(getJsonString(photo, "value"));
// This is a new photo so do a DB insert
if (photoId == null) {
ContentValues contentValues = new ContentValues();
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
contentValues.put(ContactsContract.Data.IS_SUPER_PRIMARY, 1);
contentValues.put(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes);
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing photo so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Photo._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { photoId, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes)
.build());
ops.add(ContentProviderOperation.newInsert(
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
}
// This is an existing photo so do a DB update
else {
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(ContactsContract.CommonDataKinds.Photo._ID + "=? AND " +
ContactsContract.Data.MIMETYPE + "=?",
new String[] { photoId, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE })
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, bytes)
.build());
}
}
}
}
@@ -1257,7 +1336,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add a website to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param website the item to be inserted
*/
@@ -1273,7 +1352,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add an im to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param im the item to be inserted
*/
@@ -1288,7 +1367,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add an organization to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param org the item to be inserted
*/
@@ -1306,7 +1385,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add an address to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param address the item to be inserted
*/
@@ -1327,7 +1406,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add an email to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param email the item to be inserted
*/
@@ -1343,7 +1422,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add a phone to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param phone the item to be inserted
*/
@@ -1359,7 +1438,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Add a phone to a list of database actions to be performed
*
*
* @param ops the list of database actions
* @param phone the item to be inserted
*/
@@ -1376,10 +1455,10 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Gets the raw bytes from the supplied filename
*
*
* @param filename the file to read the bytes from
* @return a byte array
* @throws IOException
* @throws IOException
*/
private byte[] getPhotoBytes(String filename) {
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
@@ -1406,10 +1485,10 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Get an input stream based on file path or uri content://, http://, file://
*
*
* @param path
* @return an input stream
* @throws IOException
* @throws IOException
*/
private InputStream getPathFromUri(String path) throws IOException {
if (path.startsWith("content:")) {
@@ -1427,7 +1506,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* Creates a new contact and stores it in the database
*
*
* @param contact the contact to be saved
* @param account the account to be saved under
*/
@@ -1551,10 +1630,10 @@ public class ContactAccessorSdk5 extends ContactAccessor {
.build());
}
// Add urls
// Add urls
JSONArray websites = null;
try {
websites = contact.getJSONArray("websites");
websites = contact.getJSONArray("urls");
if (websites != null) {
for (int i = 0; i < websites.length(); i++) {
JSONObject website = (JSONObject) websites.get(i);
@@ -1606,7 +1685,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
@Override
/**
/**
* This method will remove a Contact from the database based on ID.
* @param id the unique ID of the contact to remove
*/
@@ -1629,10 +1708,10 @@ public class ContactAccessorSdk5 extends ContactAccessor {
}
/**************************************************************************
*
* All methods below this comment are used to convert from JavaScript
*
* All methods below this comment are used to convert from JavaScript
* text types to Android integer types and vice versa.
*
*
*************************************************************************/
/**
@@ -1715,7 +1794,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* getPhoneType converts an Android phone type into a string
* @param type
* @param type
* @return phone type as string.
*/
private String getPhoneType(int type) {
@@ -1815,7 +1894,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* getPhoneType converts an Android phone type into a string
* @param type
* @param type
* @return phone type as string.
*/
private String getContactType(int type) {
@@ -1864,7 +1943,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* getPhoneType converts an Android phone type into a string
* @param type
* @param type
* @return phone type as string.
*/
private String getOrgType(int type) {
@@ -1907,7 +1986,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
/**
* getPhoneType converts an Android phone type into a string
* @param type
* @param type
* @return phone type as string.
*/
private String getAddressType(int type) {
@@ -596,7 +596,12 @@ public class CordovaWebView extends WebView {
* <log level="DEBUG" />
*/
private void loadConfiguration() {
int id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName());
int id = getResources().getIdentifier("config", "xml", this.cordova.getActivity().getPackageName());
if(id == 0)
{
id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName());
Log.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
}
if (id == 0) {
LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
return;
@@ -676,7 +681,6 @@ public class CordovaWebView extends WebView {
/*
* onKeyDown
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
@@ -696,11 +700,12 @@ public class CordovaWebView extends WebView {
}
else
{
//Do some other stuff!
return super.onKeyDown(keyCode, event);
}
}
return false;
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event)
@@ -740,6 +745,7 @@ public class CordovaWebView extends WebView {
else if(keyUpCodes.contains(keyCode))
{
//What the hell should this do?
return super.onKeyUp(keyCode, event);
}
@@ -747,6 +753,7 @@ public class CordovaWebView extends WebView {
return false;
}
public void bindButton(boolean override)
{
this.bound = override;
+1 -1
View File
@@ -38,7 +38,7 @@ import android.telephony.TelephonyManager;
public class Device extends Plugin {
public static final String TAG = "Device";
public static String cordovaVersion = "1.9.0"; // Cordova version
public static String cordovaVersion = "2.0.0rc1"; // Cordova version
public static String platform = "Android"; // Device OS
public static String uuid; // Device UUID
@@ -619,10 +619,12 @@ public class DroidGap extends Activity implements CordovaInterface {
// If app doesn't want to run in background
if (!this.keepRunning) {
// Pause JavaScript timers (including setInterval)
this.appView.pauseTimers();
}
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
}
@Override
@@ -684,6 +686,9 @@ public class DroidGap extends Activity implements CordovaInterface {
LOG.d(TAG, "onDestroy()");
super.onDestroy();
// hide the splash screen to avoid leaking a window
this.removeSplashScreen();
if (this.appView != null) {
// Send destroy event to JavaScript
@@ -779,7 +784,7 @@ public class DroidGap extends Activity implements CordovaInterface {
super.finish();
}
/**
* Launch an activity for which you would like a result when it finished. When this activity exits,
* your onActivityResult() method will be called.
@@ -941,7 +946,7 @@ public class DroidGap extends Activity implements CordovaInterface {
*/
public Context getContext() {
LOG.d(TAG, "This will be deprecated December 2012");
return this.getContext();
return this;
}
/**
@@ -966,7 +971,7 @@ public class DroidGap extends Activity implements CordovaInterface {
* Removes the Dialog that displays the splash screen
*/
public void removeSplashScreen() {
if (splashDialog != null) {
if (splashDialog != null && splashDialog.isShowing()) {
splashDialog.dismiss();
splashDialog = null;
}
@@ -0,0 +1,111 @@
package org.apache.cordova.api;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.util.Log;
@Deprecated
public class LegacyContext implements CordovaInterface {
private static final String LOG_TAG = "Deprecation Notice";
private CordovaInterface cordova;
public LegacyContext(CordovaInterface cordova) {
this.cordova = cordova;
}
@Deprecated
public void cancelLoadUrl() {
Log.i(LOG_TAG, "Replace ctx.cancelLoadUrl() with cordova.cancelLoadUrl()");
this.cordova.cancelLoadUrl();
}
@Deprecated
public Activity getActivity() {
Log.i(LOG_TAG, "Replace ctx.getActivity() with cordova.getActivity()");
return this.cordova.getActivity();
}
@Deprecated
public Context getContext() {
Log.i(LOG_TAG, "Replace ctx.getContext() with cordova.getContext()");
return this.cordova.getContext();
}
@Deprecated
public Object onMessage(String arg0, Object arg1) {
Log.i(LOG_TAG, "Replace ctx.onMessage() with cordova.onMessage()");
return this.cordova.onMessage(arg0, arg1);
}
@Deprecated
public void setActivityResultCallback(IPlugin arg0) {
Log.i(LOG_TAG, "Replace ctx.setActivityResultCallback() with cordova.setActivityResultCallback()");
this.cordova.setActivityResultCallback(arg0);
}
@Deprecated
public void startActivityForResult(IPlugin arg0, Intent arg1, int arg2) {
Log.i(LOG_TAG, "Replace ctx.startActivityForResult() with cordova.startActivityForResult()");
this.cordova.startActivityForResult(arg0, arg1, arg2);
}
@Deprecated
public void startActivity(Intent intent) {
Log.i(LOG_TAG, "Replace ctx.startActivity() with cordova.getActivity().startActivity()");
this.cordova.getActivity().startActivity(intent);
}
@Deprecated
public Object getSystemService(String name) {
Log.i(LOG_TAG, "Replace ctx.getSystemService() with cordova.getActivity().getSystemService()");
return this.cordova.getActivity().getSystemService(name);
}
@Deprecated
public AssetManager getAssets() {
Log.i(LOG_TAG, "Replace ctx.getAssets() with cordova.getActivity().getAssets()");
return this.cordova.getActivity().getAssets();
}
@Deprecated
public void runOnUiThread(Runnable runnable) {
Log.i(LOG_TAG, "Replace ctx.runOnUiThread() with cordova.getActivity().runOnUiThread()");
this.cordova.getActivity().runOnUiThread(runnable);
}
@Deprecated
public Context getApplicationContext() {
Log.i(LOG_TAG, "Replace ctx.getApplicationContext() with cordova.getActivity().getApplicationContext()");
return this.cordova.getActivity().getApplicationContext();
}
@Deprecated
public PackageManager getPackageManager() {
Log.i(LOG_TAG, "Replace ctx.getPackageManager() with cordova.getActivity().getPackageManager()");
return this.cordova.getActivity().getPackageManager();
}
@Deprecated
public SharedPreferences getSharedPreferences(String name, int mode) {
Log.i(LOG_TAG, "Replace ctx.getSharedPreferences() with cordova.getActivity().getSharedPreferences()");
return this.cordova.getActivity().getSharedPreferences(name, mode);
}
@Deprecated
public void unregisterReceiver(BroadcastReceiver receiver) {
Log.i(LOG_TAG, "Replace ctx.unregisterReceiver() with cordova.getActivity().unregisterReceiver()");
this.cordova.getActivity().unregisterReceiver(receiver);
}
@Deprecated
public Resources getResources() {
Log.i(LOG_TAG, "Replace ctx.getResources() with cordova.getActivity().getResources()");
return this.cordova.getActivity().getResources();
}
}
@@ -32,12 +32,12 @@ public abstract class Plugin implements IPlugin {
public String id;
public CordovaWebView webView; // WebView object
public CordovaInterface ctx; // CordovaActivity object
public LegacyContext ctx; // LegacyContext object
public CordovaInterface cordova;
/**
* 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.
@@ -63,13 +63,13 @@ public abstract class Plugin implements IPlugin {
*/
public void setContext(CordovaInterface ctx) {
this.cordova = ctx;
this.ctx = cordova;
this.ctx = new LegacyContext(cordova);
}
/**
* Sets the main View of the application, this is the WebView within which
* Sets the main View of the application, this is the WebView within which
* a Cordova app runs.
*
*
* @param webView The Cordova WebView
*/
public void setView(CordovaWebView webView) {
@@ -77,8 +77,8 @@ public abstract class Plugin implements IPlugin {
}
/**
* Called when the system is about to start resuming a previous activity.
*
* Called when the system is about to start resuming a previous activity.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onPause(boolean multitasking) {
@@ -92,9 +92,16 @@ public class PluginManager {
* Load plugins from res/xml/plugins.xml
*/
public void loadPlugins() {
int id = this.ctx.getActivity().getResources().getIdentifier("plugins", "xml", this.ctx.getActivity().getPackageName());
int id = this.ctx.getActivity().getResources().getIdentifier("config", "xml", this.ctx.getActivity().getPackageName());
if(id == 0)
{
id = this.ctx.getActivity().getResources().getIdentifier("plugins", "xml", this.ctx.getActivity().getPackageName());
LOG.i(TAG, "Using plugins.xml instead of config.xml. plugins.xml will eventually be deprecated");
}
if (id == 0) {
this.pluginConfigurationMissing();
//We have the error, we need to exit without crashing!
return;
}
XmlResourceParser xml = this.ctx.getActivity().getResources().getXml(id);
int eventType = -1;
@@ -361,9 +368,9 @@ public class PluginManager {
}
private void pluginConfigurationMissing() {
System.err.println("=====================================================================================");
System.err.println("ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
System.err.println("https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=blob;f=framework/res/xml/plugins.xml");
System.err.println("=====================================================================================");
LOG.e(TAG, "=====================================================================================");
LOG.e(TAG, "ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
LOG.e(TAG, "https://git-wip-us.apache.org/repos/asf?p=incubator-cordova-android.git;a=blob;f=framework/res/xml/plugins.xml");
LOG.e(TAG, "=====================================================================================");
}
}
@@ -25,13 +25,13 @@ public class PluginResult {
private final int status;
private final String message;
private boolean keepCallback = false;
public PluginResult(Status status) {
this.status = status.ordinal();
this.message = "'" + PluginResult.StatusMessages[this.status] + "'";
this.message = "\"" + PluginResult.StatusMessages[this.status] + "\"";
}
public PluginResult(Status status, String message) {
this.status = status.ordinal();
this.message = JSONObject.quote(message);
@@ -61,11 +61,11 @@ public class PluginResult {
this.status = status.ordinal();
this.message = ""+b;
}
public void setKeepCallback(boolean b) {
this.keepCallback = b;
}
public int getStatus() {
return status;
}
@@ -73,23 +73,23 @@ public class PluginResult {
public String getMessage() {
return message;
}
public boolean getKeepCallback() {
return this.keepCallback;
}
public String getJSONString() {
return "{\"status\":" + this.status + ",\"message\":" + this.message + ",\"keepCallback\":" + this.keepCallback + "}";
}
public String toSuccessCallbackString(String callbackId) {
return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");";
}
public String toErrorCallbackString(String callbackId) {
return "cordova.callbackError('"+callbackId+"', " + this.getJSONString()+ ");";
}
public static String[] StatusMessages = new String[] {
"No result",
"OK",
@@ -102,7 +102,7 @@ public class PluginResult {
"JSON error",
"Error"
};
public enum Status {
NO_RESULT,
OK,
+2 -2
View File
@@ -11,7 +11,7 @@ This document is for people who need to upgrade their Cordova versions from an o
2. Add cordova-1.8.0.jar to the libs directory in your project
3. If you are using Eclipse, please refresh your eclipse project and do a clean
4. Copy the new cordova-1.7.0.js into your project
5. Update your HTML to sue the new cordova-1.7.0.js file
5. Update your HTML to use the new cordova-1.7.0.js file
6. Update the res/xml/plugins.xml to be the same as the one found in framework/res/xml/plugins.xml
@@ -21,7 +21,7 @@ This document is for people who need to upgrade their Cordova versions from an o
2. Add cordova-1.6.0.jar to the libs directory in your project
3. If you are using Eclipse, please refresh your eclipse project and do a clean
4. Copy the new cordova-1.6.0.js into your project
5. Update your HTML to sue the new cordova-1.6.0.js file
5. Update your HTML to use the new cordova-1.6.0.js file
6. Update the res/xml/plugins.xml to be the same as the one found in framework/res/xml/plugins.xml
@@ -35,7 +35,6 @@ $
<h4>Page 3</h4>
Press the 3 buttons below. You should stay on same page.<br>
Press "backbutton" 4 times. This will go back to #test3, #test2, #test1, then return to previous Page 2.<br>
(NOTE: IS THIS CORRECT BEHAVIOR?)
</div>
<a href="sample3.html#test1" class="btn large">page3#test1</a>
<a href="sample3.html#test2" class="btn large">page3#test2</a>
+2 -2
View File
@@ -17,5 +17,5 @@
under the License.
*/
document.write('<script type="text/javascript" charset="utf-8" src="../cordova-1.7.0.js"></script>');
document.write('<script type="text/javascript" charset="utf-8" src="cordova-1.7.0.js"></script>');
document.write('<script type="text/javascript" charset="utf-8" src="../cordova.android.js"></script>');
document.write('<script type="text/javascript" charset="utf-8" src="cordova.android.js"></script>');
@@ -0,0 +1,87 @@
package org.apache.cordova.test;
import org.apache.cordova.CordovaWebView;
import android.test.ActivityInstrumentationTestCase2;
import android.view.KeyEvent;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
public class BackButtonMultiPageTest extends ActivityInstrumentationTestCase2<backbuttonmultipage> {
private int TIMEOUT = 1000;
backbuttonmultipage testActivity;
private FrameLayout containerView;
private LinearLayout innerContainer;
private CordovaWebView testView;
public BackButtonMultiPageTest() {
super("org.apache.cordova.test", backbuttonmultipage.class);
}
protected void setUp() throws Exception {
super.setUp();
testActivity = this.getActivity();
containerView = (FrameLayout) testActivity.findViewById(android.R.id.content);
innerContainer = (LinearLayout) containerView.getChildAt(0);
testView = (CordovaWebView) innerContainer.getChildAt(0);
}
public void testPreconditions(){
assertNotNull(innerContainer);
assertNotNull(testView);
}
public void testViaHref() {
testView.sendJavascript("window.location = 'sample2.html';");
sleep();
String url = testView.getUrl();
assertTrue(url.endsWith("sample2.html"));
testView.sendJavascript("window.location = 'sample3.html';");
sleep();
url = testView.getUrl();
assertTrue(url.endsWith("sample3.html"));
boolean didGoBack = testView.backHistory();
sleep();
url = testView.getUrl();
assertTrue(url.endsWith("sample2.html"));
assertTrue(didGoBack);
didGoBack = testView.backHistory();
sleep();
url = testView.getUrl();
assertTrue(url.endsWith("index.html"));
assertTrue(didGoBack);
}
public void testViaLoadUrl() {
testView.loadUrl("file:///android_asset/www/backbuttonmultipage/sample2.html");
sleep();
String url = testView.getUrl();
assertTrue(url.endsWith("sample2.html"));
testView.loadUrl("file:///android_asset/www/backbuttonmultipage/sample3.html");
sleep();
url = testView.getUrl();
assertTrue(url.endsWith("sample3.html"));
boolean didGoBack = testView.backHistory();
sleep();
url = testView.getUrl();
assertTrue(url.endsWith("sample2.html"));
assertTrue(didGoBack);
didGoBack = testView.backHistory();
sleep();
url = testView.getUrl();
assertTrue(url.endsWith("index.html"));
assertTrue(didGoBack);
}
private void sleep() {
try {
Thread.sleep(TIMEOUT);
} catch (InterruptedException e) {
fail("Unexpected Timeout");
}
}
}
@@ -23,6 +23,7 @@ import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.IPlugin;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -34,45 +35,42 @@ public class CordovaDriverAction extends Activity implements CordovaInterface {
super.onCreate(savedInstanceState);
}
@Override
public void bindBackButton(boolean arg0) {
// TODO Auto-generated method stub
}
@Override
public void cancelLoadUrl() {
// TODO Auto-generated method stub
}
@Override
public Activity getActivity() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isBackButtonBound() {
// TODO Auto-generated method stub
return false;
}
@Override
public Object onMessage(String arg0, Object arg1) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setActivityResultCallback(IPlugin arg0) {
// TODO Auto-generated method stub
}
@Override
public void startActivityForResult(IPlugin arg0, Intent arg1, int arg2) {
// TODO Auto-generated method stub
}
public Context getContext() {
return this;
}
}
@@ -1,37 +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.test;
import org.openqa.selenium.android.library.ViewAdapter;
import org.openqa.selenium.android.library.ViewFactory;
import org.apache.cordova.CordovaWebView;
import android.app.Activity;
import android.webkit.WebView;
public class CordovaViewFactory implements ViewFactory {
public ViewAdapter createNewView(Activity arg0) {
// TODO Auto-generated method stub
return new ViewAdapter("org.apache.cordova.CordovaWebView", new CordovaWebView(arg0));
}
}
@@ -24,6 +24,7 @@ import org.apache.cordova.api.CordovaInterface;
import org.apache.cordova.api.IPlugin;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
@@ -52,45 +53,41 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
}
}
@Override
public void startActivityForResult(IPlugin command, Intent intent, int requestCode) {
// TODO Auto-generated method stub
}
@Override
public void setActivityResultCallback(IPlugin plugin) {
// TODO Auto-generated method stub
}
@Override
public void bindBackButton(boolean override) {
// TODO Auto-generated method stub
}
@Override
public boolean isBackButtonBound() {
// TODO Auto-generated method stub
return false;
}
@Override
public Activity getActivity() {
// TODO Auto-generated method stub
return this;
}
@Override
public void cancelLoadUrl() {
// TODO Auto-generated method stub
}
@Override
public Object onMessage(String id, Object data) {
// TODO Auto-generated method stub
return null;
}
public Context getContext() {
return this;
}
}
@@ -44,11 +44,11 @@ public class ErrorUrlTest extends ActivityInstrumentationTestCase2<errorurl> {
private void sleep() {
try {
Thread.sleep(TIMEOUT);
Thread.sleep(TIMEOUT);
} catch (InterruptedException e) {
fail("Unexpected Timeout");
fail("Unexpected Timeout");
}
}
}
}
@@ -1,90 +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.test;
import org.apache.cordova.CordovaWebViewClient;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaChromeClient;
import org.apache.cordova.test.CordovaViewFactory;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.android.library.AndroidWebDriver;
import org.openqa.selenium.android.library.ChromeClientWrapper;
import org.openqa.selenium.android.library.ViewClientWrapper;
import android.test.ActivityInstrumentationTestCase2;
public class WebDriverTest extends ActivityInstrumentationTestCase2<CordovaDriverAction> {
private static final long TIMEOUT = 5000;
private CordovaDriverAction testActivity;
private CordovaWebView testView;
private CordovaViewFactory viewFactory;
private CordovaChromeClient appCode;
private CordovaWebViewClient viewHandler;
private AndroidWebDriver testDriver;
private ViewClientWrapper viewClientWrapper;
private ChromeClientWrapper chromeClientWrapper;
public WebDriverTest() {
super("com.phonegap.test.activities",CordovaDriverAction.class);
}
protected void setUp() throws Exception{
super.setUp();
testActivity = this.getActivity();
viewFactory = new CordovaViewFactory();
appCode = new CordovaChromeClient(testActivity);
viewHandler = new CordovaWebViewClient(testActivity);
viewClientWrapper = new ViewClientWrapper("org.apache.cordova.CordovaWebViewClient", viewHandler);
chromeClientWrapper = new ChromeClientWrapper("org.apache.cordova.CordovaChromeClient", appCode);
testDriver = new AndroidWebDriver(testActivity, viewFactory, viewClientWrapper, chromeClientWrapper);
testView = (CordovaWebView) testDriver.getWebView();
viewHandler.setWebView(testView);
appCode.setWebView(testView);
}
public void testPreconditions(){
assertNotNull(testView);
}
public void testWebLoad() {
testDriver.get("file:///android_asset/www/index.html");
sleep();
String url = testView.getUrl();
//Check the sanity!
boolean result = url.equals("file:///android_asset/www/index.html");
assertTrue(result);
WebElement platformSpan = testDriver.findElement(By.id("platform"));
String text = platformSpan.getText();
assertTrue(text.equals("Android"));
}
private void sleep() {
try {
Thread.sleep(TIMEOUT);
} catch (InterruptedException e) {
fail("Unexpected Timeout");
}
}
}