mirror of
https://github.com/apache/cordova-android.git
synced 2026-04-23 00:00:09 +08:00
Compare commits
109 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 774d21747a | |||
| 12e5b39c05 | |||
| 4d5e452ece | |||
| 1ba3ecbef3 | |||
| db6695cb02 | |||
| b3f5e039f2 | |||
| c3e17fb185 | |||
| f7ae7fe43a | |||
| e07822350e | |||
| 07439ff99c | |||
| f111c245c1 | |||
| c3502da4a0 | |||
| 4012108d48 | |||
| 4a0605e09b | |||
| 250380d73e | |||
| b30f5d782d | |||
| b00cd9b557 | |||
| e11f8f646b | |||
| 92b1de8cf8 | |||
| bbafe53a2b | |||
| e239fd970f | |||
| 7fa4515c28 | |||
| b40eb0a454 | |||
| 5e3e9ddb8e | |||
| a9a5284a67 | |||
| afe504dbbf | |||
| 0c484ddcf7 | |||
| 8d0e80620a | |||
| 1d28506b09 | |||
| 1b33dbe2ae | |||
| 80654c059d | |||
| 999c548e6e | |||
| e42913ae8a | |||
| ee07cbecba | |||
| fffaa9bced | |||
| 6195b2c99d | |||
| 2dc0727e36 | |||
| a219feaa60 | |||
| f3a09da340 | |||
| 946e345a3f | |||
| 6cb8d11b22 | |||
| fdcf9c5327 | |||
| 45c714cbb5 | |||
| 7352a309a0 | |||
| b297fe6f59 | |||
| e575212c49 | |||
| c52dc10c9e | |||
| d35c913249 | |||
| 9bac59b952 | |||
| 5016253922 | |||
| 03893071fc | |||
| d3dc94c04b | |||
| af0feabb6a | |||
| 81ab0a414f | |||
| ecd6ca0172 | |||
| db7ee192f7 | |||
| 2ec0b601fa | |||
| 79feb6d5d2 | |||
| a29b8e5b36 | |||
| 9ef487a7a5 | |||
| 563fa46ba4 | |||
| 7865c06863 | |||
| 3d53b9244d | |||
| f2afa4dd50 | |||
| 893ecec55e | |||
| 401584dbd8 | |||
| b234b0bded | |||
| b9b2c6a013 | |||
| 1d2efa0d25 | |||
| 93ec092eaf | |||
| 29ae492983 | |||
| b9f6a59a20 | |||
| d74551216f | |||
| d4302ae51b | |||
| 9d5fb0b201 | |||
| e0a5fe4002 | |||
| f9d9a0a4bd | |||
| 78f0c7b119 | |||
| c6d8343de2 | |||
| 0ccd11e587 | |||
| b486711d68 | |||
| 2eb4c5e960 | |||
| 85aa740c98 | |||
| 6415848383 | |||
| beb9460538 | |||
| c030770be7 | |||
| 0180342dff | |||
| b97748d3dc | |||
| 9d4977db00 | |||
| f095284faa | |||
| 401c2f42f9 | |||
| eb0348d47c | |||
| 1f46240ba9 | |||
| 14870726e0 | |||
| c7d6a2eecb | |||
| ce61eb2174 | |||
| f3df21ef0a | |||
| 5eb554e008 | |||
| 3ea72e5d21 | |||
| 762854ad7a | |||
| 10465066ee | |||
| 0cf9f51816 | |||
| 3d5e2340ca | |||
| d9e7984279 | |||
| e5b9900d3b | |||
| fc3f1431b2 | |||
| c8bf2f4cb1 | |||
| d16555ec4b | |||
| 3c9415b1c2 |
+12
-1
@@ -2,6 +2,7 @@
|
||||
default.properties
|
||||
gen
|
||||
assets/www/cordova.js
|
||||
framework/assets/www/.tmp*
|
||||
local.properties
|
||||
framework/proguard.cfg
|
||||
framework/cordova.jar
|
||||
@@ -12,9 +13,19 @@ framework/test/org/apache/cordova/*.class
|
||||
framework/assets/www/.DS_Store
|
||||
framework/assets/www/cordova-*.js
|
||||
framework/assets/www/phonegap-*.js
|
||||
framework/libs
|
||||
example
|
||||
./test
|
||||
tmp
|
||||
test/bin
|
||||
test/assets/www/.tmp*
|
||||
tmp/**
|
||||
*.tmp
|
||||
test/libs/*.jar
|
||||
bin/node_modules
|
||||
.metadata
|
||||
*.bak
|
||||
tmp/**/*
|
||||
*.swp
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
|
||||
+55
-43
@@ -31,14 +31,14 @@ then
|
||||
fi
|
||||
|
||||
BUILD_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
VERSION=$(cat $BUILD_PATH/VERSION)
|
||||
VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||
|
||||
PROJECT_PATH=${1:-"./example"}
|
||||
PROJECT_PATH=${1:-'./example'}
|
||||
PACKAGE=${2:-"org.apache.cordova.example"}
|
||||
ACTIVITY=${3:-"cordovaExample"}
|
||||
|
||||
# clobber any existing example
|
||||
if [ -d $PROJECT_PATH ]
|
||||
if [ -d "$PROJECT_PATH" ]
|
||||
then
|
||||
echo "Project already exists! Delete and recreate"
|
||||
exit 1
|
||||
@@ -46,21 +46,34 @@ fi
|
||||
|
||||
# cleanup after exit and/or on error
|
||||
function on_exit {
|
||||
# [ -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
|
||||
if [ -f $BUILD_PATH/framework/assets/www/cordova-$VERSION.js ]
|
||||
# [ -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
|
||||
if [ -f "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js ]
|
||||
then
|
||||
rm $BUILD_PATH/framework/assets/www/cordova-$VERSION.js
|
||||
rm "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js
|
||||
fi
|
||||
if [ -f $BUILD_PATH/framework/cordova-$VERSION.jar ]
|
||||
if [ -f "$BUILD_PATH"/framework/cordova-$VERSION.jar ]
|
||||
then
|
||||
rm $BUILD_PATH/framework/cordova-$VERSION.jar
|
||||
rm "$BUILD_PATH"/framework/cordova-$VERSION.jar
|
||||
fi
|
||||
}
|
||||
|
||||
function on_error {
|
||||
echo "An error occured. Deleting project..."
|
||||
[ -d $PROJECT_PATH ] && rm -rf $PROJECT_PATH
|
||||
[ -d "$PROJECT_PATH" ] && rm -rf "$PROJECT_PATH"
|
||||
}
|
||||
|
||||
function replace {
|
||||
local pattern=$1
|
||||
local filename=$2
|
||||
# Mac OS X requires -i argument
|
||||
if [[ "$OSTYPE" =~ "darwin" ]]
|
||||
then
|
||||
/usr/bin/sed -i '' -e $pattern "$filename"
|
||||
elif [[ "$OSTYPE" =~ "linux" ]]
|
||||
then
|
||||
/bin/sed -i -e $pattern "$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
# we do not want the script to silently fail
|
||||
@@ -69,66 +82,65 @@ trap on_exit EXIT
|
||||
|
||||
ANDROID_BIN=$( which android )
|
||||
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
||||
ACTIVITY_PATH=$PROJECT_PATH/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
||||
MANIFEST_PATH=$PROJECT_PATH/AndroidManifest.xml
|
||||
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
||||
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 ]
|
||||
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
|
||||
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
|
||||
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
|
||||
(cd $BUILD_PATH/framework && ant jar &> /dev/null )
|
||||
(cd "$BUILD_PATH"/framework && ant jar &> /dev/null )
|
||||
fi
|
||||
|
||||
|
||||
# create new android project
|
||||
$ANDROID_BIN create project --target $TARGET --path $PROJECT_PATH --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
||||
$ANDROID_BIN create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
||||
|
||||
# copy project template
|
||||
cp -r $BUILD_PATH/bin/templates/project/assets $PROJECT_PATH
|
||||
cp -r $BUILD_PATH/bin/templates/project/res $PROJECT_PATH
|
||||
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
|
||||
if [ -d $BUILD_PATH/framework ]
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
cp "$BUILD_PATH"/bin/templates/project/Activity.java "$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
|
||||
cp "$BUILD_PATH"/bin/templates/project/AndroidManifest.xml "$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
|
||||
cp $BUILD_PATH/bin/templates/cordova/appinfo.jar $PROJECT_PATH/cordova/appinfo.jar
|
||||
cp $BUILD_PATH/bin/templates/cordova/cordova $PROJECT_PATH/cordova/cordova
|
||||
cp $BUILD_PATH/bin/templates/cordova/debug $PROJECT_PATH/cordova/debug
|
||||
cp $BUILD_PATH/bin/templates/cordova/clean $PROJECT_PATH/cordova/clean
|
||||
cp $BUILD_PATH/bin/templates/cordova/log $PROJECT_PATH/cordova/log
|
||||
cp $BUILD_PATH/bin/templates/cordova/emulate $PROJECT_PATH/cordova/emulate
|
||||
cp $BUILD_PATH/bin/templates/cordova/BOOM $PROJECT_PATH/cordova/BOOM
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/debug "$PROJECT_PATH"/cordova/debug
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/emulate "$PROJECT_PATH"/cordova/emulate
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/BOOM "$PROJECT_PATH"/cordova/BOOM
|
||||
|
||||
+2
-4
@@ -154,8 +154,7 @@ 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');
|
||||
exec('%comspec% /c copy '+ROOT+'\\framework\\res\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
||||
} else {
|
||||
// copy in cordova.js
|
||||
exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||
@@ -163,8 +162,7 @@ if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
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');
|
||||
exec('%comspec% /c copy '+ROOT+'\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
||||
}
|
||||
|
||||
// copy cordova scripts
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:debuggable="true">
|
||||
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@@ -57,5 +57,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="5" />
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
</manifest>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
html,
|
||||
body {
|
||||
height:100%;
|
||||
font-size:12px;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
html {
|
||||
display:table;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color:#A7A7A7;
|
||||
background-image:linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-webkit-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-ms-linear-gradient(top, #A7A7A7 0%, #E4E4E4 51%);
|
||||
background-image:-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0, #A7A7A7),
|
||||
color-stop(0.51, #E4E4E4)
|
||||
);
|
||||
display:table-cell;
|
||||
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
text-transform:uppercase;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
.app {
|
||||
background-image:url(../img/cordova.png);
|
||||
background-repeat:no-repeat;
|
||||
margin:0px auto;
|
||||
width:275px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:2em;
|
||||
font-weight:300;
|
||||
margin:0px;
|
||||
overflow:visible;
|
||||
padding:0px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.status {
|
||||
background-color:#333333;
|
||||
border-radius:4px;
|
||||
-webkit-border-radius:4px;
|
||||
color:#FFFFFF;
|
||||
font-size:1em;
|
||||
margin:0px auto;
|
||||
padding:2px 10px;
|
||||
text-align:center;
|
||||
width:100%;
|
||||
max-width:175px;
|
||||
}
|
||||
|
||||
.status.complete {
|
||||
background-color:#4B946A;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display:none;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation:fade 3000ms infinite;
|
||||
-webkit-animation:fade 3000ms infinite;
|
||||
}
|
||||
|
||||
/* portrait */
|
||||
/* @media screen and (max-aspect-ratio: 1/1) */
|
||||
.app {
|
||||
background-position:center top;
|
||||
height:100px; /* adds enough room for text */
|
||||
padding:180px 0px 0px 0px; /* background height - shadow offset */
|
||||
}
|
||||
|
||||
/* lanscape (when wide enough) */
|
||||
@media screen and (min-aspect-ratio: 1/1) and (min-width:445px) {
|
||||
.app {
|
||||
background-position:left center;
|
||||
height:140px; /* height + padding = background image size */
|
||||
padding-left:170px; /* background width */
|
||||
padding-top:60px; /* center the text */
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -1,60 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=320; user-scalable=no" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title">
|
||||
<script type="text/javascript" charset="utf-8" src="cordova-1.9.0.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
<h1>Welcome to Cordova!</h1>
|
||||
<h2>this file is located at assets/www/index.html</h2>
|
||||
<div id="info">
|
||||
<h4>Platform: <span id="platform"> </span>, Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span>, Name: <span id="name"> </span></h4>
|
||||
<h4>Width: <span id="width"> </span>, Height: <span id="height">
|
||||
</span>, Color Depth: <span id="colorDepth"></span></h4>
|
||||
</div>
|
||||
<dl id="accel-data">
|
||||
<dt>X:</dt><dd id="x"> </dd>
|
||||
<dt>Y:</dt><dd id="y"> </dd>
|
||||
<dt>Z:</dt><dd id="z"> </dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="toggleAccel();">Toggle Accelerometer</a>
|
||||
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
|
||||
<a href="tel:411" class="btn large">Call 411</a>
|
||||
<a href="#" class="btn large" onclick="beep();">Beep</a>
|
||||
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
|
||||
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
|
||||
<a href="#" class="btn large" onclick="get_contacts();return false;">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();return false;">Check Network</a>
|
||||
<dl>
|
||||
<dt>Compass Heading:</dt><dd id="h">Off</dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="toggleCompass();return false;">Toggle Compass</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</body>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name = "format-detection" content = "telephone=no"/>
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width;" />
|
||||
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
||||
<title>Hello Cordova</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<h1>Apache Cordova</h1>
|
||||
<div id="deviceready">
|
||||
<p class="status pending blink">Connecting to Device</p>
|
||||
<p class="status complete blink hide">Device is Ready</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="cordova-2.1.0rc2.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
<script type="text/javascript">
|
||||
app.initialize();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
var app = {
|
||||
initialize: function() {
|
||||
this.bind();
|
||||
},
|
||||
bind: function() {
|
||||
document.addEventListener('deviceready', this.deviceready, false);
|
||||
},
|
||||
deviceready: function() {
|
||||
// note that this is an event handler so the scope is that of the event
|
||||
// so we need to call app.report(), and not this.report()
|
||||
app.report('deviceready');
|
||||
},
|
||||
report: function(id) {
|
||||
console.log("report:" + id);
|
||||
// hide the .pending <p> and show the .complete <p>
|
||||
document.querySelector('#' + id + ' .pending').className += ' hide';
|
||||
var completeElem = document.querySelector('#' + id + ' .complete');
|
||||
completeElem.className = completeElem.className.split('hide').join('');
|
||||
}
|
||||
};
|
||||
@@ -4,5 +4,6 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="lib" path="libs/commons-codec-1.6.jar"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:debuggable="true">
|
||||
<activity android:name=".StandAlone" android:windowSoftInputMode="adjustPan"
|
||||
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
|
||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
@@ -64,5 +64,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
<uses-sdk android:minSdkVersion="7" />
|
||||
</manifest>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="cordova-1.9.0.js"></script>
|
||||
<script src="cordova-2.1.0rc2.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:15
|
||||
target=Google Inc.:Google APIs:16
|
||||
apk-configurations=
|
||||
renderscript.opt.level=O0
|
||||
android.library=true
|
||||
|
||||
Executable → Regular
+17
@@ -17,6 +17,20 @@
|
||||
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"/>
|
||||
@@ -35,4 +49,7 @@
|
||||
<plugin name="Capture" value="org.apache.cordova.Capture"/>
|
||||
<plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
|
||||
<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
|
||||
<plugin name="Echo" value="org.apache.cordova.Echo"/>
|
||||
</plugins>
|
||||
</cordova>
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?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" />
|
||||
</cordova>
|
||||
|
||||
|
||||
|
||||
@@ -215,7 +215,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
|
||||
|
||||
// Save time that event was received
|
||||
this.timestamp = System.nanoTime();
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.x = event.values[0];
|
||||
this.y = event.values[1];
|
||||
this.z = event.values[2];
|
||||
|
||||
@@ -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");
|
||||
@@ -173,7 +176,11 @@ public class App extends Plugin {
|
||||
* This is the same as pressing the backbutton on Android device.
|
||||
*/
|
||||
public void backHistory() {
|
||||
this.webView.backHistory();
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.backHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Executable → Regular
+16
-11
@@ -20,6 +20,7 @@ package org.apache.cordova;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
@@ -66,13 +67,13 @@ public class AudioHandler extends Plugin {
|
||||
|
||||
try {
|
||||
if (action.equals("startRecordingAudio")) {
|
||||
this.startRecordingAudio(args.getString(0), args.getString(1));
|
||||
this.startRecordingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
else if (action.equals("stopRecordingAudio")) {
|
||||
this.stopRecordingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("startPlayingAudio")) {
|
||||
this.startPlayingAudio(args.getString(0), args.getString(1));
|
||||
this.startPlayingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
else if (action.equals("seekToAudio")) {
|
||||
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||
@@ -96,6 +97,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 = FileUtils.stripFileProtocol(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 +156,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,12 +199,11 @@ 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 = this.players.get(id);
|
||||
if ( audio == null) {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
AudioPlayer audio = new AudioPlayer(this, id);
|
||||
this.players.put(id, audio);
|
||||
audio.startRecording(file);
|
||||
}
|
||||
|
||||
@@ -209,7 +215,6 @@ public class AudioHandler extends Plugin {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopRecording();
|
||||
this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
Executable → Regular
+228
-142
@@ -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.
|
||||
@@ -42,14 +44,19 @@ import java.io.IOException;
|
||||
*/
|
||||
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_STARTING,
|
||||
MEDIA_RUNNING,
|
||||
MEDIA_PAUSED,
|
||||
MEDIA_STOPPED,
|
||||
MEDIA_LOADING
|
||||
};
|
||||
|
||||
private static final String LOG_TAG = "AudioPlayer";
|
||||
|
||||
// AudioPlayer message ids
|
||||
private static int MEDIA_STATE = 1;
|
||||
@@ -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 MediaPlayer mPlayer = null; // Audio player object
|
||||
private boolean prepareOnly = false;
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
|
||||
private MediaPlayer player = null; // Audio player object
|
||||
private boolean prepareOnly = true; // playback after file prepare flag
|
||||
private int seekOnPrepared = 0; // seek to this location once media is prepared
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -82,14 +92,18 @@ 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;
|
||||
this.recorder = new MediaRecorder();
|
||||
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.3gp";
|
||||
} else {
|
||||
this.tempFile = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording.mp3";
|
||||
this.tempFile = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording.3gp";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,13 +111,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,14 +132,13 @@ 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) {
|
||||
break;
|
||||
case NONE:
|
||||
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);
|
||||
@@ -133,7 +146,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
try {
|
||||
this.recorder.prepare();
|
||||
this.recorder.start();
|
||||
this.setState(MEDIA_RUNNING);
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
@@ -141,8 +154,8 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
e.printStackTrace();
|
||||
}
|
||||
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,10 +184,11 @@ 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.recorder.reset();
|
||||
this.moveFile(this.audioFile);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -183,81 +197,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 != null) {
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //insures this is always reset
|
||||
} else {
|
||||
this.prepareOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,11 +220,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 +236,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 != null) {
|
||||
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 +250,14 @@ 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.pause();
|
||||
this.player.seekTo(0);
|
||||
Log.d(LOG_TAG, "stopPlaying is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state);
|
||||
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,11 @@ 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) {
|
||||
Log.d(LOG_TAG, "on completion is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -317,8 +278,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 +320,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 +338,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);
|
||||
// seek to any location received while not prepared
|
||||
this.seekToPlaying(this.seekOnPrepared);
|
||||
// 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);
|
||||
this.seekOnPrepared = 0; //reset only when played
|
||||
} else {
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
}
|
||||
|
||||
// Save off duration
|
||||
this.duration = getDurationInSeconds();
|
||||
this.prepareOnly = false;
|
||||
// reset prepare only flag
|
||||
this.prepareOnly = true;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,23 +368,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 +396,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 + ", " + 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 +431,120 @@ 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) {
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
return false;
|
||||
case MEDIA_LOADING:
|
||||
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING instead
|
||||
Log.d(LOG_TAG, "AudioPlayer Loading: startPlaying() called during media preparation: " + STATE.MEDIA_STARTING.ordinal());
|
||||
this.prepareOnly = false;
|
||||
return false;
|
||||
case MEDIA_STARTING:
|
||||
case MEDIA_RUNNING:
|
||||
case MEDIA_PAUSED:
|
||||
return true;
|
||||
case MEDIA_STOPPED:
|
||||
//if we are readying the same file
|
||||
if (this.audioFile.compareTo(file) == 0) {
|
||||
//reset the audio file
|
||||
player.seekTo(0);
|
||||
player.pause();
|
||||
return true;
|
||||
} else {
|
||||
//reset the player
|
||||
this.player.reset();
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
this.handler.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,10 +56,12 @@ public class CallbackServer implements Runnable {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "CallbackServer";
|
||||
|
||||
private ServerSocket waitSocket;
|
||||
/**
|
||||
* The list of JavaScript statements to be sent to JavaScript.
|
||||
* This can be null when there are no messages available.
|
||||
*/
|
||||
private LinkedList<String> javascript;
|
||||
private NativeToJsMessageQueue jsMessageQueue;
|
||||
|
||||
/**
|
||||
* The port to listen on.
|
||||
@@ -76,10 +78,6 @@ public class CallbackServer implements Runnable {
|
||||
*/
|
||||
private boolean active;
|
||||
|
||||
/**
|
||||
* Indicates that the JavaScript statements list is empty
|
||||
*/
|
||||
private boolean empty;
|
||||
|
||||
/**
|
||||
* Indicates that polling should be used instead of XHR.
|
||||
@@ -97,9 +95,7 @@ public class CallbackServer implements Runnable {
|
||||
public CallbackServer() {
|
||||
//Log.d(LOG_TAG, "CallbackServer()");
|
||||
this.active = false;
|
||||
this.empty = true;
|
||||
this.port = 0;
|
||||
this.javascript = new LinkedList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,10 +108,8 @@ public class CallbackServer implements Runnable {
|
||||
*/
|
||||
public void init(String url) {
|
||||
//System.out.println("CallbackServer.start("+url+")");
|
||||
this.active = false;
|
||||
this.empty = true;
|
||||
this.stopServer();
|
||||
this.port = 0;
|
||||
this.javascript = new LinkedList<String>();
|
||||
|
||||
// Determine if XHR or polling is to be used
|
||||
if ((url != null) && !url.startsWith("file://")) {
|
||||
@@ -132,16 +126,6 @@ public class CallbackServer implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-init when loading a new HTML page into webview.
|
||||
*
|
||||
* @param url The URL of the Cordova app being loaded
|
||||
*/
|
||||
public void reinit(String url) {
|
||||
this.stopServer();
|
||||
this.init(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if polling is being used instead of XHR.
|
||||
* @return
|
||||
@@ -200,7 +184,7 @@ public class CallbackServer implements Runnable {
|
||||
try {
|
||||
this.active = true;
|
||||
String request;
|
||||
ServerSocket waitSocket = new ServerSocket(0);
|
||||
waitSocket = new ServerSocket(0);
|
||||
this.port = waitSocket.getLocalPort();
|
||||
//Log.d(LOG_TAG, "CallbackServer -- using port " +this.port);
|
||||
this.token = java.util.UUID.randomUUID().toString();
|
||||
@@ -223,11 +207,19 @@ public class CallbackServer implements Runnable {
|
||||
// Must have security token
|
||||
if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
|
||||
//Log.d(LOG_TAG, "CallbackServer -- Processing GET request");
|
||||
String js = null;
|
||||
|
||||
// Wait until there is some data to send, or send empty data every 10 sec
|
||||
// to prevent XHR timeout on the client
|
||||
synchronized (this) {
|
||||
while (this.empty) {
|
||||
while (this.active) {
|
||||
if (jsMessageQueue != null) {
|
||||
// TODO(agrieve): Should this use popAll() instead?
|
||||
js = jsMessageQueue.pop();
|
||||
if (js != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
this.wait(10000); // prevent timeout from happening
|
||||
//Log.d(LOG_TAG, "CallbackServer>>> break <<<");
|
||||
@@ -241,17 +233,14 @@ public class CallbackServer implements Runnable {
|
||||
if (this.active) {
|
||||
|
||||
// If no data, then send 404 back to client before it times out
|
||||
if (this.empty) {
|
||||
if (js == null) {
|
||||
//Log.d(LOG_TAG, "CallbackServer -- sending data 0");
|
||||
response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
|
||||
}
|
||||
else {
|
||||
//Log.d(LOG_TAG, "CallbackServer -- sending item");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n";
|
||||
String js = this.getJavascript();
|
||||
if (js != null) {
|
||||
response += encode(js, "UTF-8");
|
||||
}
|
||||
response += encode(js, "UTF-8");
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -289,6 +278,8 @@ public class CallbackServer implements Runnable {
|
||||
if (this.active) {
|
||||
this.active = false;
|
||||
|
||||
try { waitSocket.close(); } catch (IOException ignore) {}
|
||||
|
||||
// Break out of server wait
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
@@ -303,45 +294,9 @@ public class CallbackServer implements Runnable {
|
||||
this.stopServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of JavaScript statements.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getSize() {
|
||||
public void onNativeToJsMessageAvailable(NativeToJsMessageQueue queue) {
|
||||
synchronized (this) {
|
||||
int size = this.javascript.size();
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next JavaScript statement and remove from list.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getJavascript() {
|
||||
synchronized (this) {
|
||||
if (this.javascript.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
String statement = this.javascript.remove(0);
|
||||
if (this.javascript.size() == 0) {
|
||||
this.empty = true;
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript statement to the list.
|
||||
*
|
||||
* @param statement
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
synchronized (this) {
|
||||
this.javascript.add(statement);
|
||||
this.empty = false;
|
||||
this.jsMessageQueue = queue;
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
//import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
@@ -36,7 +35,6 @@ import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
//import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -90,6 +88,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
private int numPics;
|
||||
|
||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||
private Uri scanMe; // Uri of image to be added to content store
|
||||
|
||||
//This should never be null!
|
||||
//private CordovaInterface cordova;
|
||||
@@ -143,6 +142,15 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
this.correctOrientation = args.getBoolean(8);
|
||||
this.saveToPhotoAlbum = args.getBoolean(9);
|
||||
|
||||
// If the user specifies a 0 or smaller width/height
|
||||
// make it -1 so later comparrisions succeed
|
||||
if (this.targetWidth < 1) {
|
||||
this.targetWidth = -1;
|
||||
}
|
||||
if (this.targetHeight < 1) {
|
||||
this.targetHeight = -1;
|
||||
}
|
||||
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
@@ -265,21 +273,23 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
// 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 {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Bitmap bitmap = null;
|
||||
Uri uri = null;
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
@@ -295,9 +305,8 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
Uri uri;
|
||||
if (!this.saveToPhotoAlbum) {
|
||||
uri = Uri.fromFile(new File("/data/data/" + this.cordova.getActivity().getPackageName() + "/", (new File(FileUtils.stripFileProtocol(this.imageUri.toString()))).getName()));
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
} else {
|
||||
uri = getUriFromMediaStore();
|
||||
}
|
||||
@@ -324,7 +333,6 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
|
||||
if (this.encodingType == JPEG) {
|
||||
String exifPath;
|
||||
if (this.saveToPhotoAlbum) {
|
||||
@@ -338,16 +346,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
}
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
if (saveToPhotoAlbum) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
} else {
|
||||
// If you don't want to save the file to the photo album you need to append a timestamp
|
||||
// to the returned URI to do some cache busting.
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString() + "?" + System.currentTimeMillis()), this.callbackId);
|
||||
}
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
|
||||
this.cleanup(FILE_URI, this.imageUri, bitmap);
|
||||
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
|
||||
bitmap = null;
|
||||
|
||||
} catch (IOException e) {
|
||||
@@ -378,73 +380,86 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
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 sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
|
||||
cols,
|
||||
null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
// 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);
|
||||
// If we don't have a valid image path quit.
|
||||
if (imagePath == null) {
|
||||
this.failPicture("Unable to retreive picture!");
|
||||
return;
|
||||
}
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
this.processPicture(bitmap);
|
||||
}
|
||||
Bitmap bitmap = getScaledBitmap(imagePath);
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
// Do we need to scale the returned file
|
||||
if (this.targetHeight > 0 && this.targetWidth > 0) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(resizePath);
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
OutputStream os = new FileOutputStream(resizePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
// The resized image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.success(new PluginResult(PluginResult.Status.OK, ("file://" + resizePath + "?" + System.currentTimeMillis())), this.callbackId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
if (this.correctOrientation) {
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
|
||||
cols, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
this.processPicture(bitmap);
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
// Do we need to scale the returned file
|
||||
if (this.targetHeight > 0 && this.targetWidth > 0) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(resizePath);
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
OutputStream os = new FileOutputStream(resizePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
// The resized image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.success(new PluginResult(PluginResult.Status.OK, ("file://" + resizePath + "?" + System.currentTimeMillis())), this.callbackId);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
}
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
}
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
@@ -533,20 +548,79 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
return BitmapFactory.decodeFile(imagePath);
|
||||
}
|
||||
|
||||
Bitmap unscaledBitmap = decodeFile(imagePath,
|
||||
this.targetWidth, this.targetHeight);
|
||||
return Bitmap.createScaledBitmap(unscaledBitmap, this.targetWidth, this.targetHeight, true);
|
||||
}
|
||||
|
||||
public static Bitmap decodeFile(String pathName, int dstWidth, int dstHeight) {
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(pathName, options);
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, dstWidth, dstHeight);
|
||||
return BitmapFactory.decodeFile(pathName, options);
|
||||
}
|
||||
BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
// determine the correct aspect ratio
|
||||
int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
|
||||
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintain the aspect ratio so the resulting image does not look smooshed
|
||||
*
|
||||
* @param origWidth
|
||||
* @param origHeight
|
||||
* @return
|
||||
*/
|
||||
public int[] calculateAspectRatio(int origWidth, int origHeight) {
|
||||
int newWidth = this.targetWidth;
|
||||
int newHeight = this.targetHeight;
|
||||
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (newWidth <= 0 && newHeight <= 0) {
|
||||
newWidth = origWidth;
|
||||
newHeight = origHeight;
|
||||
}
|
||||
// Only the width was specified
|
||||
else if (newWidth > 0 && newHeight <= 0) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
}
|
||||
// only the height was specified
|
||||
else if (newWidth <= 0 && newHeight > 0) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
}
|
||||
// If the user specified both a positive width and height
|
||||
// (potentially different aspect ratio) then the width or height is
|
||||
// scaled so that the image fits while maintaining aspect ratio.
|
||||
// Alternatively, the specified width and height could have been
|
||||
// kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
|
||||
// would result in whitespace in the new image.
|
||||
else {
|
||||
double newRatio = newWidth / (double) newHeight;
|
||||
double origRatio = origWidth / (double) origHeight;
|
||||
|
||||
if (origRatio > newRatio) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
} else if (origRatio < newRatio) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
}
|
||||
}
|
||||
|
||||
int[] retval = new int[2];
|
||||
retval[0] = newWidth;
|
||||
retval[1] = newHeight;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out what ratio we can load our image into memory at while still being bigger than
|
||||
* our desired width and height
|
||||
*
|
||||
* @param srcWidth
|
||||
* @param srcHeight
|
||||
* @param dstWidth
|
||||
* @param dstHeight
|
||||
* @return
|
||||
*/
|
||||
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
|
||||
final float srcAspect = (float)srcWidth / (float)srcHeight;
|
||||
final float dstAspect = (float)dstWidth / (float)dstHeight;
|
||||
@@ -574,8 +648,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
/**
|
||||
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
|
||||
* @param newImage
|
||||
*/
|
||||
private void cleanup(int imageType, Uri oldImage, Bitmap bitmap) {
|
||||
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
@@ -585,7 +660,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
checkForDuplicateImage(imageType);
|
||||
// Scan for the gallery to update pic refs in gallery
|
||||
this.scanForGallery();
|
||||
if (this.saveToPhotoAlbum && newImage != null) {
|
||||
this.scanForGallery(newImage);
|
||||
}
|
||||
|
||||
System.gc();
|
||||
}
|
||||
@@ -610,7 +687,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
|
||||
if ((currentNumOfImages - numPics) == diff) {
|
||||
cursor.moveToLast();
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
|
||||
if (diff == 2) {
|
||||
id--;
|
||||
}
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
}
|
||||
@@ -660,18 +740,20 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
}
|
||||
|
||||
private void scanForGallery() {
|
||||
if(this.conn!=null) this.conn.disconnect();
|
||||
private void scanForGallery(Uri newImage) {
|
||||
this.scanMe = newImage;
|
||||
if(this.conn != null) {
|
||||
this.conn.disconnect();
|
||||
}
|
||||
this.conn = new MediaScannerConnection(this.ctx.getActivity().getApplicationContext(), this);
|
||||
conn.connect();
|
||||
}
|
||||
|
||||
public void onMediaScannerConnected() {
|
||||
try{
|
||||
this.conn.scanFile(this.imageUri.toString(), "image/*");
|
||||
this.conn.scanFile(this.scanMe.toString(), "image/*");
|
||||
} catch (java.lang.IllegalStateException e){
|
||||
e.printStackTrace();
|
||||
LOG.d(LOG_TAG, "Can;t scan file in MediaScanner aftering taking picture");
|
||||
LOG.e(LOG_TAG, "Can't scan file in MediaScanner aftering taking picture");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,6 +19,26 @@
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Debug;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -31,27 +51,8 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
//import android.app.Activity;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
//import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ContactAccessor} that uses current Contacts API.
|
||||
@@ -71,6 +72,7 @@ import android.webkit.WebView;
|
||||
* social status updates (see {@link android.provider.ContactsContract.StatusUpdates}).
|
||||
* </ul>
|
||||
*/
|
||||
|
||||
public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
|
||||
/**
|
||||
@@ -150,6 +152,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
|
||||
try {
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (!multiple) {
|
||||
@@ -163,6 +166,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
searchTerm = "%";
|
||||
}
|
||||
|
||||
|
||||
//Log.d(LOG_TAG, "Search Term = " + searchTerm);
|
||||
//Log.d(LOG_TAG, "Field Length = " + fields.length());
|
||||
//Log.d(LOG_TAG, "Fields = " + fields.toString());
|
||||
@@ -181,19 +185,88 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
ContactsContract.Data.CONTACT_ID + " ASC");
|
||||
|
||||
// Create a set of unique ids
|
||||
//Log.d(LOG_TAG, "ID cursor query returns = " + idCursor.getCount());
|
||||
Set<String> contactIds = new HashSet<String>();
|
||||
int idColumn = -1;
|
||||
while (idCursor.moveToNext()) {
|
||||
contactIds.add(idCursor.getString(idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID)));
|
||||
if (idColumn < 0) {
|
||||
idColumn = idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
|
||||
}
|
||||
contactIds.add(idCursor.getString(idColumn));
|
||||
}
|
||||
idCursor.close();
|
||||
|
||||
// Build a query that only looks at ids
|
||||
WhereOptions idOptions = buildIdClause(contactIds, searchTerm);
|
||||
|
||||
// Determine which columns we should be fetching.
|
||||
HashSet<String> columnsToFetch = new HashSet<String>();
|
||||
columnsToFetch.add(ContactsContract.Data.CONTACT_ID);
|
||||
columnsToFetch.add(ContactsContract.Data.RAW_CONTACT_ID);
|
||||
columnsToFetch.add(ContactsContract.Data.MIMETYPE);
|
||||
|
||||
if (isRequired("displayName", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
|
||||
}
|
||||
if (isRequired("name", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.PREFIX);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.SUFFIX);
|
||||
}
|
||||
if (isRequired("phoneNumbers", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Phone._ID);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Phone.TYPE);
|
||||
}
|
||||
if (isRequired("emails", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Email.DATA);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Email.TYPE);
|
||||
}
|
||||
if (isRequired("addresses", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal._ID);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.TYPE);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.STREET);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.CITY);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.REGION);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY);
|
||||
}
|
||||
if (isRequired("organizations", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization._ID);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.TYPE);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.DEPARTMENT);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.COMPANY);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.TITLE);
|
||||
}
|
||||
if (isRequired("ims", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Im._ID);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Im.DATA);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Im.TYPE);
|
||||
}
|
||||
if (isRequired("note", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Note.NOTE);
|
||||
}
|
||||
if (isRequired("nickname", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Nickname.NAME);
|
||||
}
|
||||
if (isRequired("urls", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Website._ID);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Website.URL);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Website.TYPE);
|
||||
}
|
||||
if (isRequired("birthday", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Event.START_DATE);
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Event.TYPE);
|
||||
}
|
||||
if (isRequired("photos", populate)) {
|
||||
columnsToFetch.add(ContactsContract.CommonDataKinds.Photo._ID);
|
||||
}
|
||||
|
||||
// Do the id query
|
||||
Cursor c = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
null,
|
||||
columnsToFetch.toArray(new String[] {}),
|
||||
idOptions.getWhere(),
|
||||
idOptions.getWhereArgs(),
|
||||
ContactsContract.Data.CONTACT_ID + " ASC");
|
||||
@@ -258,11 +331,23 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
JSONArray websites = new JSONArray();
|
||||
JSONArray photos = new JSONArray();
|
||||
|
||||
ArrayList<String> names = new ArrayList<String>();
|
||||
|
||||
// Column indices
|
||||
int colContactId = c.getColumnIndex(ContactsContract.Data.CONTACT_ID);
|
||||
int colRawContactId = c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID);
|
||||
int colMimetype = c.getColumnIndex(ContactsContract.Data.MIMETYPE);
|
||||
int colDisplayName = c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
|
||||
int colNote = c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE);
|
||||
int colNickname = c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME);
|
||||
int colBirthday = c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE);
|
||||
int colEventType = c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE);
|
||||
|
||||
if (c.getCount() > 0) {
|
||||
while (c.moveToNext() && (contacts.length() <= (limit - 1))) {
|
||||
try {
|
||||
contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
|
||||
rawId = c.getString(c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
|
||||
contactId = c.getString(colContactId);
|
||||
rawId = c.getString(colRawContactId);
|
||||
|
||||
// If we are in the first row set the oldContactId
|
||||
if (c.getPosition() == 0) {
|
||||
@@ -300,11 +385,12 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
}
|
||||
|
||||
// Grab the mimetype of the current row as it will be used in a lot of comparisons
|
||||
mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE));
|
||||
mimetype = c.getString(colMimetype);
|
||||
|
||||
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
|
||||
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)));
|
||||
contact.put("displayName", c.getString(colDisplayName));
|
||||
}
|
||||
|
||||
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("name", populate)) {
|
||||
contact.put("name", nameQuery(c));
|
||||
@@ -331,20 +417,20 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("note", populate)) {
|
||||
contact.put("note", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE)));
|
||||
contact.put("note", c.getString(colNote));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("nickname", populate)) {
|
||||
contact.put("nickname", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
|
||||
contact.put("nickname", c.getString(colNickname));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("urls", populate)) {
|
||||
websites.put(websiteQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) {
|
||||
if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
|
||||
&& isRequired("birthday", populate)) {
|
||||
contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
|
||||
if (isRequired("birthday", populate) &&
|
||||
ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(colEventType)) {
|
||||
contact.put("birthday", c.getString(colBirthday));
|
||||
}
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
|
||||
@@ -357,6 +443,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
|
||||
// Set the old contact ID
|
||||
oldContactId = contactId;
|
||||
|
||||
}
|
||||
|
||||
// Push the last contact into the contacts array
|
||||
@@ -944,29 +1031,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 +1077,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 +1123,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 +1179,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 +1229,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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1151,31 +1293,43 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
// 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 +1355,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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1554,7 +1719,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
// 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);
|
||||
|
||||
@@ -20,15 +20,14 @@ package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
//import android.app.Activity;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.AlertDialog;
|
||||
//import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.KeyEvent;
|
||||
//import android.view.View;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
@@ -101,7 +100,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
dlg.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
result.confirm();
|
||||
result.cancel();
|
||||
}
|
||||
});
|
||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
@@ -204,16 +203,23 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
String action = array.getString(1);
|
||||
String callbackId = array.getString(2);
|
||||
boolean async = array.getBoolean(3);
|
||||
String r = this.appView.pluginManager.exec(service, action, callbackId, message, async);
|
||||
result.confirm(r);
|
||||
PluginResult r = this.appView.pluginManager.exec(service, action, callbackId, message, async);
|
||||
result.confirm(r == null ? "" : r.getJSONString());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the native->JS bridge mode.
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) {
|
||||
this.appView.jsMessageQueue.setBridgeMode(Integer.parseInt(message));
|
||||
result.confirm("");
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
||||
String r = this.appView.callbackServer.getJavascript();
|
||||
// TODO(agrieve): Use popAll() here.
|
||||
String r = this.appView.jsMessageQueue.pop();
|
||||
result.confirm(r);
|
||||
}
|
||||
|
||||
@@ -310,6 +316,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||
}
|
||||
|
||||
@TargetApi(8)
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
||||
{
|
||||
|
||||
@@ -30,12 +30,17 @@ import java.util.regex.Pattern;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONException;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
@@ -57,6 +62,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
public PluginManager pluginManager;
|
||||
public CallbackServer callbackServer;
|
||||
private boolean paused;
|
||||
|
||||
|
||||
/** Actvities and other important classes **/
|
||||
@@ -81,6 +87,10 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
private boolean volumeupBound;
|
||||
|
||||
private boolean handleButton = false;
|
||||
|
||||
NativeToJsMessageQueue jsMessageQueue;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -117,7 +127,7 @@ public class CordovaWebView extends WebView {
|
||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
||||
}
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova, this));
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.cordova, this));
|
||||
this.initWebViewClient(this.cordova);
|
||||
this.loadConfiguration();
|
||||
this.setup();
|
||||
}
|
||||
@@ -141,7 +151,7 @@ public class CordovaWebView extends WebView {
|
||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
||||
}
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova, this));
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.cordova, this));
|
||||
this.initWebViewClient(this.cordova);
|
||||
this.loadConfiguration();
|
||||
this.setup();
|
||||
}
|
||||
@@ -154,7 +164,8 @@ public class CordovaWebView extends WebView {
|
||||
* @param defStyle
|
||||
* @param privateBrowsing
|
||||
*/
|
||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) {
|
||||
@TargetApi(11)
|
||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) {
|
||||
super(context, attrs, defStyle, privateBrowsing);
|
||||
if (CordovaInterface.class.isInstance(context))
|
||||
{
|
||||
@@ -165,16 +176,30 @@ public class CordovaWebView extends WebView {
|
||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
||||
}
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova));
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.cordova));
|
||||
this.initWebViewClient(this.cordova);
|
||||
this.loadConfiguration();
|
||||
this.setup();
|
||||
}
|
||||
|
||||
|
||||
private void initWebViewClient(CordovaInterface cordova) {
|
||||
if(android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.cordova, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.setWebViewClient(new IceCreamCordovaWebViewClient(this.cordova, this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize webview.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressLint("NewApi")
|
||||
private void setup() {
|
||||
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
|
||||
|
||||
this.setInitialScale(0);
|
||||
this.setVerticalScrollBarEnabled(false);
|
||||
@@ -186,9 +211,14 @@ public class CordovaWebView extends WebView {
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
|
||||
|
||||
//Set the nav dump for HTC
|
||||
settings.setNavDump(true);
|
||||
//Set the nav dump for HTC 2.x devices (disabling for ICS/Jellybean)
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
settings.setNavDump(true);
|
||||
|
||||
// Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
|
||||
// while we do this
|
||||
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
||||
Level16Apis.enableUniversalAccess(settings);
|
||||
// Enable database
|
||||
settings.setDatabaseEnabled(true);
|
||||
String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
@@ -207,6 +237,22 @@ public class CordovaWebView extends WebView {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
exposeJsInterface();
|
||||
}
|
||||
|
||||
private void exposeJsInterface() {
|
||||
// addJavascriptInterface crashes on the 2.3 emulator.
|
||||
if (Build.VERSION.RELEASE.startsWith("2.3") && Build.MANUFACTURER.equals("unknown")) {
|
||||
Log.i(TAG, "Disabled addJavascriptInterface() bridge callback due to a bug on the 2.3 emulator");
|
||||
return;
|
||||
}
|
||||
this.addJavascriptInterface(new Object() {
|
||||
@SuppressWarnings("unused")
|
||||
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
||||
PluginResult r = pluginManager.exec(service, action, callbackId, arguments, true /* async */);
|
||||
return r == null ? "" : r.getJSONString();
|
||||
}
|
||||
}, "_cordovaExec");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,7 +460,7 @@ public class CordovaWebView extends WebView {
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
private void loadUrlNow(String url) {
|
||||
void loadUrlNow(String url) {
|
||||
LOG.d(TAG, ">>> loadUrlNow()");
|
||||
super.loadUrl(url);
|
||||
}
|
||||
@@ -453,9 +499,7 @@ public class CordovaWebView extends WebView {
|
||||
* @param message
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.sendJavascript(statement);
|
||||
}
|
||||
this.jsMessageQueue.add(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,7 +640,12 @@ public class CordovaWebView extends WebView {
|
||||
* <log level="DEBUG" />
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
int id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName());
|
||||
int id = getResources().getIdentifier("config", "xml", this.cordova.getActivity().getPackageName());
|
||||
if(id == 0)
|
||||
{
|
||||
id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName());
|
||||
Log.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
|
||||
}
|
||||
if (id == 0) {
|
||||
LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
|
||||
return;
|
||||
@@ -676,7 +725,6 @@ public class CordovaWebView extends WebView {
|
||||
/*
|
||||
* onKeyDown
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
@@ -696,18 +744,16 @@ 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
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
// If back key is bound, then send event to JavaScript
|
||||
@@ -740,13 +786,14 @@ 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;
|
||||
//Does webkit change this behaviour?
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
public void bindButton(boolean override)
|
||||
{
|
||||
this.bound = override;
|
||||
@@ -777,4 +824,80 @@ public class CordovaWebView extends WebView {
|
||||
{
|
||||
return this.bound;
|
||||
}
|
||||
|
||||
public void handlePause(boolean keepRunning)
|
||||
{
|
||||
LOG.d(TAG, "Handle the pause");
|
||||
// Send pause event to JavaScript
|
||||
this.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.pluginManager != null) {
|
||||
this.pluginManager.onPause(keepRunning);
|
||||
}
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!keepRunning) {
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.pauseTimers();
|
||||
}
|
||||
paused = true;
|
||||
|
||||
}
|
||||
|
||||
public void handleResume(boolean keepRunning, boolean activityResultKeepRunning)
|
||||
{
|
||||
|
||||
// Send resume event to JavaScript
|
||||
this.loadUrl("javascript:try{cordova.fireDocumentEvent('resume');}catch(e){console.log('exception firing resume event from native');};");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.pluginManager != null) {
|
||||
this.pluginManager.onResume(keepRunning);
|
||||
}
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.resumeTimers();
|
||||
paused = false;
|
||||
}
|
||||
|
||||
public void handleDestroy()
|
||||
{
|
||||
// Send destroy event to JavaScript
|
||||
this.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.loadUrl("about:blank");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.pluginManager != null) {
|
||||
this.pluginManager.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
public void onNewIntent(Intent intent)
|
||||
{
|
||||
//Forward to plugins
|
||||
if (this.pluginManager != null) {
|
||||
this.pluginManager.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPaused()
|
||||
{
|
||||
return paused;
|
||||
}
|
||||
|
||||
public boolean hadKeyEvent() {
|
||||
return handleButton;
|
||||
}
|
||||
|
||||
// Wrapping these functions in their own class prevents warnings in adb like:
|
||||
// VFY: unable to resolve virtual method 285: Landroid/webkit/WebSettings;.setAllowUniversalAccessFromFileURLs
|
||||
@TargetApi(16)
|
||||
private static class Level16Apis {
|
||||
static void enableUniversalAccess(WebSettings settings) {
|
||||
settings.setAllowUniversalAccessFromFileURLs(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ package org.apache.cordova;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
@@ -28,6 +30,7 @@ import org.apache.cordova.api.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
@@ -37,6 +40,7 @@ import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.net.http.SslError;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.webkit.HttpAuthHandler;
|
||||
import android.webkit.SslErrorHandler;
|
||||
@@ -49,7 +53,11 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String TAG = "Cordova";
|
||||
// Disable URL-based exec() bridge by default since it's a bit of a
|
||||
// security concern.
|
||||
private static boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
|
||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||
CordovaInterface cordova;
|
||||
CordovaWebView appView;
|
||||
private boolean doClearHistory = false;
|
||||
@@ -86,6 +94,29 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
this.appView = view;
|
||||
}
|
||||
|
||||
|
||||
// Parses commands sent by setting the webView's URL to:
|
||||
// cdvbrg:service/action/callbackId#jsonArgs
|
||||
private void handleExecUrl(String url) {
|
||||
int idx1 = CORDOVA_EXEC_URL_PREFIX.length();
|
||||
int idx2 = url.indexOf('#', idx1 + 1);
|
||||
int idx3 = url.indexOf('#', idx2 + 1);
|
||||
int idx4 = url.indexOf('#', idx3 + 1);
|
||||
if (idx1 == -1 || idx2 == -1 || idx3 == -1 || idx4 == -1) {
|
||||
Log.e(TAG, "Could not decode URL command: " + url);
|
||||
return;
|
||||
}
|
||||
String service = url.substring(idx1, idx2);
|
||||
String action = url.substring(idx2 + 1, idx3);
|
||||
String callbackId = url.substring(idx3 + 1, idx4);
|
||||
String jsonArgs = url.substring(idx4 + 1);
|
||||
PluginResult r = appView.pluginManager.exec(service, action, callbackId, jsonArgs, true /* async */);
|
||||
String callbackString = r.toCallbackString(callbackId);
|
||||
if (r != null) {
|
||||
appView.sendJavascript(callbackString);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the host application a chance to take over the control when a new url
|
||||
* is about to be loaded in the current WebView.
|
||||
@@ -94,11 +125,15 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
* @param url The url to be loaded.
|
||||
* @return true to override, false for default behavior
|
||||
*/
|
||||
@Override
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
// Check if it's an exec() bridge command message.
|
||||
if (ENABLE_LOCATION_CHANGE_EXEC_MODE && url.startsWith(CORDOVA_EXEC_URL_PREFIX)) {
|
||||
handleExecUrl(url);
|
||||
}
|
||||
|
||||
// First give any plugins the chance to handle the url themselves
|
||||
if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
||||
// Give plugins the chance to handle the url
|
||||
else if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
||||
}
|
||||
|
||||
// If dialing phone (tel:5551212)
|
||||
@@ -230,14 +265,14 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
this.doClearHistory = true;
|
||||
}
|
||||
|
||||
// Create callback server and plugin manager
|
||||
// Flush stale messages.
|
||||
this.appView.jsMessageQueue.reset();
|
||||
|
||||
// Create callback server
|
||||
if (this.appView.callbackServer == null) {
|
||||
this.appView.callbackServer = new CallbackServer();
|
||||
this.appView.callbackServer.init(url);
|
||||
}
|
||||
else {
|
||||
this.appView.callbackServer.reinit(url);
|
||||
}
|
||||
this.appView.callbackServer.init(url);
|
||||
|
||||
// Broadcast message that page has loaded
|
||||
this.appView.postMessage("onPageStarted", url);
|
||||
@@ -346,6 +381,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
* @param handler An SslErrorHandler object that will handle the user's response.
|
||||
* @param error The SSL error object.
|
||||
*/
|
||||
@TargetApi(8)
|
||||
@Override
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
|
||||
@@ -459,43 +495,4 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
this.authenticationTokens.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
if(url.contains("?") || url.contains("#")){
|
||||
return generateWebResourceResponse(url);
|
||||
} else {
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
}
|
||||
}
|
||||
|
||||
private WebResourceResponse generateWebResourceResponse(String url) {
|
||||
final String ANDROID_ASSET = "file:///android_asset/";
|
||||
if (url.startsWith(ANDROID_ASSET)) {
|
||||
String niceUrl = url;
|
||||
niceUrl = url.replaceFirst(ANDROID_ASSET, "");
|
||||
if(niceUrl.contains("?")){
|
||||
niceUrl = niceUrl.split("\\?")[0];
|
||||
}
|
||||
else if(niceUrl.contains("#"))
|
||||
{
|
||||
niceUrl = niceUrl.split("#")[0];
|
||||
}
|
||||
|
||||
String mimetype = null;
|
||||
if(niceUrl.endsWith(".html")){
|
||||
mimetype = "text/html";
|
||||
}
|
||||
|
||||
try {
|
||||
AssetManager assets = cordova.getActivity().getAssets();
|
||||
Uri uri = Uri.parse(niceUrl);
|
||||
InputStream stream = assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
|
||||
WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream);
|
||||
return response;
|
||||
} catch (IOException e) {
|
||||
LOG.e("generateWebResourceResponse", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ import android.telephony.TelephonyManager;
|
||||
public class Device extends Plugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "1.9.0"; // Cordova version
|
||||
public static String cordovaVersion = "2.1.0rc2"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import android.graphics.Color;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
@@ -600,6 +601,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
LOG.d(TAG, "Paused the application!");
|
||||
|
||||
// Don't process pause if shutting down, since onDestroy() will be called
|
||||
if (this.activityState == ACTIVITY_EXITING) {
|
||||
return;
|
||||
@@ -608,21 +611,13 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
if (this.appView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send pause event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.appView.pluginManager != null) {
|
||||
this.appView.pluginManager.onPause(this.keepRunning);
|
||||
else
|
||||
{
|
||||
this.appView.handlePause(this.keepRunning);
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -631,11 +626,9 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
**/
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
//Forward to plugins
|
||||
if ((this.appView != null) && (this.appView.pluginManager != null)) {
|
||||
this.appView.pluginManager.onNewIntent(intent);
|
||||
}
|
||||
if (this.appView != null)
|
||||
this.appView.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -645,6 +638,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
LOG.d(TAG, "Resuming the App");
|
||||
if (this.activityState == ACTIVITY_STARTING) {
|
||||
this.activityState = ACTIVITY_RUNNING;
|
||||
return;
|
||||
@@ -654,13 +648,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send resume event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{cordova.fireDocumentEvent('resume');}catch(e){console.log('exception firing resume event from native');};");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.appView.pluginManager != null) {
|
||||
this.appView.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
|
||||
}
|
||||
this.appView.handleResume(this.keepRunning, this.activityResultKeepRunning);
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning || this.activityResultKeepRunning) {
|
||||
@@ -670,9 +658,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
this.keepRunning = this.activityResultKeepRunning;
|
||||
this.activityResultKeepRunning = false;
|
||||
}
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,18 +669,11 @@ 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
|
||||
this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.appView.pluginManager != null) {
|
||||
this.appView.pluginManager.onDestroy();
|
||||
}
|
||||
appView.handleDestroy();
|
||||
}
|
||||
else {
|
||||
this.endActivity();
|
||||
@@ -736,8 +714,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
* @param message
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
if (this.appView != null && this.appView.callbackServer != null) {
|
||||
this.appView.callbackServer.sendJavascript(statement);
|
||||
if (this.appView != null) {
|
||||
this.appView.jsMessageQueue.add(statement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -762,10 +740,10 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop spinner.
|
||||
* Stop spinner - Must be called from UI thread
|
||||
*/
|
||||
public void spinnerStop() {
|
||||
if (this.spinnerDialog != null) {
|
||||
if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
@@ -834,9 +812,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
|
||||
final DroidGap me = this;
|
||||
|
||||
// Stop "app loading" spinner if showing
|
||||
this.spinnerStop();
|
||||
|
||||
// If errorUrl specified, then load it
|
||||
final String errorUrl = me.getStringProperty("errorUrl", null);
|
||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||
@@ -844,6 +819,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
// Load URL on UI thread
|
||||
me.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
// Stop "app loading" spinner if showing
|
||||
me.spinnerStop();
|
||||
me.appView.showWebPage(errorUrl, false, true, null);
|
||||
}
|
||||
});
|
||||
@@ -941,7 +918,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
*/
|
||||
public Context getContext() {
|
||||
LOG.d(TAG, "This will be deprecated December 2012");
|
||||
return this.getContext();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -966,7 +943,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;
|
||||
}
|
||||
@@ -1055,5 +1032,4 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class Echo extends Plugin {
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
try {
|
||||
String result = args.getString(0);
|
||||
if ("echo".equals(action) || "echoAsync".equals(action)) {
|
||||
return new PluginResult(PluginResult.Status.OK, result);
|
||||
}
|
||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
return "echo".equals(action);
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,7 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.webkit.CookieManager;
|
||||
|
||||
@@ -74,7 +75,7 @@ public class FileTransfer extends Plugin {
|
||||
String source = null;
|
||||
String target = null;
|
||||
try {
|
||||
source = URLDecoder.decode(args.getString(0));
|
||||
source = args.getString(0);
|
||||
target = args.getString(1);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Missing source or target");
|
||||
@@ -82,7 +83,7 @@ public class FileTransfer extends Plugin {
|
||||
}
|
||||
|
||||
if (action.equals("upload")) {
|
||||
return upload(source, target, args);
|
||||
return upload(URLDecoder.decode(source), target, args);
|
||||
} else if (action.equals("download")) {
|
||||
return download(source, target);
|
||||
} else {
|
||||
@@ -115,6 +116,11 @@ public class FileTransfer extends Plugin {
|
||||
if (params == null) params = new JSONObject();
|
||||
boolean trustEveryone = args.optBoolean(6);
|
||||
boolean chunkedMode = args.optBoolean(7) || args.isNull(7); //Always use chunked mode unless set to false as per API
|
||||
JSONObject headers = args.optJSONObject(8);
|
||||
// Look for headers on the params map for backwards compatibility with older Cordova versions.
|
||||
if (headers == null && params != null) {
|
||||
headers = params.optJSONObject("headers");
|
||||
}
|
||||
|
||||
Log.d(LOG_TAG, "fileKey: " + fileKey);
|
||||
Log.d(LOG_TAG, "fileName: " + fileName);
|
||||
@@ -122,6 +128,7 @@ public class FileTransfer extends Plugin {
|
||||
Log.d(LOG_TAG, "params: " + params);
|
||||
Log.d(LOG_TAG, "trustEveryone: " + trustEveryone);
|
||||
Log.d(LOG_TAG, "chunkedMode: " + chunkedMode);
|
||||
Log.d(LOG_TAG, "headers: " + headers);
|
||||
|
||||
// Create return object
|
||||
FileUploadResult result = new FileUploadResult();
|
||||
@@ -139,9 +146,9 @@ public class FileTransfer extends Plugin {
|
||||
//------------------ CLIENT REQUEST
|
||||
// open a URL connection to the server
|
||||
URL url = new URL(target);
|
||||
|
||||
boolean useHttps = url.getProtocol().toLowerCase().equals("https");
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (url.getProtocol().toLowerCase().equals("https")) {
|
||||
if (useHttps) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
conn = (HttpsURLConnection) url.openConnection();
|
||||
@@ -178,24 +185,31 @@ public class FileTransfer extends Plugin {
|
||||
conn.setRequestProperty("Connection", "Keep-Alive");
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY);
|
||||
|
||||
// Handle the other headers
|
||||
try {
|
||||
JSONObject headers = params.getJSONObject("headers");
|
||||
for (Iterator iter = headers.keys(); iter.hasNext();)
|
||||
{
|
||||
String headerKey = iter.next().toString();
|
||||
conn.setRequestProperty(headerKey, headers.getString(headerKey));
|
||||
}
|
||||
} catch (JSONException e1) {
|
||||
// No headers to be manipulated!
|
||||
}
|
||||
|
||||
// Set the cookies on the response
|
||||
String cookie = CookieManager.getInstance().getCookie(target);
|
||||
if (cookie != null) {
|
||||
conn.setRequestProperty("Cookie", cookie);
|
||||
}
|
||||
|
||||
// Handle the other headers
|
||||
if (headers != null) {
|
||||
try {
|
||||
for (Iterator iter = headers.keys(); iter.hasNext(); ) {
|
||||
String headerKey = iter.next().toString();
|
||||
JSONArray headerValues = headers.optJSONArray(headerKey);
|
||||
if (headerValues == null) {
|
||||
headerValues = new JSONArray();
|
||||
headerValues.put(headers.getString(headerKey));
|
||||
}
|
||||
conn.setRequestProperty(headerKey, headerValues.getString(0));
|
||||
for (int i = 1; i < headerValues.length(); ++i) {
|
||||
conn.addRequestProperty(headerKey, headerValues.getString(i));
|
||||
}
|
||||
}
|
||||
} catch (JSONException e1) {
|
||||
// No headers to be manipulated!
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Store the non-file portions of the multipart data as a string, so that we can add it
|
||||
@@ -226,19 +240,23 @@ public class FileTransfer extends Plugin {
|
||||
String tailParams = LINE_END + LINE_START + BOUNDARY + LINE_START + LINE_END;
|
||||
byte[] fileNameBytes = fileName.getBytes("UTF-8");
|
||||
|
||||
// Should set this up as an option
|
||||
int stringLength = extraBytes.length + midParams.length() + tailParams.length() + fileNameBytes.length;
|
||||
Log.d(LOG_TAG, "String Length: " + stringLength);
|
||||
int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
|
||||
Log.d(LOG_TAG, "Content Length: " + fixedLength);
|
||||
// setFixedLengthStreamingMode causes and OutOfMemoryException on pre-Froyo devices.
|
||||
// http://code.google.com/p/android/issues/detail?id=3164
|
||||
// It also causes OOM if HTTPS is used, even on newer devices.
|
||||
chunkedMode = chunkedMode && (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO || useHttps);
|
||||
|
||||
if (chunkedMode) {
|
||||
conn.setChunkedStreamingMode(maxBufferSize);
|
||||
// Although setChunkedStreamingMode sets this header, setting it explicitly here works
|
||||
// around an OutOfMemoryException when using https.
|
||||
conn.setRequestProperty("Transfer-Encoding", "chunked");
|
||||
} else {
|
||||
conn.setFixedLengthStreamingMode(fixedLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
int stringLength = extraBytes.length + midParams.length() + tailParams.length() + fileNameBytes.length;
|
||||
Log.d(LOG_TAG, "String Length: " + stringLength);
|
||||
int fixedLength = (int) fileInputStream.getChannel().size() + stringLength;
|
||||
Log.d(LOG_TAG, "Content Length: " + fixedLength);
|
||||
conn.setFixedLengthStreamingMode(fixedLength);
|
||||
}
|
||||
|
||||
|
||||
dos = new DataOutputStream( conn.getOutputStream() );
|
||||
//We don't want to change encoding, we just want this to write for all Unicode.
|
||||
@@ -255,10 +273,15 @@ public class FileTransfer extends Plugin {
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
totalBytes = 0;
|
||||
|
||||
long prevBytesRead = 0;
|
||||
while (bytesRead > 0) {
|
||||
totalBytes += bytesRead;
|
||||
result.setBytesSent(totalBytes);
|
||||
dos.write(buffer, 0, bufferSize);
|
||||
if (totalBytes > prevBytesRead + 102400) {
|
||||
prevBytesRead = totalBytes;
|
||||
Log.d(LOG_TAG, "Uploaded " + totalBytes + " of " + fixedLength + " bytes");
|
||||
}
|
||||
bytesAvailable = fileInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
@@ -467,8 +490,14 @@ public class FileTransfer extends Plugin {
|
||||
connection.connect();
|
||||
|
||||
Log.d(LOG_TAG, "Download file:" + url);
|
||||
InputStream inputStream;
|
||||
try {
|
||||
inputStream = connection.getInputStream();
|
||||
} catch(FileNotFoundException e) {
|
||||
Log.e(LOG_TAG, e.toString(), e);
|
||||
throw new IOException("Received error from server");
|
||||
}
|
||||
|
||||
InputStream inputStream = connection.getInputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int bytesRead = 0;
|
||||
|
||||
@@ -498,6 +527,7 @@ public class FileTransfer extends Plugin {
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
JSONObject error = createFileTransferError(FILE_NOT_FOUND_ERR, source, target, connection);
|
||||
Log.d(LOG_TAG, "I got a file not found exception");
|
||||
Log.e(LOG_TAG, error.toString(), e);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (MalformedURLException e) {
|
||||
|
||||
@@ -298,7 +298,9 @@ public class FileUtils extends Plugin {
|
||||
if (fp.isDirectory()) {
|
||||
File[] files = fp.listFiles();
|
||||
for (int i = 0; i < files.length; i++) {
|
||||
entries.put(getEntry(files[i]));
|
||||
if (files[i].canRead()) {
|
||||
entries.put(getEntry(files[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,36 +59,43 @@ public class GeoBroker extends Plugin {
|
||||
this.networkListener = new NetworkListener(this.locationManager, this);
|
||||
this.gpsListener = new GPSListener(this.locationManager, this);
|
||||
}
|
||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
||||
String message = "";
|
||||
PluginResult result = new PluginResult(status, message);
|
||||
result.setKeepCallback(true);
|
||||
|
||||
try {
|
||||
if (action.equals("getLocation")) {
|
||||
boolean enableHighAccuracy = args.getBoolean(0);
|
||||
int maximumAge = args.getInt(1);
|
||||
Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER));
|
||||
// Check if we can use lastKnownLocation to get a quick reading and use less battery
|
||||
if ((System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
||||
result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||
} else {
|
||||
this.getCurrentLocation(callbackId, enableHighAccuracy);
|
||||
}
|
||||
}
|
||||
else if (action.equals("addWatch")) {
|
||||
String id = args.getString(0);
|
||||
boolean enableHighAccuracy = args.getBoolean(1);
|
||||
this.addWatch(id, callbackId, enableHighAccuracy);
|
||||
}
|
||||
else if (action.equals("clearWatch")) {
|
||||
String id = args.getString(0);
|
||||
this.clearWatch(id);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
|
||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
||||
String message = "Location API is not available for this device.";
|
||||
PluginResult result = new PluginResult(status, message);
|
||||
|
||||
if ( locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ||
|
||||
locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER )) {
|
||||
|
||||
result.setKeepCallback(true);
|
||||
|
||||
try {
|
||||
if (action.equals("getLocation")) {
|
||||
boolean enableHighAccuracy = args.getBoolean(0);
|
||||
int maximumAge = args.getInt(1);
|
||||
Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER));
|
||||
// Check if we can use lastKnownLocation to get a quick reading and use less battery
|
||||
if (last != null && (System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
||||
result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||
} else {
|
||||
this.getCurrentLocation(callbackId, enableHighAccuracy);
|
||||
}
|
||||
}
|
||||
else if (action.equals("addWatch")) {
|
||||
String id = args.getString(0);
|
||||
boolean enableHighAccuracy = args.getBoolean(1);
|
||||
this.addWatch(id, callbackId, enableHighAccuracy);
|
||||
}
|
||||
else if (action.equals("clearWatch")) {
|
||||
String id = args.getString(0);
|
||||
this.clearWatch(id);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private void clearWatch(String id) {
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.LOG;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class IceCreamCordovaWebViewClient extends CordovaWebViewClient {
|
||||
|
||||
|
||||
public IceCreamCordovaWebViewClient(CordovaInterface cordova) {
|
||||
super(cordova);
|
||||
}
|
||||
|
||||
public IceCreamCordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
||||
super(cordova, view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
|
||||
if(url.contains("?") || url.contains("#")){
|
||||
return generateWebResourceResponse(url);
|
||||
} else {
|
||||
return super.shouldInterceptRequest(view, url);
|
||||
}
|
||||
}
|
||||
|
||||
private WebResourceResponse generateWebResourceResponse(String url) {
|
||||
final String ANDROID_ASSET = "file:///android_asset/";
|
||||
if (url.startsWith(ANDROID_ASSET)) {
|
||||
String niceUrl = url;
|
||||
niceUrl = url.replaceFirst(ANDROID_ASSET, "");
|
||||
if(niceUrl.contains("?")){
|
||||
niceUrl = niceUrl.split("\\?")[0];
|
||||
}
|
||||
else if(niceUrl.contains("#"))
|
||||
{
|
||||
niceUrl = niceUrl.split("#")[0];
|
||||
}
|
||||
|
||||
String mimetype = null;
|
||||
if(niceUrl.endsWith(".html")){
|
||||
mimetype = "text/html";
|
||||
}
|
||||
|
||||
try {
|
||||
AssetManager assets = cordova.getActivity().getAssets();
|
||||
Uri uri = Uri.parse(niceUrl);
|
||||
InputStream stream = assets.open(uri.getPath(), AssetManager.ACCESS_STREAMING);
|
||||
WebResourceResponse response = new WebResourceResponse(mimetype, "UTF-8", stream);
|
||||
return response;
|
||||
} catch (IOException e) {
|
||||
LOG.e("generateWebResourceResponse", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
|
||||
import android.os.Message;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* Holds the list of messages to be sent to the WebView.
|
||||
*/
|
||||
public class NativeToJsMessageQueue {
|
||||
private static final String LOG_TAG = "JsMessageQueue";
|
||||
|
||||
// This must match the default value in incubator-cordova-js/lib/android/exec.js
|
||||
private static final int DEFAULT_BRIDGE_MODE = 1;
|
||||
|
||||
/**
|
||||
* The index into registeredListeners to treat as active.
|
||||
*/
|
||||
private int activeListenerIndex;
|
||||
|
||||
/**
|
||||
* The list of JavaScript statements to be sent to JavaScript.
|
||||
*/
|
||||
private final LinkedList<String> queue = new LinkedList<String>();
|
||||
|
||||
/**
|
||||
* The array of listeners that can be used to send messages to JS.
|
||||
*/
|
||||
private final BridgeMode[] registeredListeners;
|
||||
|
||||
private final CordovaInterface cordova;
|
||||
private final CordovaWebView webView;
|
||||
|
||||
public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
|
||||
this.cordova = cordova;
|
||||
this.webView = webView;
|
||||
registeredListeners = new BridgeMode[5];
|
||||
registeredListeners[0] = null; // Polling. Requires no logic.
|
||||
registeredListeners[1] = new CallbackBridgeMode();
|
||||
registeredListeners[2] = new LoadUrlBridgeMode();
|
||||
registeredListeners[3] = new OnlineEventsBridgeMode();
|
||||
registeredListeners[4] = new PrivateApiBridgeMode();
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the bridge mode.
|
||||
*/
|
||||
public void setBridgeMode(int value) {
|
||||
if (value < 0 || value >= registeredListeners.length) {
|
||||
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
|
||||
} else {
|
||||
if (value != activeListenerIndex) {
|
||||
Log.d(LOG_TAG, "Set native->JS mode to " + value);
|
||||
synchronized (this) {
|
||||
activeListenerIndex = value;
|
||||
BridgeMode activeListener = registeredListeners[value];
|
||||
if (!queue.isEmpty() && activeListener != null) {
|
||||
activeListener.onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all messages and resets to the default bridge mode.
|
||||
*/
|
||||
public void reset() {
|
||||
synchronized (this) {
|
||||
queue.clear();
|
||||
setBridgeMode(DEFAULT_BRIDGE_MODE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes and returns the last statement in the queue.
|
||||
* Returns null if the queue is empty.
|
||||
*/
|
||||
public String pop() {
|
||||
synchronized (this) {
|
||||
if (queue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return queue.remove(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and returns all statements. Clears the queue.
|
||||
* Returns null if the queue is empty.
|
||||
*/
|
||||
public String popAll() {
|
||||
synchronized (this) {
|
||||
int length = queue.size();
|
||||
if (length == 0) {
|
||||
return null;
|
||||
}
|
||||
StringBuffer sb = new StringBuffer();
|
||||
// Wrap each statement in a try/finally so that if one throws it does
|
||||
// not affect the next.
|
||||
int i = 0;
|
||||
for (String message : queue) {
|
||||
if (++i == length) {
|
||||
sb.append(message);
|
||||
} else {
|
||||
sb.append("try{")
|
||||
.append(message)
|
||||
.append("}finally{");
|
||||
}
|
||||
}
|
||||
for ( i = 1; i < length; ++i) {
|
||||
sb.append('}');
|
||||
}
|
||||
queue.clear();
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript statement to the list.
|
||||
*/
|
||||
public void add(String statement) {
|
||||
synchronized (this) {
|
||||
queue.add(statement);
|
||||
if (registeredListeners[activeListenerIndex] != null) {
|
||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private interface BridgeMode {
|
||||
void onNativeToJsMessageAvailable();
|
||||
}
|
||||
|
||||
/** Uses a local server to send messages to JS via an XHR */
|
||||
private class CallbackBridgeMode implements BridgeMode {
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
if (webView.callbackServer != null) {
|
||||
webView.callbackServer.onNativeToJsMessageAvailable(NativeToJsMessageQueue.this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||
private class LoadUrlBridgeMode implements BridgeMode {
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
webView.loadUrlNow("javascript:" + popAll());
|
||||
}
|
||||
}
|
||||
|
||||
/** Uses online/offline events to tell the JS when to poll for messages. */
|
||||
private class OnlineEventsBridgeMode implements BridgeMode {
|
||||
boolean online = true;
|
||||
final Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!queue.isEmpty()) {
|
||||
online = !online;
|
||||
webView.setNetworkAvailable(online);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses Java reflection to access an API that lets us eval JS.
|
||||
* Requires Android 3.2.4 or above.
|
||||
*/
|
||||
private class PrivateApiBridgeMode implements BridgeMode {
|
||||
// Message added in commit:
|
||||
// http://omapzoom.org/?p=platform/frameworks/base.git;a=commitdiff;h=9497c5f8c4bc7c47789e5ccde01179abc31ffeb2
|
||||
// Which first appeared in 3.2.4ish.
|
||||
private static final int EXECUTE_JS = 194;
|
||||
|
||||
Method sendMessageMethod;
|
||||
Object webViewCore;
|
||||
boolean initFailed;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private void initReflection() {
|
||||
Object webViewObject = webView;
|
||||
Class webViewClass = WebView.class;
|
||||
try {
|
||||
Field f = webViewClass.getDeclaredField("mProvider");
|
||||
f.setAccessible(true);
|
||||
webViewObject = f.get(webView);
|
||||
webViewClass = webViewObject.getClass();
|
||||
} catch (Throwable e) {
|
||||
// mProvider is only required on newer Android releases.
|
||||
}
|
||||
|
||||
try {
|
||||
Field f = webViewClass.getDeclaredField("mWebViewCore");
|
||||
f.setAccessible(true);
|
||||
webViewCore = f.get(webViewObject);
|
||||
|
||||
if (webViewCore != null) {
|
||||
sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class);
|
||||
sendMessageMethod.setAccessible(true);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
initFailed = true;
|
||||
Log.e(LOG_TAG, "PrivateApiBridgeMode failed to find the expected APIs.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
if (sendMessageMethod == null && !initFailed) {
|
||||
initReflection();
|
||||
}
|
||||
// webViewCore is lazily initialized, and so may not be available right away.
|
||||
if (sendMessageMethod != null) {
|
||||
String js = popAll();
|
||||
Message execJsMessage = Message.obtain(null, EXECUTE_JS, js);
|
||||
try {
|
||||
sendMessageMethod.invoke(webViewCore, execJsMessage);
|
||||
} catch (Throwable e) {
|
||||
Log.e(LOG_TAG, "Reflection message bridge failed.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ public class Notification extends Plugin {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle(title);
|
||||
dlg.setCancelable(false);
|
||||
dlg.setCancelable(true);
|
||||
dlg.setPositiveButton(buttonLabel,
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@@ -229,7 +229,7 @@ public class Notification extends Plugin {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle(title);
|
||||
dlg.setCancelable(false);
|
||||
dlg.setCancelable(true);
|
||||
|
||||
// First button
|
||||
if (fButtons.length > 0) {
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
package org.apache.cordova.api;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
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();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ComponentName startService(Intent service) {
|
||||
Log.i(LOG_TAG, "Replace ctx.startService() with cordova.getActivity().startService()");
|
||||
return this.cordova.getActivity().startService(service);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
|
||||
Log.i(LOG_TAG, "Replace ctx.bindService() with cordova.getActivity().bindService()");
|
||||
return this.cordova.getActivity().bindService(service, conn, flags);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void unbindService(ServiceConnection conn) {
|
||||
Log.i(LOG_TAG, "Replace ctx.unbindService() with cordova.getActivity().unbindService()");
|
||||
this.cordova.getActivity().unbindService(conn);
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ 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;
|
||||
|
||||
/**
|
||||
@@ -63,7 +63,7 @@ public abstract class Plugin implements IPlugin {
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
this.cordova = ctx;
|
||||
this.ctx = cordova;
|
||||
this.ctx = new LegacyContext(cordova);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
@@ -165,9 +172,9 @@ public class PluginManager {
|
||||
* immediate return value. If true, either Cordova.callbackSuccess(...) or
|
||||
* Cordova.callbackError(...) is called once the plugin code has executed.
|
||||
*
|
||||
* @return JSON encoded string with a response message and status.
|
||||
* @return PluginResult to send to the page, or null if no response is ready yet.
|
||||
*/
|
||||
public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
|
||||
public PluginResult exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
|
||||
PluginResult cr = null;
|
||||
boolean runAsync = async;
|
||||
try {
|
||||
@@ -183,20 +190,9 @@ public class PluginManager {
|
||||
try {
|
||||
// Call execute on the plugin so that it can do it's thing
|
||||
PluginResult cr = plugin.execute(action, args, callbackId);
|
||||
int status = cr.getStatus();
|
||||
|
||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||
if ((status == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
||||
}
|
||||
|
||||
// Check the success (OK, NO_RESULT & !KEEP_CALLBACK)
|
||||
else if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) {
|
||||
app.sendJavascript(cr.toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
// If error
|
||||
else {
|
||||
app.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
String callbackString = cr.toCallbackString(callbackId);
|
||||
if (callbackString != null) {
|
||||
app.sendJavascript(callbackString);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
||||
@@ -205,14 +201,14 @@ public class PluginManager {
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
return "";
|
||||
return null;
|
||||
} else {
|
||||
// Call execute on the plugin so that it can do it's thing
|
||||
cr = plugin.execute(action, args, callbackId);
|
||||
|
||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||
if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
||||
return "";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,7 +223,10 @@ public class PluginManager {
|
||||
}
|
||||
app.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
return (cr != null ? cr.getJSONString() : "{ status: 0, message: 'all good' }");
|
||||
if (cr == null) {
|
||||
cr = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
}
|
||||
return cr;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -361,9 +360,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, "=====================================================================================");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class PluginResult {
|
||||
|
||||
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) {
|
||||
@@ -82,6 +82,19 @@ public class PluginResult {
|
||||
return "{\"status\":" + this.status + ",\"message\":" + this.message + ",\"keepCallback\":" + this.keepCallback + "}";
|
||||
}
|
||||
|
||||
public String toCallbackString(String callbackId) {
|
||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||
if ((status == PluginResult.Status.NO_RESULT.ordinal()) && keepCallback) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check the success (OK, NO_RESULT & !KEEP_CALLBACK)
|
||||
if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) {
|
||||
return toSuccessCallbackString(callbackId);
|
||||
}
|
||||
|
||||
return toErrorCallbackString(callbackId);
|
||||
}
|
||||
public String toSuccessCallbackString(String callbackId) {
|
||||
return "cordova.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");";
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# Cordova Upgrade Guide #
|
||||
|
||||
This document is for people who need to upgrade their Cordova versions from an older version to a current version of Cordova.
|
||||
|
||||
- To upgrade to 1.8.0, please go from 1.7.0
|
||||
- To upgrade from 1.7.0, please go from 1.6.0
|
||||
|
||||
## Upgrade to 1.8.0 from 1.7.0 ##
|
||||
|
||||
1. Remove cordova-1.7.0.jar from the libs directory in your project
|
||||
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
|
||||
6. Update the res/xml/plugins.xml to be the same as the one found in framework/res/xml/plugins.xml
|
||||
|
||||
|
||||
## Upgrade to 1.7.0 from 1.6.0 ##
|
||||
|
||||
1. Remove cordova-1.6.0.jar from the libs directory in your project
|
||||
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
|
||||
6. Update the res/xml/plugins.xml to be the same as the one found in framework/res/xml/plugins.xml
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
Bryce Curtis (5):
|
||||
[CB-352] Support initializing DroidGap with existing WebView, WebViewClient and webViewChrome. [CB-353] Create PluginEntry object to use by PluginManager.
|
||||
[CB-367] Back button event should fire on key up not key down Also changed menu key and search key to be consistent.
|
||||
Tests to verify Android native features.
|
||||
[CB-423] Problem displaying patch-9 splash screen.
|
||||
Update project template cordova.js reference and title.
|
||||
|
||||
Fil Maj (6):
|
||||
switched from "require" syntax to "cordova.require"
|
||||
cordova.require("cordova") is pretty funny. wish i didnt write it
|
||||
updates to JS: removing require+define from global scope, tweaking geolocation code, online/offline events fire on document now
|
||||
removed old javascript files and removed unused target + commented out lines in build.xml
|
||||
spacing fixes, null check in getPhoneType in contacts, returning error integers instead of objects in contacts
|
||||
updating network status plugin label and updating cordova-js to latest
|
||||
|
||||
Joe Bowser (11):
|
||||
We show the default 404 on non-resolved domains
|
||||
Fixing CB-210 with patch and adding fix for CB-210
|
||||
Tweaked File Transfer to fix CB-74
|
||||
Changing to the modern icon
|
||||
Added temporary Cordova splash for now
|
||||
Checking for the callback server before we call sendJavascript for the Kindle Fire, CB-247
|
||||
Fixing CB-343: We need to respect the whitelist
|
||||
Fixing a bug with File Upload on Android where Chunked mode isn't used by default
|
||||
First stab at CB-21, I really need more info before I can close this
|
||||
Tagged 1.6rc1
|
||||
Fixing the template, since this doesn't have to be unit tested. :)
|
||||
|
||||
macdonst (12):
|
||||
CB-383: Fixes issue with misspelled destinationType for Camera.getPicture()
|
||||
Fix for CB-389: resolveLocalFileSystemURI does not work on a resized image captured from Camera.getPicture()
|
||||
Fixing license header in com.phonegap.api.PluginManager
|
||||
CB-321: Media API: 'mediaSuccess' callback param to new Media() is called soon after new obj created
|
||||
CB-163: contactFindOptions.filter does not work as expected on Android
|
||||
CB-426: camera.getPicture ignores mediaType in 1.5
|
||||
Updating cordova.android.js for CB-421 and CB-426
|
||||
CB-438: File metadata.modificationTime returns an invalid date
|
||||
Return MediaError object and not error code from native side of Media API
|
||||
CB-446: Enhance setting data source for local files in AudioPlayer
|
||||
CB-453: FileWriter.append - Chinese characters are not appended to the file correctly
|
||||
CB-446: Enhance setting data source for local files in AudioPlayer
|
||||
|
||||
@@ -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>
|
||||
|
||||
Vendored
+2
-2
@@ -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>');
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
# project structure.
|
||||
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:15
|
||||
target=android-16
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ package org.apache.cordova.test;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import com.phonegap.api.PluginManager;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
@@ -32,6 +33,8 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<Cordov
|
||||
private FrameLayout containerView;
|
||||
private LinearLayout innerContainer;
|
||||
private CordovaWebView testView;
|
||||
private Instrumentation mInstr;
|
||||
private int TIMEOUT = 1000;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public CordovaActivityTest()
|
||||
@@ -41,6 +44,7 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<Cordov
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
mInstr = this.getInstrumentation();
|
||||
testActivity = this.getActivity();
|
||||
containerView = (FrameLayout) testActivity.findViewById(android.R.id.content);
|
||||
innerContainer = (LinearLayout) containerView.getChildAt(0);
|
||||
@@ -65,4 +69,21 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<Cordov
|
||||
}
|
||||
|
||||
|
||||
public void testPauseAndResume()
|
||||
{
|
||||
mInstr.callActivityOnPause(testActivity);
|
||||
sleep();
|
||||
assertTrue(testView.isPaused());
|
||||
mInstr.callActivityOnResume(testActivity);
|
||||
sleep();
|
||||
assertFalse(testView.isPaused());
|
||||
}
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
Thread.sleep(TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
fail("Unexpected Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.IPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -34,45 +35,42 @@ public class CordovaDriverAction extends Activity implements CordovaInterface {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindBackButton(boolean arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelLoadUrl() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackButtonBound() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String arg0, Object arg1) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActivityResultCallback(IPlugin arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityForResult(IPlugin arg0, Intent arg1, int arg2) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ package org.apache.cordova.test;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import com.phonegap.api.PluginManager;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.view.View;
|
||||
|
||||
@@ -99,6 +100,7 @@ public class CordovaTest extends
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
Thread.sleep(TIMEOUT);
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova.test;
|
||||
|
||||
import org.openqa.selenium.android.library.ViewAdapter;
|
||||
import org.openqa.selenium.android.library.ViewFactory;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class CordovaViewFactory implements ViewFactory {
|
||||
|
||||
public ViewAdapter createNewView(Activity arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
return new ViewAdapter("org.apache.cordova.CordovaWebView", new CordovaWebView(arg0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.IPlugin;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
@@ -52,45 +53,41 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityForResult(IPlugin command, Intent intent, int requestCode) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setActivityResultCallback(IPlugin plugin) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bindBackButton(boolean override) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBackButtonBound() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
// TODO Auto-generated method stub
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancelLoadUrl() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onMessage(String id, Object data) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -44,11 +44,11 @@ public class ErrorUrlTest extends ActivityInstrumentationTestCase2<errorurl> {
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
Thread.sleep(TIMEOUT);
|
||||
Thread.sleep(TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
fail("Unexpected Timeout");
|
||||
fail("Unexpected Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova.test;
|
||||
|
||||
import org.apache.cordova.CordovaWebViewClient;
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.CordovaChromeClient;
|
||||
|
||||
import org.apache.cordova.test.CordovaViewFactory;
|
||||
import org.openqa.selenium.By;
|
||||
import org.openqa.selenium.WebElement;
|
||||
import org.openqa.selenium.android.library.AndroidWebDriver;
|
||||
import org.openqa.selenium.android.library.ChromeClientWrapper;
|
||||
import org.openqa.selenium.android.library.ViewClientWrapper;
|
||||
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
|
||||
public class WebDriverTest extends ActivityInstrumentationTestCase2<CordovaDriverAction> {
|
||||
|
||||
private static final long TIMEOUT = 5000;
|
||||
private CordovaDriverAction testActivity;
|
||||
private CordovaWebView testView;
|
||||
private CordovaViewFactory viewFactory;
|
||||
private CordovaChromeClient appCode;
|
||||
private CordovaWebViewClient viewHandler;
|
||||
private AndroidWebDriver testDriver;
|
||||
private ViewClientWrapper viewClientWrapper;
|
||||
private ChromeClientWrapper chromeClientWrapper;
|
||||
|
||||
public WebDriverTest() {
|
||||
super("com.phonegap.test.activities",CordovaDriverAction.class);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception{
|
||||
super.setUp();
|
||||
|
||||
testActivity = this.getActivity();
|
||||
viewFactory = new CordovaViewFactory();
|
||||
appCode = new CordovaChromeClient(testActivity);
|
||||
viewHandler = new CordovaWebViewClient(testActivity);
|
||||
viewClientWrapper = new ViewClientWrapper("org.apache.cordova.CordovaWebViewClient", viewHandler);
|
||||
chromeClientWrapper = new ChromeClientWrapper("org.apache.cordova.CordovaChromeClient", appCode);
|
||||
testDriver = new AndroidWebDriver(testActivity, viewFactory, viewClientWrapper, chromeClientWrapper);
|
||||
testView = (CordovaWebView) testDriver.getWebView();
|
||||
viewHandler.setWebView(testView);
|
||||
appCode.setWebView(testView);
|
||||
}
|
||||
|
||||
public void testPreconditions(){
|
||||
assertNotNull(testView);
|
||||
}
|
||||
|
||||
public void testWebLoad() {
|
||||
testDriver.get("file:///android_asset/www/index.html");
|
||||
sleep();
|
||||
String url = testView.getUrl();
|
||||
//Check the sanity!
|
||||
boolean result = url.equals("file:///android_asset/www/index.html");
|
||||
assertTrue(result);
|
||||
WebElement platformSpan = testDriver.findElement(By.id("platform"));
|
||||
String text = platformSpan.getText();
|
||||
assertTrue(text.equals("Android"));
|
||||
}
|
||||
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
Thread.sleep(TIMEOUT);
|
||||
} catch (InterruptedException e) {
|
||||
fail("Unexpected Timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user