Compare commits

...

64 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
macdonst
e5e7c3fad3 Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-android 2012-06-29 15:55:34 -04:00
macdonst
2a8b9ab75e Tagging to 1.9.0 2012-06-29 15:55:03 -04:00
Fil Maj
c8f0ffb42f Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-android 2012-06-29 09:37:59 -07:00
Fil Maj
b3e68b96cf Removing CordovaWebView Guide; its going into the docs 2012-06-29 09:37:31 -07:00
macdonst
ae7a550a09 Only load Exif information if necessary 2012-06-29 11:31:33 -04:00
Anis Kadri
e069bbb800 CB-937 fixing debug for windows 2012-06-28 17:11:21 -07:00
Anis Kadri
17ff6be6a9 CB-937 fixing debug 2012-06-28 17:08:32 -07:00
Anis Kadri
d42489c67a Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-android 2012-06-28 16:47:18 -07: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
macdonst
e2047afa42 Wire rotation fix to correctOrientation parameter 2012-06-28 12:00:19 -04:00
macdonst
231b39d2dc Reset orientation exif information when photo is rotated
When a photo is taken in portrait mode we rotate it so it shows up properly in the webview. The Exif orientation must be reset to normal orientation (0) or the image will not display properly on desktops.
2012-06-28 12:00:19 -04:00
macdonst
dddce30368 Rotate image if taken in portrait mode 2012-06-28 12:00:19 -04:00
macdonst
e0e4ba2bd7 Fix double image problem on Samsung phones
On Samsung phones even if you tell the camera not to save to the photo gallery it still does. This small fix deletes the original file as it is not needed.
2012-06-28 12:00:19 -04:00
macdonst
e0eadb6b76 Using a better scaling algorithm to resize the image
Instead of reading the entire image into a bitmap then scaling we use the
inSampleSize option to get a close to the target width and height as possible
then we scale that smaller image.
2012-06-28 12:00:19 -04:00
macdonst
483e5dfbea Switch getPicture from Gallery to use file instead of content resolver 2012-06-28 12:00:18 -04:00
macdonst
8aa9d8213d Cache bust returned Image URI if saveToPhotoAlbum is false 2012-06-28 12:00:18 -04:00
macdonst
a74f71c935 Decode image from File instead of content resolver 2012-06-28 12:00:18 -04:00
macdonst
87b81e53f0 CB-978: FileTransfer.upload from a directory with a space fails 2012-06-28 11:57:06 -04:00
Joe Bowser
a2816e31c3 Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/incubator-cordova-android 2012-06-28 08:00:59 -07:00
Joe Bowser
f78af9f27b Forgot to add it renderscript.opt.level to the project. This will fix ant issues 2012-06-28 08:00:35 -07:00
Anis Kadri
98138a0a60 log was actually doing nothing...fixing it 2012-06-27 17:55:35 -07:00
Anis Kadri
e639b6303e updating create script to work from distro and source 2012-06-27 17:54:57 -07:00
Anis Kadri
99fb3ebe00 creating project without source 2012-06-26 17:34:19 -07:00
Joe Bowser
5829840409 Re-adding getContext because yo dawg, I heard you like contexts in your contexts 2012-06-26 11:25:17 -07:00
Anis Kadri
4699ab5500 forgot to add +x on BOOM 2012-06-25 15:03:19 -07:00
Anis Kadri
69fc7f39b7 setting +x on script files 2012-06-25 14:59:35 -07:00
Anis Kadri
510a962a52 deleting old BOOM 2012-06-25 14:56:09 -07:00
Anis Kadri
570fc3cfb2 removing echoes 2012-06-25 14:46:10 -07:00
Joe Bowser
5d211f2fa6 Might as well keep isBackbuttonOverriden on the plugin 2012-06-22 10:38:42 -07:00
Joe Bowser
dcb127c14d Weird merge error didn't account for isBackButtonBound 2012-06-22 10:37:33 -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
44 changed files with 1553 additions and 1113 deletions

1
.gitignore vendored
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

View File

@@ -1 +1 @@
1.9.0rc1
2.0.0rc1

View File

@@ -1,21 +0,0 @@
#! /bin/sh
# 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.
#
#
./bin/create
cd ./example && ./cordova/debug && ./cordova/log

View File

@@ -46,11 +46,16 @@ fi
# cleanup after exit and/or on error
function on_exit {
echo "Cleaning up ..."
# [ -f $BUILD_PATH/framework/libs/commons-codec-1.6.jar ] && rm $BUILD_PATH/framework/libs/commons-codec-1.6.jar
# [ -d $BUILD_PATH/framework/libs ] && rmdir $BUILD_PATH/framework/libs
[ -f $BUILD_PATH/framework/assets/www/cordova-$VERSION.js ] && rm $BUILD_PATH/framework/assets/www/cordova-$VERSION.js
[ -f $BUILD_PATH/framework/cordova-$VERSION.jar ] && rm $BUILD_PATH/framework/cordova-$VERSION.jar
if [ -f $BUILD_PATH/framework/assets/www/cordova-$VERSION.js ]
then
rm $BUILD_PATH/framework/assets/www/cordova-$VERSION.js
fi
if [ -f $BUILD_PATH/framework/cordova-$VERSION.jar ]
then
rm $BUILD_PATH/framework/cordova-$VERSION.jar
fi
}
function on_error {
@@ -58,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
@@ -69,50 +87,54 @@ MANIFEST_PATH=$PROJECT_PATH/AndroidManifest.xml
TARGET=$($ANDROID_BIN list targets | grep id: | tail -1 | cut -f 2 -d ' ' )
# if this a distribution release no need to build a jar
if [ ! -e $BUILD_PATH/cordova-$VERSION.jar ] && [ -d $BUILD_PATH/framework ]
then
# update the cordova-android framework for the desired target
$ANDROID_BIN update project --target $TARGET --path $BUILD_PATH/framework &> /dev/null
$ANDROID_BIN update project --target $TARGET --path $BUILD_PATH/framework &> /dev/null
if [ ! -e $BUILD_PATH/framework/libs/commons-codec-1.6.jar ]; then
# Use curl to get the jar (TODO: Support Apache Mirrors)
echo "Downloading common-codecs-1.6 ..."
curl -OL http://mirror.symnds.com/software/Apache//commons/codec/binaries/commons-codec-1.6-bin.zip &> /dev/null
unzip commons-codec-1.6-bin.zip &> /dev/null
mkdir -p $BUILD_PATH/framework/libs
cp commons-codec-1.6/commons-codec-1.6.jar $BUILD_PATH/framework/libs
# cleanup yo
rm commons-codec-1.6-bin.zip && rm -rf commons-codec-1.6
fi
if [ ! -e $BUILD_PATH/framework/libs/commons-codec-1.6.jar ]; then
# Use curl to get the jar (TODO: Support Apache Mirrors)
curl -OL http://mirror.symnds.com/software/Apache//commons/codec/binaries/commons-codec-1.6-bin.zip &> /dev/null
unzip commons-codec-1.6-bin.zip &> /dev/null
mkdir -p $BUILD_PATH/framework/libs
cp commons-codec-1.6/commons-codec-1.6.jar $BUILD_PATH/framework/libs
# cleanup yo
rm commons-codec-1.6-bin.zip && rm -rf commons-codec-1.6
fi
# compile cordova.js and cordova.jar
echo "Building cordova-$VERSION.jar and cordova-$VERSION.js ..."
(cd $BUILD_PATH/framework && ant jar &> /dev/null )
(cd $BUILD_PATH/framework && ant jar &> /dev/null )
fi
# create new android project
echo "Creating a new cordova android project ..."
$ANDROID_BIN create project --target $TARGET --path $PROJECT_PATH --package $PACKAGE --activity $ACTIVITY &> /dev/null
# copy project template
echo "Copying assets and resources ..."
cp -r $BUILD_PATH/bin/templates/project/assets $PROJECT_PATH
cp -r $BUILD_PATH/bin/templates/project/res $PROJECT_PATH
# copy cordova.js, cordova.jar and res/xml
echo "Setting up config and plugins list ..."
cp -r $BUILD_PATH/framework/res/xml $PROJECT_PATH/res
echo "Adding cordova-$VERSION.js and cordova-$VERSION.jar ..."
cp $BUILD_PATH/framework/assets/www/cordova-$VERSION.js $PROJECT_PATH/assets/www/cordova-$VERSION.js
cp $BUILD_PATH/framework/cordova-$VERSION.jar $PROJECT_PATH/libs/cordova-$VERSION.jar
if [ -d $BUILD_PATH/framework ]
then
cp -r $BUILD_PATH/framework/res/xml $PROJECT_PATH/res
cp $BUILD_PATH/framework/assets/www/cordova-$VERSION.js $PROJECT_PATH/assets/www/cordova-$VERSION.js
cp $BUILD_PATH/framework/cordova-$VERSION.jar $PROJECT_PATH/libs/cordova-$VERSION.jar
else
cp -r $BUILD_PATH/xml $PROJECT_PATH/res/xml
cp $BUILD_PATH/cordova-$VERSION.js $PROJECT_PATH/assets/www/cordova-$VERSION.js
cp $BUILD_PATH/cordova-$VERSION.jar $PROJECT_PATH/libs/cordova-$VERSION.jar
fi
# interpolate the activity name and package
echo "Updating Activity and AndroidManifest ..."
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

View File

@@ -133,13 +133,15 @@ var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
// create the project
exec('android.bat create project --target '+TARGET+' --path '+PROJECT_PATH+' --package '+PACKAGE+' --activity '+ACTIVITY);
// update the cordova framework project to a target that exists on this machine
exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
// pull down commons codec if necessary
downloadCommonsCodec();
exec('ant.bat -f '+ ROOT +'\\framework\\build.xml jar');
// build from source. distro should have these files
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
// update the cordova framework project to a target that exists on this machine
exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
// pull down commons codec if necessary
downloadCommonsCodec();
exec('ant.bat -f '+ ROOT +'\\framework\\build.xml jar');
}
// copy in the project template
exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
@@ -147,16 +149,23 @@ exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\assets '+PROJECT_
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
// copy in cordova.js
exec('%comspec% /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
// copy in cordova.jar
exec('%comspec% /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
// copy in xml
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\cordova.xml ' + PROJECT_PATH + '\\res\\xml\\cordova.xml /Y');
exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\plugins.xml ' + PROJECT_PATH + '\\res\\xml\\plugins.xml /Y');
// check if we have the source or the distro files
if(fso.FolderExists(ROOT + '\\framework')) {
exec('%comspec% /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
exec('%comspec% /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\cordova.xml ' + PROJECT_PATH + '\\res\\xml\\cordova.xml /Y');
exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\plugins.xml ' + PROJECT_PATH + '\\res\\xml\\plugins.xml /Y');
} else {
// copy in cordova.js
exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
// copy in cordova.jar
exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
// copy in xml
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
exec('%comspec% /c copy '+ROOT+'\\xml\\cordova.xml ' + PROJECT_PATH + '\\res\\xml\\cordova.xml /Y');
exec('%comspec% /c copy '+ROOT+'\\xml\\plugins.xml ' + PROJECT_PATH + '\\res\\xml\\plugins.xml /Y');
}
// copy cordova scripts
fso.CreateFolder(PROJECT_PATH + '\\cordova');

0
bin/templates/cordova/BOOM Normal file → Executable file
View File

0
bin/templates/cordova/clean Normal file → Executable file
View File

24
bin/templates/cordova/cordova Normal file → Executable file
View File

@@ -61,12 +61,15 @@ function log {
adb logcat
}
function debug_install {
ant debug install
}
function debug {
ant debug
if [ $(check_devices) == 0 ] ; then
ant debug install
else
ant debug
echo "##################################################################"
echo "# Plug in your device or launch an emulator with cordova/emulate #"
echo "##################################################################"
fi
}
function launch {
@@ -75,16 +78,7 @@ function launch {
}
function BOOM {
clean
if [ $(check_devices) == 0 ] ; then
debug_install && launch
return
else
debug
echo "##################################################################"
echo "# Plug in your device or launch an emulator with cordova/emulate #"
echo "##################################################################"
fi
clean && debug && launch
}
# TODO parse arguments

View File

@@ -8,7 +8,7 @@ function exec(command) {
if(!oExec.StdOut.AtEndOfStream) {
var line = oExec.StdOut.ReadLine();
// XXX: Change to verbose mode
//WScript.StdOut.WriteLine(line);
// WScript.StdOut.WriteLine(line);
output += line;
}
WScript.sleep(100);
@@ -71,15 +71,18 @@ function clean() {
}
function debug() {
exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
}
function debug_install() {
exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
if(emulator_running()) {
exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
} else {
exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
WScript.Echo("##################################################################");
WScript.Echo("# Plug in your device or launch an emulator with cordova/emulate #");
WScript.Echo("##################################################################");
}
}
function log() {
WScript.Echo(exec("%comspec% /c adb.bat logcat"));
shell.Run("%comspec% /c adb logcat");
}
function launch() {
@@ -90,15 +93,8 @@ function launch() {
function BOOM() {
clean();
if(emulator_running()) {
debug_install();
launch();
} else {
debug();
WScript.Echo("##################################################################");
WScript.Echo("# Plug in your device or launch an emulator with cordova/emulate #");
WScript.Echo("##################################################################");
}
debug();
launch();
}
var args = WScript.Arguments;
if(args.count() != 1) {

0
bin/templates/cordova/debug Normal file → Executable file
View File

0
bin/templates/cordova/emulate Normal file → Executable file
View File

0
bin/templates/cordova/log Normal file → Executable file
View File

View File

@@ -0,0 +1 @@
%~dp0\cordova.bat log

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.0rc1.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>

View File

@@ -1,6 +1,6 @@
// commit 25033fceac7c800623f1f16881b784d19eba69cc
// commit fd00bff18daf29606d88263f7586f20cf9421861
// File generated at :: Thu Jun 21 2012 10:45:32 GMT-0700 (PDT)
// 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'),

View File

@@ -19,7 +19,7 @@
<html>
<head>
<title></title>
<script src="cordova-1.9.0rc1.js"></script>
<script src="cordova-2.0.0rc1.js"></script>
</head>
<body>

View File

@@ -12,3 +12,4 @@ split.density=false
# Project target.
target=Google Inc.:Google APIs:15
apk-configurations=
renderscript.opt.level=O0

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>

View File

@@ -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];

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");
@@ -199,6 +202,15 @@ public class App extends Plugin {
webView.bindButton(button, override);
}
/**
* Return whether the Android back button is overridden by the user.
*
* @return boolean
*/
public boolean isBackbuttonOverridden() {
return webView.isBackButtonBound();
}
/**
* Exit the Android application.
*/

25
framework/src/org/apache/cordova/AudioHandler.java Executable file → Normal file
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));
}

400
framework/src/org/apache/cordova/AudioPlayer.java Executable file → Normal file
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();
}
}
}

View File

@@ -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,10 +35,10 @@ 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;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.Bitmap.CompressFormat;
import android.media.MediaScannerConnection;
@@ -82,11 +81,14 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
private int encodingType; // Type of encoding to use
private int mediaType; // What type of media to retrieve
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
private boolean correctOrientation; // Should the pictures orientation be corrected
private boolean allowEdit; // Should we allow the user to crop the image
public String callbackId;
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;
@@ -136,8 +138,19 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
this.targetHeight = args.getInt(4);
this.encodingType = args.getInt(5);
this.mediaType = args.getInt(6);
this.allowEdit = args.getBoolean(7);
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);
}
@@ -181,7 +194,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
// Specify file so that large image is captured and returned
// TODO: What if there isn't any external storage?
File photo = createCaptureFile(encodingType);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
this.imageUri = Uri.fromFile(photo);
@@ -245,20 +257,322 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
}
/**
* Scales the bitmap according to the requested size.
* Called when the camera view exits.
*
* @param bitmap The bitmap to scale.
* @return Bitmap A new Bitmap object of the same bitmap after scaling.
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
*/
public Bitmap scaleBitmap(Bitmap bitmap) {
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
// Get src and dest types from request code
int srcType = (requestCode / 16) - 1;
int destType = (requestCode % 16) - 1;
int rotate = 0;
// If CAMERA
if (srcType == CAMERA) {
// Create an ExifHelper to save the exif data that is lost during compression
ExifHelper exif = new ExifHelper();
try {
if (this.encodingType == JPEG) {
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
exif.readExifData();
rotate = exif.getOrientation();
}
} catch (IOException e) {
e.printStackTrace();
}
// If image available
if (resultCode == Activity.RESULT_OK) {
try {
Bitmap bitmap = null;
Uri uri = null;
// If sending base64 image back
if (destType == DATA_URL) {
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
if (rotate != 0 && this.correctOrientation) {
bitmap = getRotatedBitmap(rotate, bitmap, exif);
}
this.processPicture(bitmap);
checkForDuplicateImage(DATA_URL);
}
// If sending filename back
else if (destType == FILE_URI) {
if (!this.saveToPhotoAlbum) {
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
} else {
uri = getUriFromMediaStore();
}
if (uri == null) {
this.failPicture("Error capturing image - no media storage found.");
}
// If all this is true we shouldn't compress the image.
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && rotate == 0) {
writeUncompressedImage(uri);
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
} else {
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
if (rotate != 0 && this.correctOrientation) {
bitmap = getRotatedBitmap(rotate, bitmap, exif);
}
// Add compressed version of captured image to returned media store Uri
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
// Restore exif data to file
if (this.encodingType == JPEG) {
String exifPath;
if (this.saveToPhotoAlbum) {
exifPath = FileUtils.getRealPathFromURI(uri, this.cordova);
} else {
exifPath = uri.getPath();
}
exif.createOutFile(exifPath);
exif.writeExifData();
}
}
// Send Uri back to JavaScript for viewing image
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
bitmap = null;
} catch (IOException e) {
e.printStackTrace();
this.failPicture("Error capturing image.");
}
}
// If cancelled
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Camera cancelled.");
}
// If something else
else {
this.failPicture("Did not complete!");
}
}
// If retrieving photo from library
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = intent.getData();
// If you ask for video or all media type you will automatically get back a file URI
// and there will be no attempt to resize any returned data
if (this.mediaType != PICTURE) {
this.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);
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);
}
}
// 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();
}
}
}
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Selection cancelled.");
}
else {
this.failPicture("Selection did not complete!");
}
}
}
/**
* Figure out if the bitmap should be rotated. For instance if the picture was taken in
* portrait mode
*
* @param rotate
* @param bitmap
* @return rotated bitmap
*/
private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
Matrix matrix = new Matrix();
if (rotate == 180) {
matrix.setRotate(rotate);
} else {
matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
}
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
exif.resetOrientation();
return bitmap;
}
/**
* In the special case where the default width, height and quality are unchanged
* we just write the file out to disk saving the expensive Bitmap.compress function.
*
* @param uri
* @throws FileNotFoundException
* @throws IOException
*/
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
IOException {
FileInputStream fis = new FileInputStream(FileUtils.stripFileProtocol(imageUri.toString()));
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
os.close();
fis.close();
}
/**
* Create entry in media store for image
*
* @return uri
*/
private Uri getUriFromMediaStore() {
ContentValues values = new ContentValues();
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri uri;
try {
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} catch (UnsupportedOperationException e) {
LOG.d(LOG_TAG, "Can't write to external media storage.");
try {
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
} catch (UnsupportedOperationException ex) {
LOG.d(LOG_TAG, "Can't write to internal media storage.");
return null;
}
}
return uri;
}
/**
* Return a scaled bitmap based on the target width and height
*
* @param imagePath
* @return
*/
private Bitmap getScaledBitmap(String imagePath) {
// If no new width or height were specified return the original bitmap
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
return BitmapFactory.decodeFile(imagePath);
}
// figure out the original width and height of the image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
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;
int origWidth = bitmap.getWidth();
int origHeight = bitmap.getHeight();
// If no new width or height were specified return the original bitmap
if (newWidth <= 0 && newHeight <= 0) {
return bitmap;
newWidth = origWidth;
newHeight = origHeight;
}
// Only the width was specified
else if (newWidth > 0 && newHeight <= 0) {
@@ -285,237 +599,32 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
}
}
Bitmap retval = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
bitmap.recycle();
System.gc();
int[] retval = new int[2];
retval[0] = newWidth;
retval[1] = newHeight;
return retval;
}
/**
* Called when the camera view exits.
* Figure out what ratio we can load our image into memory at while still being bigger than
* our desired width and height
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
* @param srcWidth
* @param srcHeight
* @param dstWidth
* @param dstHeight
* @return
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
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;
// Get src and dest types from request code
int srcType = (requestCode / 16) - 1;
int destType = (requestCode % 16) - 1;
int rotate = 0;
// Create an ExifHelper to save the exif data that is lost during compression
ExifHelper exif = new ExifHelper();
try {
if (this.encodingType == JPEG) {
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
exif.readExifData();
}
} catch (IOException e) {
e.printStackTrace();
if (srcAspect > dstAspect) {
return srcWidth / dstWidth;
} else {
return srcHeight / dstHeight;
}
// If CAMERA
if (srcType == CAMERA) {
// If image available
if (resultCode == Activity.RESULT_OK) {
try {
Bitmap bitmap = null;
// If sending base64 image back
if (destType == DATA_URL) {
bitmap = scaleBitmap(getBitmapFromResult(intent));
this.processPicture(bitmap);
checkForDuplicateImage(DATA_URL);
}
// 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()));
} else {
// Create entry in media store for image
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
ContentValues values = new ContentValues();
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
try {
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} catch (UnsupportedOperationException e) {
LOG.d(LOG_TAG, "Can't write to external media storage.");
try {
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
} catch (UnsupportedOperationException ex) {
LOG.d(LOG_TAG, "Can't write to internal media storage.");
this.failPicture("Error capturing image - no media storage found.");
return;
}
}
}
// If all this is true we shouldn't compress the image.
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100) {
FileInputStream fis = new FileInputStream(FileUtils.stripFileProtocol(imageUri.toString()));
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
os.close();
fis.close();
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
} else {
bitmap = scaleBitmap(getBitmapFromResult(intent));
// Add compressed version of captured image to returned media store Uri
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
// Restore exif data to file
if (this.encodingType == JPEG) {
String exifPath;
if (this.saveToPhotoAlbum) {
exifPath = FileUtils.getRealPathFromURI(uri, this.cordova);
} else {
exifPath = uri.getPath();
}
exif.createOutFile(exifPath);
exif.writeExifData();
}
}
// Send Uri back to JavaScript for viewing image
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
this.cleanup(FILE_URI, this.imageUri, bitmap);
bitmap = null;
} catch (IOException e) {
e.printStackTrace();
this.failPicture("Error capturing image.");
}
}
// If cancelled
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Camera cancelled.");
}
// If something else
else {
this.failPicture("Did not complete!");
}
}
// If retrieving photo from library
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
if (resultCode == Activity.RESULT_OK) {
Uri uri = intent.getData();
android.content.ContentResolver resolver = this.cordova.getActivity().getContentResolver();
// If you ask for video or all media type you will automatically get back a file URI
// and there will be no attempt to resize any returned data
if (this.mediaType != PICTURE) {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
else {
// If sending base64 image back
if (destType == DATA_URL) {
try {
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
String[] cols = { MediaStore.Images.Media.ORIENTATION };
Cursor cursor = this.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);
}
bitmap = scaleBitmap(bitmap);
this.processPicture(bitmap);
bitmap.recycle();
bitmap = null;
System.gc();
} catch (FileNotFoundException e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
}
}
// 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 {
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
bitmap = scaleBitmap(bitmap);
String fileName = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
OutputStream os = new FileOutputStream(fileName);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
// Restore exif data to file
if (this.encodingType == JPEG) {
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
exif.writeExifData();
}
bitmap.recycle();
bitmap = null;
// The resized image is cached by the app in order to get around this and not have to delete you
// application cache I'm adding the current system time to the end of the file url.
this.success(new PluginResult(PluginResult.Status.OK, ("file://" + fileName + "?" + System.currentTimeMillis())), this.callbackId);
System.gc();
} catch (Exception e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
}
}
else {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
}
}
}
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Selection cancelled.");
}
else {
this.failPicture("Selection did not complete!");
}
}
}
private Bitmap getBitmapFromResult(Intent intent)
throws IOException, FileNotFoundException {
Bitmap bitmap = null;
try {
bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getActivity().getContentResolver(), imageUri);
} catch (FileNotFoundException e) {
Uri uri = intent.getData();
android.content.ContentResolver resolver = this.ctx.getActivity().getContentResolver();
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
}
return bitmap;
}
}
/**
* Creates a cursor that can be used to determine how many images we have.
@@ -530,20 +639,25 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
null,
null);
}
/**
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
* @param newImage
*/
private void cleanup(int imageType, Uri oldImage, Bitmap bitmap) {
bitmap.recycle();
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
if (bitmap != null) {
bitmap.recycle();
}
// Clean up initial camera-written image file.
(new File(FileUtils.stripFileProtocol(oldImage.toString()))).delete();
checkForDuplicateImage(imageType);
// Scan for the gallery to update pic refs in gallery
this.scanForGallery();
if (this.saveToPhotoAlbum && newImage != null) {
this.scanForGallery(newImage);
}
System.gc();
}
@@ -560,14 +674,17 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
Cursor cursor = queryImgDB(contentStore);
int currentNumOfImages = cursor.getCount();
if (type == FILE_URI) {
if (type == FILE_URI && this.saveToPhotoAlbum) {
diff = 2;
}
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
if ((currentNumOfImages - numPics) == diff) {
cursor.moveToLast();
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 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);
}
@@ -616,24 +733,26 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
public void failPicture(String err) {
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
}
private void scanForGallery() {
if(this.conn!=null) this.conn.disconnect();
this.conn = new MediaScannerConnection(this.ctx.getActivity().getApplicationContext(), this);
conn.connect();
}
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");
}
}
public void onScanCompleted(String path, Uri uri) {
this.conn.disconnect();
this.conn.disconnect();
}
}

View File

@@ -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) {

View File

@@ -54,10 +54,10 @@ public class CordovaWebView extends WebView {
private HashMap<String, Boolean> whiteListCache = new HashMap<String, Boolean>();
private ArrayList<Integer> keyDownCodes = new ArrayList<Integer>();
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
public PluginManager pluginManager;
public CallbackServer callbackServer;
/** Actvities and other important classes **/
private CordovaInterface cordova;
@@ -65,7 +65,7 @@ public class CordovaWebView extends WebView {
@SuppressWarnings("unused")
private CordovaChromeClient chromeClient;
//This is for the polyfil history
//This is for the polyfil history
private String url;
String baseUrl;
private Stack<String> urls = new Stack<String>();
@@ -83,7 +83,7 @@ public class CordovaWebView extends WebView {
/**
* Constructor.
*
*
* @param context
*/
public CordovaWebView(Context context) {
@@ -102,7 +102,7 @@ public class CordovaWebView extends WebView {
/**
* Constructor.
*
*
* @param context
* @param attrs
*/
@@ -124,11 +124,11 @@ public class CordovaWebView extends WebView {
/**
* Constructor.
*
*
* @param context
* @param attrs
* @param defStyle
*
*
*/
public CordovaWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@@ -148,7 +148,7 @@ public class CordovaWebView extends WebView {
/**
* Constructor.
*
*
* @param context
* @param attrs
* @param defStyle
@@ -211,7 +211,7 @@ public class CordovaWebView extends WebView {
/**
* Set the WebViewClient.
*
*
* @param client
*/
public void setWebViewClient(CordovaWebViewClient client) {
@@ -221,7 +221,7 @@ public class CordovaWebView extends WebView {
/**
* Set the WebChromeClient.
*
*
* @param client
*/
public void setWebChromeClient(CordovaChromeClient client) {
@@ -231,7 +231,7 @@ public class CordovaWebView extends WebView {
/**
* Add entry to approved list of URLs (whitelist)
*
*
* @param origin URL regular expression to allow
* @param subdomains T=include all subdomains under origin
*/
@@ -269,7 +269,7 @@ public class CordovaWebView extends WebView {
/**
* Determine if URL is in approved list of URLs to load.
*
*
* @param url
* @return
*/
@@ -297,7 +297,7 @@ public class CordovaWebView extends WebView {
/**
* Load the url into the webview.
*
*
* @param url
*/
@Override
@@ -323,7 +323,7 @@ public class CordovaWebView extends WebView {
/**
* Load the url into the webview after waiting for period of time.
* This is used to display the splashscreen for certain amount of time.
*
*
* @param url
* @param time The number of ms to wait before loading webview
*/
@@ -342,7 +342,7 @@ public class CordovaWebView extends WebView {
/**
* Load the url into the webview.
*
*
* @param url
*/
public void loadUrlIntoView(final String url) {
@@ -411,7 +411,7 @@ public class CordovaWebView extends WebView {
/**
* Load URL in webview.
*
*
* @param url
*/
private void loadUrlNow(String url) {
@@ -422,7 +422,7 @@ public class CordovaWebView extends WebView {
/**
* Load the url into the webview after waiting for period of time.
* This is used to display the splashscreen for certain amount of time.
*
*
* @param url
* @param time The number of ms to wait before loading webview
*/
@@ -449,7 +449,7 @@ public class CordovaWebView extends WebView {
/**
* Send JavaScript statement back to JavaScript.
* (This is a convenience method)
*
*
* @param message
*/
public void sendJavascript(String statement) {
@@ -459,8 +459,8 @@ public class CordovaWebView extends WebView {
}
/**
* Send a message to all plugins.
*
* Send a message to all plugins.
*
* @param id The message id
* @param data The message data
*/
@@ -470,8 +470,8 @@ public class CordovaWebView extends WebView {
}
}
/**
* Returns the top url on the stack without removing it from
/**
* Returns the top url on the stack without removing it from
* the stack.
*/
public String peekAtUrlStack() {
@@ -483,7 +483,7 @@ public class CordovaWebView extends WebView {
/**
* Add a url to the stack
*
*
* @param url
*/
public void pushUrl(String url) {
@@ -492,7 +492,7 @@ public class CordovaWebView extends WebView {
/**
* Go to previous page in history. (We manage our own history)
*
*
* @return true if we went back, false if we are already at top
*/
public boolean backHistory() {
@@ -517,7 +517,7 @@ public class CordovaWebView extends WebView {
/**
* Return true if there is a history item.
*
*
* @return
*/
public boolean canGoBack() {
@@ -532,7 +532,7 @@ public class CordovaWebView extends WebView {
/**
* Load the specified URL in the Cordova webview or a new browser instance.
*
*
* NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
*
* @param url The url to load.
@@ -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;
@@ -625,6 +630,7 @@ public class CordovaWebView extends WebView {
String value = xml.getAttributeValue(null, "value");
LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
Log.d("CordovaLog", "Found preference for " + name + "=" + value);
// Save preferences in Intent
this.cordova.getActivity().getIntent().putExtra(name, value);
@@ -640,7 +646,7 @@ public class CordovaWebView extends WebView {
}
// Init preferences
if ("true".equals(this.getProperty("useBrowserHistory", "true"))) {
if ("true".equals(this.getProperty("useBrowserHistory", "false"))) {
this.useBrowserHistory = true;
}
else {
@@ -655,7 +661,7 @@ public class CordovaWebView extends WebView {
/**
* Get string property for activity.
*
*
* @param name
* @param defaultValue
* @return
@@ -671,11 +677,10 @@ public class CordovaWebView extends WebView {
}
return p.toString();
}
/*
* onKeyDown
* onKeyDown
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
@@ -695,16 +700,17 @@ 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)
{
Log.d(TAG, "KeyDown has been triggered on the view");
// If back key
@@ -719,7 +725,7 @@ public class CordovaWebView extends WebView {
if (this.backHistory()) {
return true;
}
// If not, then invoke default behaviour
// If not, then invoke default behaviour
else {
//this.activityState = ACTIVITY_EXITING;
return false;
@@ -739,18 +745,20 @@ public class CordovaWebView extends WebView {
else if(keyUpCodes.contains(keyCode))
{
//What the hell should this do?
return super.onKeyUp(keyCode, event);
}
Log.d(TAG, "KeyUp has been triggered on the view");
return false;
}
public void bindButton(boolean override)
{
this.bound = override;
}
public void bindButton(String button, boolean override) {
// TODO Auto-generated method stub
if (button.compareTo("volumeup")==0) {
@@ -760,7 +768,7 @@ public class CordovaWebView extends WebView {
keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN);
}
}
public void bindButton(int keyCode, boolean keyDown, boolean override) {
if(keyDown)
{
@@ -771,4 +779,9 @@ public class CordovaWebView extends WebView {
keyUpCodes.add(keyCode);
}
}
public boolean isBackButtonBound()
{
return this.bound;
}
}

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.0rc1"; // Cordova version
public static String cordovaVersion = "2.0.0rc1"; // Cordova version
public static String platform = "Android"; // Device OS
public static String uuid; // Device UUID

View File

@@ -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.
@@ -940,7 +945,8 @@ public class DroidGap extends Activity implements CordovaInterface {
* @return
*/
public Context getContext() {
return this.getContext();
LOG.d(TAG, "This will be deprecated December 2012");
return this;
}
/**
@@ -965,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;
}

View File

@@ -162,4 +162,24 @@ public class ExifHelper {
this.outFile.saveAttributes();
}
public int getOrientation() {
int o = Integer.parseInt(this.orientation);
if (o == ExifInterface.ORIENTATION_NORMAL) {
return 0;
} else if (o == ExifInterface.ORIENTATION_ROTATE_90) {
return 90;
} else if (o == ExifInterface.ORIENTATION_ROTATE_180) {
return 180;
} else if (o == ExifInterface.ORIENTATION_ROTATE_270) {
return 270;
} else {
return 0;
}
}
public void resetOrientation() {
this.orientation = "" + ExifInterface.ORIENTATION_NORMAL;
}
}

View File

@@ -29,6 +29,7 @@ import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
@@ -73,7 +74,7 @@ public class FileTransfer extends Plugin {
String source = null;
String target = null;
try {
source = args.getString(0);
source = URLDecoder.decode(args.getString(0));
target = args.getString(1);
} catch (JSONException e) {
Log.d(LOG_TAG, "Missing source or target");
@@ -321,7 +322,7 @@ public class FileTransfer extends Plugin {
} catch (Throwable t) {
// Shouldn't happen, but will
JSONObject error = createFileTransferError(CONNECTION_ERR, source, target, conn);
Log.wtf(LOG_TAG, error.toString(), t);
Log.e(LOG_TAG, error.toString(), t);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} finally {
if (conn != null) {

View File

@@ -19,6 +19,7 @@
package org.apache.cordova.api;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
/**
@@ -51,7 +52,10 @@ public interface CordovaInterface {
* @return
*/
public abstract Activity getActivity();
@Deprecated
public abstract Context getContext();
@Deprecated
public abstract void cancelLoadUrl();

View File

@@ -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();
}
}

View File

@@ -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) {

View File

@@ -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, "=====================================================================================");
}
}

View File

@@ -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,

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

View File

@@ -1,53 +0,0 @@
# How to use Cordova as a component #
Beginning in Cordova 1.8, with the assistance of the CordovaActivity, you can use Cordova as a component in your Android applications. This component is known in Android
as the CordovaWebView, and new Cordova-based applications from 1.8 and greater will be using the CordovaWebView as its main view, whether the legacy DroidGap approach is
used or not.
The pre-requisites are the same as the pre-requisites for Android application development. It is assumed that you are familiar with Android Development. If not, please
look at the Getting Started guide to developing an Cordova Application and start there before continuing with this approach. Since this is not the main method that people use
to run applications, the instructions are currently manual. In the future, we may try to further automate the project generation.
## Pre-requisites ##
1. **Cordova 1.8** or greater downloaded
2. Android SDK updated with 15
## Guide to using CordovaWebView in an Android Project ##
1. Use bin/create to fetch the commons-codec-1.6.jar
2. Go into framework and run ant jar to build the cordova jar (currently cordova-1.8.jar at the time of writing)
3. Copy the cordova jar into your Android project libs directory
4. Edit your main.xml to look similar the following. The layout_height, layout_width and ID can be modified to suit your application:
` <org.apache.cordova.CordovaWebView$
android:id="@+id/tutorialView"$
android:layout_width="match_parent"$
android:layout_height="match_parent" />$
`
5. Modify your activity so that it implements the CordovaInterface. It is recommended that you implement the methods that are included. You may wish to copy the methods from framework/src/org/apache/cordova/DroidGap.java, or you may wish to implement your own methods. Below is a fragment of code from a basic application that uses the interface:
`
public class CordovaViewTestActivity extends Activity implements CordovaInterface {$
CordovaWebView phoneGap;$
$
/** Called when the activity is first created. */$
@Override$
public void onCreate(Bundle savedInstanceState) {$
super.onCreate(savedInstanceState);$
setContentView(R.layout.main);$
$
phoneGap = (CordovaWebView) findViewById(R.id.phoneGapView);$
$
phoneGap.loadUrl("file:///android_asset/www/index.html");$
}$
`
6. Copy the HTML and Javascript used to the assets directory of your Android project
7. Copy cordova.xml and plugins.xml from framework/res/xml to the framework/res/xml in your project

View File

@@ -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>

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>');

View File

@@ -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");
}
}
}

View File

@@ -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;
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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");
}
}
}
}

View File

@@ -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");
}
}
}