Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
311a2f6023 | ||
|
|
59a3cf93e6 | ||
|
|
a42f095cef | ||
|
|
a29340523f | ||
|
|
5ad7a7c014 | ||
|
|
c6fa7e4aad | ||
|
|
d4b248fbe3 | ||
|
|
48881d081a | ||
|
|
331024414e | ||
|
|
9d0c5349bb | ||
|
|
dc40d8afac | ||
|
|
005877b4b8 | ||
|
|
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 | ||
|
|
8013b760e3 | ||
|
|
a29b8e5b36 | ||
|
|
9ef487a7a5 | ||
|
|
563fa46ba4 | ||
|
|
7865c06863 | ||
|
|
3d53b9244d | ||
|
|
f2afa4dd50 | ||
|
|
893ecec55e | ||
|
|
401584dbd8 | ||
|
|
b234b0bded | ||
|
|
b9b2c6a013 | ||
|
|
1d2efa0d25 |
12
.gitignore
vendored
@@ -13,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
|
||||
|
||||
|
||||
11
README.md
@@ -82,15 +82,10 @@ Start adb logcat (console.log calls output here):
|
||||
|
||||
./cordova/log
|
||||
|
||||
Running the [callback/callback-test](http://github.com/callback/callback-test) tests:
|
||||
---
|
||||
|
||||
./bin/test
|
||||
|
||||
Creating a new Cordova Android Project
|
||||
---
|
||||
|
||||
./bin/create ~/Desktop/myapp com.phonegap.special MyApp
|
||||
./bin/create ~/Desktop/myapp com.myapp.special MyApp
|
||||
|
||||
Importing a Cordova Android Project into Eclipse
|
||||
----
|
||||
@@ -110,5 +105,5 @@ Further Reading
|
||||
---
|
||||
|
||||
- [http://developer.android.com](http://developer.android.com)
|
||||
- [http://docs.phonegap.com](http://docs.phonegap.com)
|
||||
- [http://wiki.phonegap.com](http://wiki.phonegap.com)
|
||||
- [http://incubator.apache.org/cordova/](http://incubator.apache.org/cordova/)
|
||||
- [http://wiki.apache.org/cordova/](http://wiki.apache.org/cordova/)
|
||||
|
||||
93
bin/create
@@ -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,33 +46,33 @@ 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 = 'darwin11' ]
|
||||
if [[ "$OSTYPE" =~ "darwin" ]]
|
||||
then
|
||||
sed -i '' -e $pattern $filename
|
||||
elif [ $OSTYPE = 'linux-gnu' ]
|
||||
/usr/bin/sed -i '' -e $pattern "$filename"
|
||||
elif [[ "$OSTYPE" =~ "linux" ]]
|
||||
then
|
||||
sed -i -e $pattern $filename
|
||||
/bin/sed -i -e $pattern "$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -82,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
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" $ACTIVITY_PATH
|
||||
replace "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
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" $MANIFEST_PATH
|
||||
replace "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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -48,8 +48,8 @@
|
||||
|
||||
<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">
|
||||
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
|
||||
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>
|
||||
|
||||
@@ -1,16 +1,30 @@
|
||||
html,
|
||||
body {
|
||||
height:100%;
|
||||
font-size:12px;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
html {
|
||||
display:table;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
* {
|
||||
-webkit-touch-callout: none; /* prevent callout to copy image, etc when tap to hold */
|
||||
-webkit-text-size-adjust: none; /* prevent webkit from resizing text to fit */
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
|
||||
-webkit-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
|
||||
}
|
||||
|
||||
body {
|
||||
background-color:#A7A7A7;
|
||||
background-color:#E4E4E4;
|
||||
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%);
|
||||
@@ -21,46 +35,65 @@ body {
|
||||
color-stop(0, #A7A7A7),
|
||||
color-stop(0.51, #E4E4E4)
|
||||
);
|
||||
display:table-cell;
|
||||
background-attachment:fixed;
|
||||
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
font-size:12px;
|
||||
height:100%;
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
text-transform:uppercase;
|
||||
vertical-align:middle;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
/* Portrait layout (default) */
|
||||
.app {
|
||||
background-image:url(../img/cordova.png);
|
||||
background-repeat:no-repeat;
|
||||
margin:0px auto;
|
||||
width:275px;
|
||||
background:url(../img/logo.png) no-repeat center top; /* 170px x 200px */
|
||||
position:absolute; /* position in the center of the screen */
|
||||
left:50%;
|
||||
top:50%;
|
||||
height:50px; /* text area height */
|
||||
width:225px; /* text area width */
|
||||
text-align:center;
|
||||
padding:180px 0px 0px 0px; /* image height is 200px (bottom 20px are overlapped with text) */
|
||||
margin:-115px 0px 0px -112px; /* offset vertical: half of image height and text area height */
|
||||
/* offset horizontal: half of text area width */
|
||||
}
|
||||
|
||||
/* Landscape layout (with min-width) */
|
||||
@media screen and (min-aspect-ratio: 1/1) and (min-width:400px) {
|
||||
.app {
|
||||
background-position:left center;
|
||||
padding:75px 0px 75px 170px; /* padding-top + padding-bottom + text area = image height */
|
||||
margin:-90px 0px 0px -198px; /* offset vertical: half of image height */
|
||||
/* offset horizontal: half of image width and text area width */
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:2em;
|
||||
font-weight:300;
|
||||
font-size:24px;
|
||||
font-weight:normal;
|
||||
margin:0px;
|
||||
overflow:visible;
|
||||
padding:0px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.status {
|
||||
background-color:#333333;
|
||||
.event {
|
||||
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;
|
||||
font-size:12px;
|
||||
margin:0px 30px;
|
||||
padding:2px 0px;
|
||||
}
|
||||
|
||||
.status.complete {
|
||||
.event.listening {
|
||||
background-color:#333333;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.event.received {
|
||||
background-color:#4B946A;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display:none;
|
||||
}
|
||||
|
||||
@@ -80,21 +113,3 @@ h1 {
|
||||
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 */
|
||||
}
|
||||
}
|
||||
|
||||
BIN
bin/templates/project/assets/www/img/logo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
@@ -1,21 +1,39 @@
|
||||
<!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.
|
||||
-->
|
||||
<html>
|
||||
<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;" />
|
||||
<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, height=device-height, target-densitydpi=device-dpi" />
|
||||
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
||||
<title>Hello Cordova</title>
|
||||
<title>Hello World</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>
|
||||
<h1>Apache Cordova</h1>
|
||||
<div id="deviceready" class="blink">
|
||||
<p class="event listening">Connecting to Device</p>
|
||||
<p class="event received">Device is Ready</p>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="cordova-2.0.0.js"></script>
|
||||
<script type="text/javascript" src="cordova-2.1.0.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
<script type="text/javascript">
|
||||
app.initialize();
|
||||
|
||||
@@ -1,15 +1,49 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
var app = {
|
||||
// Application Constructor
|
||||
initialize: function() {
|
||||
this.bind();
|
||||
this.bindEvents();
|
||||
},
|
||||
bind: function() {
|
||||
document.addEventListener('deviceready', this.deviceready, false);
|
||||
// Bind Event Listeners
|
||||
//
|
||||
// Bind any events that are required on startup. Common events are:
|
||||
// 'load', 'deviceready', 'offline', and 'online'.
|
||||
bindEvents: function() {
|
||||
document.addEventListener('deviceready', this.onDeviceReady, false);
|
||||
},
|
||||
deviceready: function() {
|
||||
app.report('deviceready');
|
||||
// deviceready Event Handler
|
||||
//
|
||||
// The scope of 'this' is the event. In order to call the 'receivedEvent'
|
||||
// function, we must explicity call 'app.receivedEvent(...);'
|
||||
onDeviceReady: function() {
|
||||
app.receivedEvent('deviceready');
|
||||
},
|
||||
report: function(id) {
|
||||
document.querySelector('#' + id + ' .pending').classList.add('hide');
|
||||
document.querySelector('#' + id + ' .complete').classList.remove('hide');
|
||||
// Update DOM on a Received Event
|
||||
receivedEvent: function(id) {
|
||||
var parentElement = document.getElementById(id);
|
||||
var listeningElement = parentElement.querySelector('.listening');
|
||||
var receivedElement = parentElement.querySelector('.received');
|
||||
|
||||
listeningElement.setAttribute('style', 'display:none;');
|
||||
receivedElement.setAttribute('style', 'display:block;');
|
||||
|
||||
console.log('Received Event: ' + id);
|
||||
}
|
||||
};
|
||||
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 217 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 88 KiB |
|
After Width: | Height: | Size: 478 KiB |
|
After Width: | Height: | Size: 493 KiB |
68
bin/templates/project/assets/www/spec.html
Normal file
@@ -0,0 +1,68 @@
|
||||
<!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.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Jasmine Spec Runner</title>
|
||||
|
||||
<!-- jasmine source -->
|
||||
<link rel="shortcut icon" type="image/png" href="spec/lib/jasmine-1.2.0/jasmine_favicon.png">
|
||||
<link rel="stylesheet" type="text/css" href="spec/lib/jasmine-1.2.0/jasmine.css">
|
||||
<script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine.js"></script>
|
||||
<script type="text/javascript" src="spec/lib/jasmine-1.2.0/jasmine-html.js"></script>
|
||||
|
||||
<!-- include source files here... -->
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
|
||||
<!-- include spec files here... -->
|
||||
<script type="text/javascript" src="spec/helper.js"></script>
|
||||
<script type="text/javascript" src="spec/index.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
(function() {
|
||||
var jasmineEnv = jasmine.getEnv();
|
||||
jasmineEnv.updateInterval = 1000;
|
||||
|
||||
var htmlReporter = new jasmine.HtmlReporter();
|
||||
|
||||
jasmineEnv.addReporter(htmlReporter);
|
||||
|
||||
jasmineEnv.specFilter = function(spec) {
|
||||
return htmlReporter.specFilter(spec);
|
||||
};
|
||||
|
||||
var currentWindowOnload = window.onload;
|
||||
|
||||
window.onload = function() {
|
||||
if (currentWindowOnload) {
|
||||
currentWindowOnload();
|
||||
}
|
||||
execJasmine();
|
||||
};
|
||||
|
||||
function execJasmine() {
|
||||
jasmineEnv.execute();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="stage" style="display:none;"></div>
|
||||
</body>
|
||||
</html>
|
||||
33
bin/templates/project/assets/www/spec/helper.js
Normal file
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
afterEach(function() {
|
||||
document.getElementById('stage').innerHTML = '';
|
||||
});
|
||||
|
||||
var helper = {
|
||||
trigger: function(obj, name) {
|
||||
var e = document.createEvent('Event');
|
||||
e.initEvent(name, true, true);
|
||||
obj.dispatchEvent(e);
|
||||
},
|
||||
getComputedStyle: function(querySelector, property) {
|
||||
var element = document.querySelector(querySelector);
|
||||
return window.getComputedStyle(element).getPropertyValue(property);
|
||||
}
|
||||
};
|
||||
67
bin/templates/project/assets/www/spec/index.js
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
describe('app', function() {
|
||||
describe('initialize', function() {
|
||||
it('should bind deviceready', function() {
|
||||
runs(function() {
|
||||
spyOn(app, 'onDeviceReady');
|
||||
app.initialize();
|
||||
helper.trigger(window.document, 'deviceready');
|
||||
});
|
||||
|
||||
waitsFor(function() {
|
||||
return (app.onDeviceReady.calls.length > 0);
|
||||
}, 'onDeviceReady should be called once', 500);
|
||||
|
||||
runs(function() {
|
||||
expect(app.onDeviceReady).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('onDeviceReady', function() {
|
||||
it('should report that it fired', function() {
|
||||
spyOn(app, 'receivedEvent');
|
||||
app.onDeviceReady();
|
||||
expect(app.receivedEvent).toHaveBeenCalledWith('deviceready');
|
||||
});
|
||||
});
|
||||
|
||||
describe('receivedEvent', function() {
|
||||
beforeEach(function() {
|
||||
var el = document.getElementById('stage');
|
||||
el.innerHTML = ['<div id="deviceready">',
|
||||
' <p class="event listening">Listening</p>',
|
||||
' <p class="event received">Received</p>',
|
||||
'</div>'].join('\n');
|
||||
});
|
||||
|
||||
it('should hide the listening element', function() {
|
||||
app.receivedEvent('deviceready');
|
||||
var displayStyle = helper.getComputedStyle('#deviceready .listening', 'display');
|
||||
expect(displayStyle).toEqual('none');
|
||||
});
|
||||
|
||||
it('should show the received element', function() {
|
||||
app.receivedEvent('deviceready');
|
||||
var displayStyle = helper.getComputedStyle('#deviceready .received', 'display');
|
||||
expect(displayStyle).toEqual('block');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2008-2011 Pivotal Labs
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -0,0 +1,616 @@
|
||||
jasmine.HtmlReporterHelpers = {};
|
||||
|
||||
jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) {
|
||||
el.appendChild(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
|
||||
var results = child.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
|
||||
return status;
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
|
||||
var parentDiv = this.dom.summary;
|
||||
var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
|
||||
var parent = child[parentSuite];
|
||||
|
||||
if (parent) {
|
||||
if (typeof this.views.suites[parent.id] == 'undefined') {
|
||||
this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
|
||||
}
|
||||
parentDiv = this.views.suites[parent.id].element;
|
||||
}
|
||||
|
||||
parentDiv.appendChild(childElement);
|
||||
};
|
||||
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
|
||||
for(var fn in jasmine.HtmlReporterHelpers) {
|
||||
ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter = function(_doc) {
|
||||
var self = this;
|
||||
var doc = _doc || window.document;
|
||||
|
||||
var reporterView;
|
||||
|
||||
var dom = {};
|
||||
|
||||
// Jasmine Reporter Public Interface
|
||||
self.logRunningSpecs = false;
|
||||
|
||||
self.reportRunnerStarting = function(runner) {
|
||||
var specs = runner.specs() || [];
|
||||
|
||||
if (specs.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createReporterDom(runner.env.versionString());
|
||||
doc.body.appendChild(dom.reporter);
|
||||
|
||||
reporterView = new jasmine.HtmlReporter.ReporterView(dom);
|
||||
reporterView.addSpecs(specs, self.specFilter);
|
||||
};
|
||||
|
||||
self.reportRunnerResults = function(runner) {
|
||||
reporterView && reporterView.complete();
|
||||
};
|
||||
|
||||
self.reportSuiteResults = function(suite) {
|
||||
reporterView.suiteComplete(suite);
|
||||
};
|
||||
|
||||
self.reportSpecStarting = function(spec) {
|
||||
if (self.logRunningSpecs) {
|
||||
self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
self.reportSpecResults = function(spec) {
|
||||
reporterView.specComplete(spec);
|
||||
};
|
||||
|
||||
self.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
self.specFilter = function(spec) {
|
||||
if (!focusedSpecName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return spec.getFullName().indexOf(focusedSpecName()) === 0;
|
||||
};
|
||||
|
||||
return self;
|
||||
|
||||
function focusedSpecName() {
|
||||
var specName;
|
||||
|
||||
(function memoizeFocusedSpec() {
|
||||
if (specName) {
|
||||
return;
|
||||
}
|
||||
|
||||
var paramMap = [];
|
||||
var params = doc.location.search.substring(1).split('&');
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
specName = paramMap.spec;
|
||||
})();
|
||||
|
||||
return specName;
|
||||
}
|
||||
|
||||
function createReporterDom(version) {
|
||||
dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
|
||||
dom.banner = self.createDom('div', { className: 'banner' },
|
||||
self.createDom('span', { className: 'title' }, "Jasmine "),
|
||||
self.createDom('span', { className: 'version' }, version)),
|
||||
|
||||
dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
|
||||
dom.alert = self.createDom('div', {className: 'alert'}),
|
||||
dom.results = self.createDom('div', {className: 'results'},
|
||||
dom.summary = self.createDom('div', { className: 'summary' }),
|
||||
dom.details = self.createDom('div', { id: 'details' }))
|
||||
);
|
||||
}
|
||||
};
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporter.ReporterView = function(dom) {
|
||||
this.startedAt = new Date();
|
||||
this.runningSpecCount = 0;
|
||||
this.completeSpecCount = 0;
|
||||
this.passedCount = 0;
|
||||
this.failedCount = 0;
|
||||
this.skippedCount = 0;
|
||||
|
||||
this.createResultsMenu = function() {
|
||||
this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
|
||||
this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
|
||||
' | ',
|
||||
this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));
|
||||
|
||||
this.summaryMenuItem.onclick = function() {
|
||||
dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
|
||||
};
|
||||
|
||||
this.detailsMenuItem.onclick = function() {
|
||||
showDetails();
|
||||
};
|
||||
};
|
||||
|
||||
this.addSpecs = function(specs, specFilter) {
|
||||
this.totalSpecCount = specs.length;
|
||||
|
||||
this.views = {
|
||||
specs: {},
|
||||
suites: {}
|
||||
};
|
||||
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
var spec = specs[i];
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
|
||||
if (specFilter(spec)) {
|
||||
this.runningSpecCount++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.specComplete = function(spec) {
|
||||
this.completeSpecCount++;
|
||||
|
||||
if (isUndefined(this.views.specs[spec.id])) {
|
||||
this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
|
||||
}
|
||||
|
||||
var specView = this.views.specs[spec.id];
|
||||
|
||||
switch (specView.status()) {
|
||||
case 'passed':
|
||||
this.passedCount++;
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.failedCount++;
|
||||
break;
|
||||
|
||||
case 'skipped':
|
||||
this.skippedCount++;
|
||||
break;
|
||||
}
|
||||
|
||||
specView.refresh();
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
this.suiteComplete = function(suite) {
|
||||
var suiteView = this.views.suites[suite.id];
|
||||
if (isUndefined(suiteView)) {
|
||||
return;
|
||||
}
|
||||
suiteView.refresh();
|
||||
};
|
||||
|
||||
this.refresh = function() {
|
||||
|
||||
if (isUndefined(this.resultsMenu)) {
|
||||
this.createResultsMenu();
|
||||
}
|
||||
|
||||
// currently running UI
|
||||
if (isUndefined(this.runningAlert)) {
|
||||
this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
|
||||
dom.alert.appendChild(this.runningAlert);
|
||||
}
|
||||
this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);
|
||||
|
||||
// skipped specs UI
|
||||
if (isUndefined(this.skippedAlert)) {
|
||||
this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
|
||||
}
|
||||
|
||||
this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.skippedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.skippedAlert);
|
||||
}
|
||||
|
||||
// passing specs UI
|
||||
if (isUndefined(this.passedAlert)) {
|
||||
this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
|
||||
}
|
||||
this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);
|
||||
|
||||
// failing specs UI
|
||||
if (isUndefined(this.failedAlert)) {
|
||||
this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
|
||||
}
|
||||
this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);
|
||||
|
||||
if (this.failedCount === 1 && isDefined(dom.alert)) {
|
||||
dom.alert.appendChild(this.failedAlert);
|
||||
dom.alert.appendChild(this.resultsMenu);
|
||||
}
|
||||
|
||||
// summary info
|
||||
this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
|
||||
this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
|
||||
};
|
||||
|
||||
this.complete = function() {
|
||||
dom.alert.removeChild(this.runningAlert);
|
||||
|
||||
this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";
|
||||
|
||||
if (this.failedCount === 0) {
|
||||
dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
|
||||
} else {
|
||||
showDetails();
|
||||
}
|
||||
|
||||
dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
|
||||
};
|
||||
|
||||
return this;
|
||||
|
||||
function showDetails() {
|
||||
if (dom.reporter.className.search(/showDetails/) === -1) {
|
||||
dom.reporter.className += " showDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function isUndefined(obj) {
|
||||
return typeof obj === 'undefined';
|
||||
}
|
||||
|
||||
function isDefined(obj) {
|
||||
return !isUndefined(obj);
|
||||
}
|
||||
|
||||
function specPluralizedFor(count) {
|
||||
var str = count + " spec";
|
||||
if (count > 1) {
|
||||
str += "s"
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);
|
||||
|
||||
|
||||
jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
|
||||
this.spec = spec;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.symbol = this.createDom('li', { className: 'pending' });
|
||||
this.dom.symbolSummary.appendChild(this.symbol);
|
||||
|
||||
this.summary = this.createDom('div', { className: 'specSummary' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.description)
|
||||
);
|
||||
|
||||
this.detail = this.createDom('div', { className: 'specDetail' },
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
|
||||
title: this.spec.getFullName()
|
||||
}, this.spec.getFullName())
|
||||
);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.spec);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
|
||||
this.symbol.className = this.status();
|
||||
|
||||
switch (this.status()) {
|
||||
case 'skipped':
|
||||
break;
|
||||
|
||||
case 'passed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
break;
|
||||
|
||||
case 'failed':
|
||||
this.appendSummaryToSuiteDiv();
|
||||
this.appendFailureDetail();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
|
||||
this.summary.className += ' ' + this.status();
|
||||
this.appendToSummary(this.spec, this.summary);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
|
||||
this.detail.className += ' ' + this.status();
|
||||
|
||||
var resultItems = this.spec.results().getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
this.detail.appendChild(messagesDiv);
|
||||
this.dom.details.appendChild(this.detail);
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
|
||||
this.suite = suite;
|
||||
this.dom = dom;
|
||||
this.views = views;
|
||||
|
||||
this.element = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
|
||||
);
|
||||
|
||||
this.appendToSummary(this.suite, this.element);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.status = function() {
|
||||
return this.getSpecStatus(this.suite);
|
||||
};
|
||||
|
||||
jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
|
||||
this.element.className += " " + this.status();
|
||||
};
|
||||
|
||||
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);
|
||||
|
||||
/* @deprecated Use jasmine.HtmlReporter instead
|
||||
*/
|
||||
jasmine.TrivialReporter = function(doc) {
|
||||
this.document = doc || document;
|
||||
this.suiteDivs = {};
|
||||
this.logRunningSpecs = false;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
|
||||
var el = document.createElement(type);
|
||||
|
||||
for (var i = 2; i < arguments.length; i++) {
|
||||
var child = arguments[i];
|
||||
|
||||
if (typeof child === 'string') {
|
||||
el.appendChild(document.createTextNode(child));
|
||||
} else {
|
||||
if (child) { el.appendChild(child); }
|
||||
}
|
||||
}
|
||||
|
||||
for (var attr in attrs) {
|
||||
if (attr == "className") {
|
||||
el[attr] = attrs[attr];
|
||||
} else {
|
||||
el.setAttribute(attr, attrs[attr]);
|
||||
}
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
|
||||
var showPassed, showSkipped;
|
||||
|
||||
this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
|
||||
this.createDom('div', { className: 'banner' },
|
||||
this.createDom('div', { className: 'logo' },
|
||||
this.createDom('span', { className: 'title' }, "Jasmine"),
|
||||
this.createDom('span', { className: 'version' }, runner.env.versionString())),
|
||||
this.createDom('div', { className: 'options' },
|
||||
"Show ",
|
||||
showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
|
||||
showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
|
||||
this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
|
||||
)
|
||||
),
|
||||
|
||||
this.runnerDiv = this.createDom('div', { className: 'runner running' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
|
||||
this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
|
||||
this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
|
||||
);
|
||||
|
||||
this.document.body.appendChild(this.outerDiv);
|
||||
|
||||
var suites = runner.suites();
|
||||
for (var i = 0; i < suites.length; i++) {
|
||||
var suite = suites[i];
|
||||
var suiteDiv = this.createDom('div', { className: 'suite' },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
|
||||
this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
|
||||
this.suiteDivs[suite.id] = suiteDiv;
|
||||
var parentDiv = this.outerDiv;
|
||||
if (suite.parentSuite) {
|
||||
parentDiv = this.suiteDivs[suite.parentSuite.id];
|
||||
}
|
||||
parentDiv.appendChild(suiteDiv);
|
||||
}
|
||||
|
||||
this.startedAt = new Date();
|
||||
|
||||
var self = this;
|
||||
showPassed.onclick = function(evt) {
|
||||
if (showPassed.checked) {
|
||||
self.outerDiv.className += ' show-passed';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
|
||||
}
|
||||
};
|
||||
|
||||
showSkipped.onclick = function(evt) {
|
||||
if (showSkipped.checked) {
|
||||
self.outerDiv.className += ' show-skipped';
|
||||
} else {
|
||||
self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
|
||||
var results = runner.results();
|
||||
var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
|
||||
this.runnerDiv.setAttribute("class", className);
|
||||
//do it twice for IE
|
||||
this.runnerDiv.setAttribute("className", className);
|
||||
var specs = runner.specs();
|
||||
var specCount = 0;
|
||||
for (var i = 0; i < specs.length; i++) {
|
||||
if (this.specFilter(specs[i])) {
|
||||
specCount++;
|
||||
}
|
||||
}
|
||||
var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
|
||||
message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
|
||||
this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
|
||||
|
||||
this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
|
||||
var results = suite.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.totalCount === 0) { // todo: change this to check results.skipped
|
||||
status = 'skipped';
|
||||
}
|
||||
this.suiteDivs[suite.id].className += " " + status;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
|
||||
if (this.logRunningSpecs) {
|
||||
this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
|
||||
var results = spec.results();
|
||||
var status = results.passed() ? 'passed' : 'failed';
|
||||
if (results.skipped) {
|
||||
status = 'skipped';
|
||||
}
|
||||
var specDiv = this.createDom('div', { className: 'spec ' + status },
|
||||
this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
|
||||
this.createDom('a', {
|
||||
className: 'description',
|
||||
href: '?spec=' + encodeURIComponent(spec.getFullName()),
|
||||
title: spec.getFullName()
|
||||
}, spec.description));
|
||||
|
||||
|
||||
var resultItems = results.getItems();
|
||||
var messagesDiv = this.createDom('div', { className: 'messages' });
|
||||
for (var i = 0; i < resultItems.length; i++) {
|
||||
var result = resultItems[i];
|
||||
|
||||
if (result.type == 'log') {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
|
||||
} else if (result.type == 'expect' && result.passed && !result.passed()) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
|
||||
|
||||
if (result.trace.stack) {
|
||||
messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (messagesDiv.childNodes.length > 0) {
|
||||
specDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
this.suiteDivs[spec.suite.id].appendChild(specDiv);
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.log = function() {
|
||||
var console = jasmine.getGlobal().console;
|
||||
if (console && console.log) {
|
||||
if (console.log.apply) {
|
||||
console.log.apply(console, arguments);
|
||||
} else {
|
||||
console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.getLocation = function() {
|
||||
return this.document.location;
|
||||
};
|
||||
|
||||
jasmine.TrivialReporter.prototype.specFilter = function(spec) {
|
||||
var paramMap = {};
|
||||
var params = this.getLocation().search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
var p = params[i].split('=');
|
||||
paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
|
||||
}
|
||||
|
||||
if (!paramMap.spec) {
|
||||
return true;
|
||||
}
|
||||
return spec.getFullName().indexOf(paramMap.spec) === 0;
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
body { background-color: #eeeeee; padding: 0; margin: 5px; overflow-y: scroll; }
|
||||
|
||||
#HTMLReporter { font-size: 11px; font-family: Monaco, "Lucida Console", monospace; line-height: 14px; color: #333333; }
|
||||
#HTMLReporter a { text-decoration: none; }
|
||||
#HTMLReporter a:hover { text-decoration: underline; }
|
||||
#HTMLReporter p, #HTMLReporter h1, #HTMLReporter h2, #HTMLReporter h3, #HTMLReporter h4, #HTMLReporter h5, #HTMLReporter h6 { margin: 0; line-height: 14px; }
|
||||
#HTMLReporter .banner, #HTMLReporter .symbolSummary, #HTMLReporter .summary, #HTMLReporter .resultMessage, #HTMLReporter .specDetail .description, #HTMLReporter .alert .bar, #HTMLReporter .stackTrace { padding-left: 9px; padding-right: 9px; }
|
||||
#HTMLReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#HTMLReporter .version { color: #aaaaaa; }
|
||||
#HTMLReporter .banner { margin-top: 14px; }
|
||||
#HTMLReporter .duration { color: #aaaaaa; float: right; }
|
||||
#HTMLReporter .symbolSummary { overflow: hidden; *zoom: 1; margin: 14px 0; }
|
||||
#HTMLReporter .symbolSummary li { display: block; float: left; height: 7px; width: 14px; margin-bottom: 7px; font-size: 16px; }
|
||||
#HTMLReporter .symbolSummary li.passed { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.passed:before { color: #5e7d00; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.failed { line-height: 9px; }
|
||||
#HTMLReporter .symbolSummary li.failed:before { color: #b03911; content: "x"; font-weight: bold; margin-left: -1px; }
|
||||
#HTMLReporter .symbolSummary li.skipped { font-size: 14px; }
|
||||
#HTMLReporter .symbolSummary li.skipped:before { color: #bababa; content: "\02022"; }
|
||||
#HTMLReporter .symbolSummary li.pending { line-height: 11px; }
|
||||
#HTMLReporter .symbolSummary li.pending:before { color: #aaaaaa; content: "-"; }
|
||||
#HTMLReporter .bar { line-height: 28px; font-size: 14px; display: block; color: #eee; }
|
||||
#HTMLReporter .runningAlert { background-color: #666666; }
|
||||
#HTMLReporter .skippedAlert { background-color: #aaaaaa; }
|
||||
#HTMLReporter .skippedAlert:first-child { background-color: #333333; }
|
||||
#HTMLReporter .skippedAlert:hover { text-decoration: none; color: white; text-decoration: underline; }
|
||||
#HTMLReporter .passingAlert { background-color: #a6b779; }
|
||||
#HTMLReporter .passingAlert:first-child { background-color: #5e7d00; }
|
||||
#HTMLReporter .failingAlert { background-color: #cf867e; }
|
||||
#HTMLReporter .failingAlert:first-child { background-color: #b03911; }
|
||||
#HTMLReporter .results { margin-top: 14px; }
|
||||
#HTMLReporter #details { display: none; }
|
||||
#HTMLReporter .resultsMenu, #HTMLReporter .resultsMenu a { background-color: #fff; color: #333333; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem { font-weight: normal; text-decoration: inherit; }
|
||||
#HTMLReporter.showDetails .summaryMenuItem:hover { text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .detailsMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter.showDetails .summary { display: none; }
|
||||
#HTMLReporter.showDetails #details { display: block; }
|
||||
#HTMLReporter .summaryMenuItem { font-weight: bold; text-decoration: underline; }
|
||||
#HTMLReporter .summary { margin-top: 14px; }
|
||||
#HTMLReporter .summary .suite .suite, #HTMLReporter .summary .specSummary { margin-left: 14px; }
|
||||
#HTMLReporter .summary .specSummary.passed a { color: #5e7d00; }
|
||||
#HTMLReporter .summary .specSummary.failed a { color: #b03911; }
|
||||
#HTMLReporter .description + .suite { margin-top: 0; }
|
||||
#HTMLReporter .suite { margin-top: 14px; }
|
||||
#HTMLReporter .suite a { color: #333333; }
|
||||
#HTMLReporter #details .specDetail { margin-bottom: 28px; }
|
||||
#HTMLReporter #details .specDetail .description { display: block; color: white; background-color: #b03911; }
|
||||
#HTMLReporter .resultMessage { padding-top: 14px; color: #333333; }
|
||||
#HTMLReporter .resultMessage span.result { display: block; }
|
||||
#HTMLReporter .stackTrace { margin: 5px 0 0 0; max-height: 224px; overflow: auto; line-height: 18px; color: #666666; border: 1px solid #ddd; background: white; white-space: pre; }
|
||||
|
||||
#TrivialReporter { padding: 8px 13px; position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; background-color: white; font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; /*.resultMessage {*/ /*white-space: pre;*/ /*}*/ }
|
||||
#TrivialReporter a:visited, #TrivialReporter a { color: #303; }
|
||||
#TrivialReporter a:hover, #TrivialReporter a:active { color: blue; }
|
||||
#TrivialReporter .run_spec { float: right; padding-right: 5px; font-size: .8em; text-decoration: none; }
|
||||
#TrivialReporter .banner { color: #303; background-color: #fef; padding: 5px; }
|
||||
#TrivialReporter .logo { float: left; font-size: 1.1em; padding-left: 5px; }
|
||||
#TrivialReporter .logo .version { font-size: .6em; padding-left: 1em; }
|
||||
#TrivialReporter .runner.running { background-color: yellow; }
|
||||
#TrivialReporter .options { text-align: right; font-size: .8em; }
|
||||
#TrivialReporter .suite { border: 1px outset gray; margin: 5px 0; padding-left: 1em; }
|
||||
#TrivialReporter .suite .suite { margin: 5px; }
|
||||
#TrivialReporter .suite.passed { background-color: #dfd; }
|
||||
#TrivialReporter .suite.failed { background-color: #fdd; }
|
||||
#TrivialReporter .spec { margin: 5px; padding-left: 1em; clear: both; }
|
||||
#TrivialReporter .spec.failed, #TrivialReporter .spec.passed, #TrivialReporter .spec.skipped { padding-bottom: 5px; border: 1px solid gray; }
|
||||
#TrivialReporter .spec.failed { background-color: #fbb; border-color: red; }
|
||||
#TrivialReporter .spec.passed { background-color: #bfb; border-color: green; }
|
||||
#TrivialReporter .spec.skipped { background-color: #bbb; }
|
||||
#TrivialReporter .messages { border-left: 1px dashed gray; padding-left: 1em; padding-right: 1em; }
|
||||
#TrivialReporter .passed { background-color: #cfc; display: none; }
|
||||
#TrivialReporter .failed { background-color: #fbb; }
|
||||
#TrivialReporter .skipped { color: #777; background-color: #eee; display: none; }
|
||||
#TrivialReporter .resultMessage span.result { display: block; line-height: 2em; color: black; }
|
||||
#TrivialReporter .resultMessage .mismatch { color: black; }
|
||||
#TrivialReporter .stackTrace { white-space: pre; font-size: .8em; margin-left: 10px; max-height: 5em; overflow: auto; border: 1px inset red; padding: 1em; background: #eef; }
|
||||
#TrivialReporter .finished-at { padding-left: 1em; font-size: .6em; }
|
||||
#TrivialReporter.show-passed .passed, #TrivialReporter.show-skipped .skipped { display: block; }
|
||||
#TrivialReporter #jasmine_content { position: fixed; right: 100%; }
|
||||
#TrivialReporter .runner { border: 1px solid gray; display: block; margin: 5px 0; padding: 2px 0 2px 10px; }
|
||||
2529
bin/templates/project/assets/www/spec/lib/jasmine-1.2.0/jasmine.js
Normal file
@@ -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>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// commit 114cf5304a74ff8f7c9ff1d21cf5652298af04b0
|
||||
// commit 143f5221a6251c9cbccdedc57005c61551b97f12
|
||||
|
||||
// File generated at :: Wed Jul 18 2012 14:44:33 GMT-0700 (PDT)
|
||||
// File generated at :: Wed Sep 12 2012 12:51:58 GMT-0700 (PDT)
|
||||
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
@@ -186,13 +186,19 @@ var cordova = {
|
||||
},
|
||||
/**
|
||||
* Method to fire event from native code
|
||||
* bNoDetach is required for events which cause an exception which needs to be caught in native code
|
||||
*/
|
||||
fireDocumentEvent: function(type, data) {
|
||||
fireDocumentEvent: function(type, data, bNoDetach) {
|
||||
var evt = createEvent(type, data);
|
||||
if (typeof documentEventHandlers[type] != 'undefined') {
|
||||
setTimeout(function() {
|
||||
documentEventHandlers[type].fire(evt);
|
||||
}, 0);
|
||||
if( bNoDetach ) {
|
||||
documentEventHandlers[type].fire(evt);
|
||||
}
|
||||
else {
|
||||
setTimeout(function() {
|
||||
documentEventHandlers[type].fire(evt);
|
||||
}, 0);
|
||||
}
|
||||
} else {
|
||||
document.dispatchEvent(evt);
|
||||
}
|
||||
@@ -207,10 +213,6 @@ var cordova = {
|
||||
window.dispatchEvent(evt);
|
||||
}
|
||||
},
|
||||
// TODO: this is Android only; think about how to do this better
|
||||
shuttingDown:false,
|
||||
UsePolling:false,
|
||||
// END TODO
|
||||
|
||||
// TODO: iOS only
|
||||
// This queue holds the currently executing command and all pending
|
||||
@@ -404,7 +406,8 @@ module.exports = {
|
||||
|
||||
// file: lib/common/channel.js
|
||||
define("cordova/channel", function(require, exports, module) {
|
||||
var utils = require('cordova/utils');
|
||||
var utils = require('cordova/utils'),
|
||||
nextGuid = 1;
|
||||
|
||||
/**
|
||||
* Custom pub-sub "channel" that can have functions subscribed to it
|
||||
@@ -456,7 +459,6 @@ var Channel = function(type, opts) {
|
||||
this.type = type;
|
||||
this.handlers = {};
|
||||
this.numHandlers = 0;
|
||||
this.guid = 1;
|
||||
this.fired = false;
|
||||
this.enabled = true;
|
||||
this.events = {
|
||||
@@ -549,19 +551,19 @@ Channel.prototype.subscribe = function(f, c, g) {
|
||||
|
||||
g = g || func.observer_guid || f.observer_guid;
|
||||
if (!g) {
|
||||
// first time we've seen this subscriber
|
||||
g = this.guid++;
|
||||
}
|
||||
else {
|
||||
// subscriber already handled; dont set it twice
|
||||
return g;
|
||||
// first time any channel has seen this subscriber
|
||||
g = nextGuid++;
|
||||
}
|
||||
func.observer_guid = g;
|
||||
f.observer_guid = g;
|
||||
this.handlers[g] = func;
|
||||
this.numHandlers++;
|
||||
if (this.events.onSubscribe) this.events.onSubscribe.call(this);
|
||||
if (this.fired) func.call(this);
|
||||
|
||||
// Don't add the same handler more than once.
|
||||
if (!this.handlers[g]) {
|
||||
this.handlers[g] = func;
|
||||
this.numHandlers++;
|
||||
if (this.events.onSubscribe) this.events.onSubscribe.call(this);
|
||||
if (this.fired) func.apply(this, this.fireArgs);
|
||||
}
|
||||
return g;
|
||||
};
|
||||
|
||||
@@ -575,15 +577,14 @@ Channel.prototype.subscribeOnce = function(f, c) {
|
||||
|
||||
var g = null;
|
||||
var _this = this;
|
||||
var m = function() {
|
||||
f.apply(c || null, arguments);
|
||||
_this.unsubscribe(g);
|
||||
};
|
||||
if (this.fired) {
|
||||
if (typeof c == "object") { f = utils.close(c, f); }
|
||||
f.apply(this, this.fireArgs);
|
||||
f.apply(c || null, this.fireArgs);
|
||||
} else {
|
||||
g = this.subscribe(m);
|
||||
g = this.subscribe(function() {
|
||||
_this.unsubscribe(g);
|
||||
f.apply(c || null, arguments);
|
||||
});
|
||||
f.observer_guid = g;
|
||||
}
|
||||
return g;
|
||||
};
|
||||
@@ -599,7 +600,6 @@ Channel.prototype.unsubscribe = function(g) {
|
||||
var handler = this.handlers[g];
|
||||
if (handler) {
|
||||
if (handler.observer_guid) handler.observer_guid=null;
|
||||
this.handlers[g] = null;
|
||||
delete this.handlers[g];
|
||||
this.numHandlers--;
|
||||
if (this.events.onUnsubscribe) this.events.onUnsubscribe.call(this);
|
||||
@@ -613,14 +613,17 @@ Channel.prototype.fire = function(e) {
|
||||
if (this.enabled) {
|
||||
var fail = false;
|
||||
this.fired = true;
|
||||
for (var item in this.handlers) {
|
||||
var handler = this.handlers[item];
|
||||
if (typeof handler == 'function') {
|
||||
var rv = (handler.apply(this, arguments)===false);
|
||||
fail = fail || rv;
|
||||
}
|
||||
}
|
||||
this.fireArgs = arguments;
|
||||
// Copy the values first so that it is safe to modify it from within
|
||||
// callbacks.
|
||||
var toCall = [];
|
||||
for (var item in this.handlers) {
|
||||
toCall.push(this.handlers[item]);
|
||||
}
|
||||
for (var i = 0; i < toCall.length; ++i) {
|
||||
var rv = (toCall[i].apply(this, arguments)===false);
|
||||
fail = fail || rv;
|
||||
}
|
||||
return !fail;
|
||||
}
|
||||
return true;
|
||||
@@ -685,13 +688,6 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
PhoneGap:{
|
||||
children: {
|
||||
exec: {
|
||||
path: 'cordova/exec'
|
||||
}
|
||||
}
|
||||
},
|
||||
navigator: {
|
||||
children: {
|
||||
notification: {
|
||||
@@ -890,75 +886,170 @@ define("cordova/exec", function(require, exports, module) {
|
||||
* @param {String} action Action to be run in cordova
|
||||
* @param {String[]} [args] Zero or more arguments to pass to the method
|
||||
*/
|
||||
var cordova = require('cordova');
|
||||
var cordova = require('cordova'),
|
||||
callback = require('cordova/plugin/android/callback'),
|
||||
polling = require('cordova/plugin/android/polling'),
|
||||
jsToNativeBridgeMode,
|
||||
nativeToJsBridgeMode,
|
||||
jsToNativeModes = {
|
||||
PROMPT: 0,
|
||||
JS_OBJECT: 1,
|
||||
// This mode is currently for benchmarking purposes only. It must be enabled
|
||||
// on the native side through the ENABLE_LOCATION_CHANGE_EXEC_MODE
|
||||
// constant within CordovaWebViewClient.java before it will work.
|
||||
LOCATION_CHANGE: 2
|
||||
},
|
||||
nativeToJsModes = {
|
||||
// Polls for messages using the prompt() bridge.
|
||||
POLLING: 0,
|
||||
// Does an XHR to a local server, which will send back messages. This is
|
||||
// broken on ICS when a proxy server is configured.
|
||||
HANGING_GET: 1,
|
||||
// For LOAD_URL to be viable, it would need to have a work-around for
|
||||
// the bug where the soft-keyboard gets dismissed when a message is sent.
|
||||
LOAD_URL: 2,
|
||||
// For the ONLINE_EVENT to be viable, it would need to intercept all event
|
||||
// listeners (both through addEventListener and window.ononline) as well
|
||||
// as set the navigator property itself.
|
||||
ONLINE_EVENT: 3,
|
||||
// Uses reflection to access private APIs of the WebView that can send JS
|
||||
// to be executed.
|
||||
// Requires Android 3.2.4 or above.
|
||||
PRIVATE_API: 4
|
||||
};
|
||||
|
||||
module.exports = function(success, fail, service, action, args) {
|
||||
try {
|
||||
var callbackId = service + cordova.callbackId++;
|
||||
if (success || fail) {
|
||||
cordova.callbacks[callbackId] = {success:success, fail:fail};
|
||||
function androidExec(success, fail, service, action, args) {
|
||||
// Set default bridge modes if they have not already been set.
|
||||
if (jsToNativeBridgeMode === undefined) {
|
||||
androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT);
|
||||
}
|
||||
|
||||
var r = prompt(JSON.stringify(args), "gap:"+JSON.stringify([service, action, callbackId, true]));
|
||||
|
||||
// If a result was returned
|
||||
if (r.length > 0) {
|
||||
var v = JSON.parse(r);
|
||||
|
||||
// If status is OK, then return value back to caller
|
||||
if (v.status === cordova.callbackStatus.OK) {
|
||||
|
||||
// If there is a success callback, then call it now with
|
||||
// returned value
|
||||
if (success) {
|
||||
try {
|
||||
success(v.message);
|
||||
} catch (e) {
|
||||
console.log("Error in success callback: " + callbackId + " = " + e);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return v.message;
|
||||
}
|
||||
|
||||
// If no result
|
||||
else if (v.status === cordova.callbackStatus.NO_RESULT) {
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
|
||||
// If error, then display error
|
||||
else {
|
||||
console.log("Error: Status="+v.status+" Message="+v.message);
|
||||
|
||||
// If there is a fail callback, then call it now with returned value
|
||||
if (fail) {
|
||||
try {
|
||||
fail(v.message);
|
||||
}
|
||||
catch (e1) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e1);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
if (nativeToJsBridgeMode === undefined) {
|
||||
if (callback.isAvailable()) {
|
||||
androidExec.setNativeToJsBridgeMode(nativeToJsModes.HANGING_GET);
|
||||
} else {
|
||||
androidExec.setNativeToJsBridgeMode(nativeToJsModes.POLLING);
|
||||
}
|
||||
}
|
||||
} catch (e2) {
|
||||
console.log("Error: "+e2);
|
||||
}
|
||||
try {
|
||||
var callbackId = service + cordova.callbackId++,
|
||||
argsJson = JSON.stringify(args),
|
||||
result;
|
||||
if (success || fail) {
|
||||
cordova.callbacks[callbackId] = {success:success, fail:fail};
|
||||
}
|
||||
|
||||
if (jsToNativeBridgeMode == jsToNativeModes.LOCATION_CHANGE) {
|
||||
window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;
|
||||
} else if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT) {
|
||||
// Explicit cast to string is required on Android 2.1 to convert from
|
||||
// a Java string to a JS string.
|
||||
result = '' + _cordovaExec.exec(service, action, callbackId, argsJson);
|
||||
} else {
|
||||
result = prompt(argsJson, "gap:"+JSON.stringify([service, action, callbackId, true]));
|
||||
}
|
||||
|
||||
// If a result was returned
|
||||
if (result) {
|
||||
var v = JSON.parse(result);
|
||||
|
||||
// If status is OK, then return value back to caller
|
||||
if (v.status === cordova.callbackStatus.OK) {
|
||||
|
||||
// If there is a success callback, then call it now with
|
||||
// returned value
|
||||
if (success) {
|
||||
try {
|
||||
success(v.message);
|
||||
} catch (e) {
|
||||
console.log("Error in success callback: " + callbackId + " = " + e);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return v.message;
|
||||
}
|
||||
|
||||
// If no result
|
||||
else if (v.status === cordova.callbackStatus.NO_RESULT) {
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
|
||||
// If error, then display error
|
||||
else {
|
||||
console.log("Error: Status="+v.status+" Message="+v.message);
|
||||
|
||||
// If there is a fail callback, then call it now with returned value
|
||||
if (fail) {
|
||||
try {
|
||||
fail(v.message);
|
||||
}
|
||||
catch (e1) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e1);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete cordova.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (e2) {
|
||||
console.log("Error: "+e2);
|
||||
}
|
||||
}
|
||||
|
||||
function onOnLineEvent(e) {
|
||||
while (polling.pollOnce());
|
||||
}
|
||||
|
||||
androidExec.jsToNativeModes = jsToNativeModes;
|
||||
androidExec.nativeToJsModes = nativeToJsModes;
|
||||
|
||||
androidExec.setJsToNativeBridgeMode = function(mode) {
|
||||
if (mode == jsToNativeModes.JS_OBJECT && !window._cordovaExec) {
|
||||
console.log('Falling back on PROMPT mode since _cordovaExec is missing.');
|
||||
mode = jsToNativeModes.PROMPT;
|
||||
}
|
||||
jsToNativeBridgeMode = mode;
|
||||
};
|
||||
|
||||
androidExec.setNativeToJsBridgeMode = function(mode) {
|
||||
if (mode == nativeToJsBridgeMode) {
|
||||
return;
|
||||
}
|
||||
if (nativeToJsBridgeMode == nativeToJsModes.POLLING) {
|
||||
polling.stop();
|
||||
} else if (nativeToJsBridgeMode == nativeToJsModes.HANGING_GET) {
|
||||
callback.stop();
|
||||
} else if (nativeToJsBridgeMode == nativeToJsModes.ONLINE_EVENT) {
|
||||
window.removeEventListener('online', onOnLineEvent, false);
|
||||
window.removeEventListener('offline', onOnLineEvent, false);
|
||||
}
|
||||
|
||||
nativeToJsBridgeMode = mode;
|
||||
// Tell the native side to switch modes.
|
||||
prompt(mode, "gap_bridge_mode:");
|
||||
|
||||
if (mode == nativeToJsModes.POLLING) {
|
||||
polling.start();
|
||||
} else if (mode == nativeToJsModes.HANGING_GET) {
|
||||
callback.start();
|
||||
} else if (mode == nativeToJsModes.ONLINE_EVENT) {
|
||||
window.addEventListener('online', onOnLineEvent, false);
|
||||
window.addEventListener('offline', onOnLineEvent, false);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = androidExec;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/platform.js
|
||||
@@ -968,34 +1059,8 @@ module.exports = {
|
||||
initialize:function() {
|
||||
var channel = require("cordova/channel"),
|
||||
cordova = require('cordova'),
|
||||
callback = require('cordova/plugin/android/callback'),
|
||||
polling = require('cordova/plugin/android/polling'),
|
||||
exec = require('cordova/exec');
|
||||
|
||||
channel.onDestroy.subscribe(function() {
|
||||
cordova.shuttingDown = true;
|
||||
});
|
||||
|
||||
// Start listening for XHR callbacks
|
||||
// Figure out which bridge approach will work on this Android
|
||||
// device: polling or XHR-based callbacks
|
||||
setTimeout(function() {
|
||||
if (cordova.UsePolling) {
|
||||
polling();
|
||||
}
|
||||
else {
|
||||
var isPolling = prompt("usePolling", "gap_callbackServer:");
|
||||
cordova.UsePolling = isPolling;
|
||||
if (isPolling == "true") {
|
||||
cordova.UsePolling = true;
|
||||
polling();
|
||||
} else {
|
||||
cordova.UsePolling = false;
|
||||
callback();
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
|
||||
// Inject a listener for the backbutton on the document.
|
||||
var backButtonChannel = cordova.addDocumentEventHandler('backbutton', {
|
||||
onSubscribe:function() {
|
||||
@@ -2585,10 +2650,12 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
|
||||
var mimeType = null;
|
||||
var params = null;
|
||||
var chunkedMode = true;
|
||||
var headers = null;
|
||||
if (options) {
|
||||
fileKey = options.fileKey;
|
||||
fileName = options.fileName;
|
||||
mimeType = options.mimeType;
|
||||
headers = options.headers;
|
||||
if (options.chunkedMode !== null || typeof options.chunkedMode != "undefined") {
|
||||
chunkedMode = options.chunkedMode;
|
||||
}
|
||||
@@ -2605,7 +2672,7 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
|
||||
errorCallback(error);
|
||||
};
|
||||
|
||||
exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode]);
|
||||
exec(successCallback, fail, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, trustAllHosts, chunkedMode, headers]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -2675,15 +2742,19 @@ define("cordova/plugin/FileUploadOptions", function(require, exports, module) {
|
||||
* @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
|
||||
* @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
|
||||
* @param params {Object} Object with key: value params to send to the server.
|
||||
* @param headers {Object} Keys are header names, values are header values. Multiple
|
||||
* headers of the same name are not supported.
|
||||
*/
|
||||
var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
|
||||
var FileUploadOptions = function(fileKey, fileName, mimeType, params, headers) {
|
||||
this.fileKey = fileKey || null;
|
||||
this.fileName = fileName || null;
|
||||
this.mimeType = mimeType || null;
|
||||
this.params = params || null;
|
||||
this.headers = headers || null;
|
||||
};
|
||||
|
||||
module.exports = FileUploadOptions;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/plugin/FileUploadResult.js
|
||||
@@ -2871,7 +2942,7 @@ FileWriter.prototype.seek = function(offset) {
|
||||
if (offset < 0) {
|
||||
this.position = Math.max(offset + this.length, 0);
|
||||
}
|
||||
// Offset is bigger then file size so set position
|
||||
// Offset is bigger than file size so set position
|
||||
// to the end of the file.
|
||||
else if (offset > this.length) {
|
||||
this.position = this.length;
|
||||
@@ -3078,7 +3149,6 @@ Media.prototype.stop = function() {
|
||||
var me = this;
|
||||
exec(function() {
|
||||
me._position = 0;
|
||||
me.successCallback();
|
||||
}, this.errorCallback, "Media", "stopPlayingAudio", [this.id]);
|
||||
};
|
||||
|
||||
@@ -3124,14 +3194,14 @@ Media.prototype.getCurrentPosition = function(success, fail) {
|
||||
* Start recording audio file.
|
||||
*/
|
||||
Media.prototype.startRecord = function() {
|
||||
exec(this.successCallback, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
|
||||
exec(null, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop recording audio file.
|
||||
*/
|
||||
Media.prototype.stopRecord = function() {
|
||||
exec(this.successCallback, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
|
||||
exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -3153,34 +3223,39 @@ Media.prototype.setVolume = function(volume) {
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
* @param status The status code (int)
|
||||
* @param msg The status message (string)
|
||||
* @param msgType The 'type' of update this is
|
||||
* @param value Use of value is determined by the msgType
|
||||
*/
|
||||
Media.onStatus = function(id, msg, value) {
|
||||
Media.onStatus = function(id, msgType, value) {
|
||||
|
||||
var media = mediaObjects[id];
|
||||
// If state update
|
||||
if (msg === Media.MEDIA_STATE) {
|
||||
if (value === Media.MEDIA_STOPPED) {
|
||||
if (media.successCallback) {
|
||||
media.successCallback();
|
||||
}
|
||||
}
|
||||
if (media.statusCallback) {
|
||||
media.statusCallback(value);
|
||||
|
||||
if(media) {
|
||||
switch(msgType) {
|
||||
case Media.MEDIA_STATE :
|
||||
media.statusCallback && media.statusCallback(value);
|
||||
if(value == Media.MEDIA_STOPPED) {
|
||||
media.successCallback && media.successCallback();
|
||||
}
|
||||
break;
|
||||
case Media.MEDIA_DURATION :
|
||||
media._duration = value;
|
||||
break;
|
||||
case Media.MEDIA_ERROR :
|
||||
media.errorCallback && media.errorCallback(value);
|
||||
break;
|
||||
case Media.MEDIA_POSITION :
|
||||
media._position = Number(value);
|
||||
break;
|
||||
default :
|
||||
console && console.error && console.error("Unhandled Media.onStatus :: " + msgType);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (msg === Media.MEDIA_DURATION) {
|
||||
media._duration = value;
|
||||
}
|
||||
else if (msg === Media.MEDIA_ERROR) {
|
||||
if (media.errorCallback) {
|
||||
// value should be a MediaError object when msg == MEDIA_ERROR
|
||||
media.errorCallback(value);
|
||||
}
|
||||
}
|
||||
else if (msg === Media.MEDIA_POSITION) {
|
||||
media._position = value;
|
||||
else {
|
||||
console && console.error && console.error("Received Media.onStatus callback for unknown media :: " + id);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
module.exports = Media;
|
||||
@@ -3190,20 +3265,36 @@ module.exports = Media;
|
||||
define("cordova/plugin/MediaError", function(require, exports, module) {
|
||||
/**
|
||||
* This class contains information about any Media errors.
|
||||
* @constructor
|
||||
*/
|
||||
var MediaError = function(code, msg) {
|
||||
this.code = (code !== undefined ? code : null);
|
||||
this.message = msg || "";
|
||||
};
|
||||
*/
|
||||
/*
|
||||
According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror
|
||||
We should never be creating these objects, we should just implement the interface
|
||||
which has 1 property for an instance, 'code'
|
||||
|
||||
MediaError.MEDIA_ERR_NONE_ACTIVE = 0;
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
MediaError.MEDIA_ERR_DECODE = 3;
|
||||
MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
|
||||
instead of doing :
|
||||
errorCallbackFunction( new MediaError(3,'msg') );
|
||||
we should simply use a literal :
|
||||
errorCallbackFunction( {'code':3} );
|
||||
*/
|
||||
|
||||
if(!MediaError) {
|
||||
var MediaError = function(code, msg) {
|
||||
this.code = (typeof code != 'undefined') ? code : null;
|
||||
this.message = msg || ""; // message is NON-standard! do not use!
|
||||
};
|
||||
}
|
||||
|
||||
MediaError.MEDIA_ERR_NONE_ACTIVE = MediaError.MEDIA_ERR_NONE_ACTIVE || 0;
|
||||
MediaError.MEDIA_ERR_ABORTED = MediaError.MEDIA_ERR_ABORTED || 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = MediaError.MEDIA_ERR_NETWORK || 2;
|
||||
MediaError.MEDIA_ERR_DECODE = MediaError.MEDIA_ERR_DECODE || 3;
|
||||
MediaError.MEDIA_ERR_NONE_SUPPORTED = MediaError.MEDIA_ERR_NONE_SUPPORTED || 4;
|
||||
// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below.
|
||||
// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes
|
||||
MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4;
|
||||
|
||||
module.exports = MediaError;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/plugin/MediaFile.js
|
||||
@@ -3241,28 +3332,6 @@ MediaFile.prototype.getFormatData = function(successCallback, errorCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: can we axe this?
|
||||
/**
|
||||
* Casts a PluginResult message property (array of objects) to an array of MediaFile objects
|
||||
* (used in Objective-C and Android)
|
||||
*
|
||||
* @param {PluginResult} pluginResult
|
||||
*/
|
||||
MediaFile.cast = function(pluginResult) {
|
||||
var mediaFiles = [];
|
||||
for (var i=0; i<pluginResult.message.length; i++) {
|
||||
var mediaFile = new MediaFile();
|
||||
mediaFile.name = pluginResult.message[i].name;
|
||||
mediaFile.fullPath = pluginResult.message[i].fullPath;
|
||||
mediaFile.type = pluginResult.message[i].type;
|
||||
mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
|
||||
mediaFile.size = pluginResult.message[i].size;
|
||||
mediaFiles.push(mediaFile);
|
||||
}
|
||||
pluginResult.message = mediaFiles;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
module.exports = MediaFile;
|
||||
|
||||
});
|
||||
@@ -3521,7 +3590,7 @@ var accelerometer = {
|
||||
|
||||
if (running) {
|
||||
// If we're already running then immediately invoke the success callback
|
||||
// but only if we have retreived a value, sample code does not check for null ...
|
||||
// but only if we have retrieved a value, sample code does not check for null ...
|
||||
if(accel) {
|
||||
successCallback(accel);
|
||||
}
|
||||
@@ -3630,89 +3699,82 @@ module.exports = {
|
||||
define("cordova/plugin/android/callback", function(require, exports, module) {
|
||||
var port = null,
|
||||
token = null,
|
||||
cordova = require('cordova'),
|
||||
polling = require('cordova/plugin/android/polling'),
|
||||
callback = function() {
|
||||
// Exit if shutting down app
|
||||
if (cordova.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
xmlhttp;
|
||||
|
||||
// If polling flag was changed, start using polling from now on
|
||||
if (cordova.UsePolling) {
|
||||
polling();
|
||||
return;
|
||||
}
|
||||
function startXhr() {
|
||||
// cordova/exec depends on this module, so we can't require cordova/exec on the module level.
|
||||
var exec = require('cordova/exec'),
|
||||
xmlhttp = new XMLHttpRequest();
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
// Callback function when XMLHttpRequest is ready
|
||||
xmlhttp.onreadystatechange=function(){
|
||||
if (!xmlhttp) {
|
||||
return;
|
||||
}
|
||||
if (xmlhttp.readyState === 4){
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status === 200) {
|
||||
|
||||
// Callback function when XMLHttpRequest is ready
|
||||
xmlhttp.onreadystatechange=function(){
|
||||
if(xmlhttp.readyState === 4){
|
||||
// Need to url decode the response
|
||||
var msg = decodeURIComponent(xmlhttp.responseText);
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(msg);
|
||||
}
|
||||
catch (e) {
|
||||
// If we're getting an error here, seeing the message will help in debugging
|
||||
console.log("JSCallback: Message from Server: " + msg);
|
||||
console.log("JSCallback Error: "+e);
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(startXhr, 1);
|
||||
}
|
||||
|
||||
// Exit if shutting down app
|
||||
if (cordova.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
// If callback ping (used to keep XHR request from timing out)
|
||||
else if (xmlhttp.status === 404) {
|
||||
setTimeout(startXhr, 10);
|
||||
}
|
||||
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status === 200) {
|
||||
// 0 == Page is unloading.
|
||||
// 400 == Bad request.
|
||||
// 403 == invalid token.
|
||||
// 503 == server stopped.
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed with status " + xmlhttp.status);
|
||||
exec.setNativeToJsBridgeMode(exec.nativeToJsModes.POLLING);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Need to url decode the response
|
||||
var msg = decodeURIComponent(xmlhttp.responseText);
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(msg);
|
||||
}
|
||||
catch (e) {
|
||||
// If we're getting an error here, seeing the message will help in debugging
|
||||
console.log("JSCallback: Message from Server: " + msg);
|
||||
console.log("JSCallback Error: "+e);
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(callback, 1);
|
||||
}
|
||||
if (port === null) {
|
||||
port = prompt("getPort", "gap_callbackServer:");
|
||||
}
|
||||
if (token === null) {
|
||||
token = prompt("getToken", "gap_callbackServer:");
|
||||
}
|
||||
xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
|
||||
xmlhttp.send();
|
||||
}
|
||||
|
||||
// If callback ping (used to keep XHR request from timing out)
|
||||
else if (xmlhttp.status === 404) {
|
||||
setTimeout(callback, 10);
|
||||
}
|
||||
module.exports = {
|
||||
start: function() {
|
||||
startXhr();
|
||||
},
|
||||
|
||||
// If security error
|
||||
else if (xmlhttp.status === 403) {
|
||||
console.log("JSCallback Error: Invalid token. Stopping callbacks.");
|
||||
}
|
||||
stop: function() {
|
||||
if (xmlhttp) {
|
||||
var tmp = xmlhttp;
|
||||
xmlhttp = null;
|
||||
tmp.abort();
|
||||
}
|
||||
},
|
||||
|
||||
// If server is stopping
|
||||
else if (xmlhttp.status === 503) {
|
||||
console.log("JSCallback Server Closed: Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If request wasn't GET
|
||||
else if (xmlhttp.status === 400) {
|
||||
console.log("JSCallback Error: Bad request. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If error, revert to polling
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed.");
|
||||
cordova.UsePolling = true;
|
||||
polling();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (port === null) {
|
||||
port = prompt("getPort", "gap_callbackServer:");
|
||||
}
|
||||
if (token === null) {
|
||||
token = prompt("getToken", "gap_callbackServer:");
|
||||
}
|
||||
xmlhttp.open("GET", "http://127.0.0.1:"+port+"/"+token , true);
|
||||
xmlhttp.send();
|
||||
isAvailable: function() {
|
||||
return ("true" != prompt("usePolling", "gap_callbackServer:"));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = callback;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/plugin/android/device.js
|
||||
@@ -3819,38 +3881,47 @@ module.exports = {
|
||||
// file: lib/android/plugin/android/polling.js
|
||||
define("cordova/plugin/android/polling", function(require, exports, module) {
|
||||
var cordova = require('cordova'),
|
||||
period = 50,
|
||||
polling = function() {
|
||||
// Exit if shutting down app
|
||||
if (cordova.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
POLL_INTERVAL = 50,
|
||||
enabled = false;
|
||||
|
||||
// If polling flag was changed, stop using polling from now on and switch to XHR server / callback
|
||||
if (!cordova.UsePolling) {
|
||||
require('cordova/plugin/android/callback')();
|
||||
return;
|
||||
}
|
||||
function pollOnce() {
|
||||
var msg = prompt("", "gap_poll:");
|
||||
if (msg) {
|
||||
try {
|
||||
eval(""+msg);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("JSCallbackPolling: Message from Server: " + msg);
|
||||
console.log("JSCallbackPolling Error: "+e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var msg = prompt("", "gap_poll:");
|
||||
if (msg) {
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(""+msg);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("JSCallbackPolling: Message from Server: " + msg);
|
||||
console.log("JSCallbackPolling Error: "+e);
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(polling, 1);
|
||||
}
|
||||
else {
|
||||
setTimeout(polling, period);
|
||||
}
|
||||
function doPoll() {
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
var nextDelay = POLL_INTERVAL;
|
||||
if (pollOnce()) {
|
||||
nextDelay = 0;
|
||||
}
|
||||
setTimeout(doPoll, nextDelay);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
start: function() {
|
||||
enabled = true;
|
||||
setTimeout(doPoll, 1);
|
||||
},
|
||||
stop: function() {
|
||||
enabled = false;
|
||||
},
|
||||
pollOnce: pollOnce
|
||||
};
|
||||
|
||||
module.exports = polling;
|
||||
|
||||
});
|
||||
|
||||
// file: lib/android/plugin/android/storage.js
|
||||
@@ -4723,7 +4794,7 @@ var contacts = {
|
||||
* This function creates a new contact, but it does not persist the contact
|
||||
* to device storage. To persist the contact to device storage, invoke
|
||||
* contact.save().
|
||||
* @param properties an object who's properties will be examined to create a new Contact
|
||||
* @param properties an object whose properties will be examined to create a new Contact
|
||||
* @returns new Contact object
|
||||
*/
|
||||
create:function(properties) {
|
||||
@@ -4808,6 +4879,25 @@ Device.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
|
||||
module.exports = new Device();
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/plugin/echo.js
|
||||
define("cordova/plugin/echo", function(require, exports, module) {
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
/**
|
||||
* Sends the given message through exec() to the Echo plugink, which sends it back to the successCallback.
|
||||
* @param successCallback invoked with a FileSystem object
|
||||
* @param errorCallback invoked if error occurs retrieving file system
|
||||
* @param message The string to be echoed.
|
||||
* @param forceAsync Whether to force an async return value (for testing native->js bridge).
|
||||
*/
|
||||
module.exports = function(successCallback, errorCallback, message, forceAsync) {
|
||||
var action = forceAsync ? 'echoAsync' : 'echo';
|
||||
exec(successCallback, errorCallback, "Echo", action, [message]);
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
// file: lib/common/plugin/geolocation.js
|
||||
@@ -4918,7 +5008,7 @@ var geolocation = {
|
||||
} else if (options.timeout === 0) {
|
||||
fail({
|
||||
code:PositionError.TIMEOUT,
|
||||
message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceed's provided PositionOptions' maximumAge parameter."
|
||||
message:"timeout value in PositionOptions set to 0 and no cached Position object available, or cached Position object's age exceeds provided PositionOptions' maximumAge parameter."
|
||||
});
|
||||
// Otherwise we have to call into native to retrieve a position.
|
||||
} else {
|
||||
@@ -5082,7 +5172,7 @@ CurrentLevel = LevelsMap.WARN;
|
||||
*
|
||||
* The value used determines which messages get printed. The logging
|
||||
* values above are in order, and only messages logged at the logging
|
||||
* level or above will actually be displayed to the user. Eg, the
|
||||
* level or above will actually be displayed to the user. E.g., the
|
||||
* default level is WARN, so only messages logged with LOG, ERROR, or
|
||||
* WARN will be displayed; INFO and DEBUG messages will be ignored.
|
||||
*/
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="cordova-2.0.0.js"></script>
|
||||
<script src="cordova-2.1.0.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
|
||||
|
||||
@@ -27,10 +27,11 @@
|
||||
|
||||
<!-- <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 -->
|
||||
<access origin=".*"/>
|
||||
|
||||
<log level="DEBUG"/>
|
||||
<preference name="useBrowserHistory" value="false" />
|
||||
<preference name="exit-on-suspend" value="false" />
|
||||
<plugins>
|
||||
<plugin name="App" value="org.apache.cordova.App"/>
|
||||
<plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
|
||||
@@ -49,6 +50,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,27 +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 com.phonegap.api;
|
||||
|
||||
/**
|
||||
* Plugin interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the PluginManager.
|
||||
*/
|
||||
public interface IPlugin extends org.apache.cordova.api.IPlugin {
|
||||
}
|
||||
@@ -1,28 +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 com.phonegap.api;
|
||||
|
||||
/**
|
||||
* Log to Android logging system.
|
||||
*
|
||||
* Log message can be a string or a printf formatted string with arguments.
|
||||
* See http://developer.android.com/reference/java/util/Formatter.html
|
||||
*/
|
||||
public class LOG extends org.apache.cordova.api.LOG {
|
||||
}
|
||||
@@ -1,28 +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 com.phonegap.api;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
/**
|
||||
* The Cordova activity abstract class that is extended by DroidGap.
|
||||
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
||||
*/
|
||||
public abstract class PhonegapActivity extends Activity implements org.apache.cordova.api.CordovaInterface {
|
||||
}
|
||||
@@ -1,27 +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 com.phonegap.api;
|
||||
|
||||
/**
|
||||
* Plugin interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the PluginManager.
|
||||
*/
|
||||
public abstract class Plugin extends org.apache.cordova.api.Plugin {
|
||||
}
|
||||
@@ -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 com.phonegap.api;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
||||
*
|
||||
* Calling native plugin code can be done by calling PluginManager.exec(...)
|
||||
* from JavaScript.
|
||||
*/
|
||||
public class PluginManager extends org.apache.cordova.api.PluginManager {
|
||||
|
||||
public PluginManager(WebView app, CordovaInterface ctx) throws Exception {
|
||||
super((CordovaWebView) app, ctx);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +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 com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class PluginResult extends org.apache.cordova.api.PluginResult {
|
||||
|
||||
public PluginResult(Status status) {
|
||||
super(status);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, String message) {
|
||||
super(status, message);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONArray message) {
|
||||
super(status, message);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONObject message) {
|
||||
super(status, message);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, int i) {
|
||||
super(status, i);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, float f) {
|
||||
super(status, f);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, boolean b) {
|
||||
super(status, b);
|
||||
}
|
||||
}
|
||||
@@ -176,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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
@@ -97,10 +98,10 @@ public class AudioHandler extends Plugin {
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("create")) {
|
||||
String id = args.getString(0);
|
||||
String src = args.getString(1);
|
||||
AudioPlayer audio = new AudioPlayer(this, id, src);
|
||||
this.players.put(id, audio);
|
||||
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));
|
||||
@@ -198,12 +199,12 @@ public class AudioHandler extends Plugin {
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecordingAudio(String id, String file) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if ( audio == null) {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if ( audio == null) {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
audio.startRecording(file);
|
||||
}
|
||||
audio.startRecording(file);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,7 +215,6 @@ public class AudioHandler extends Plugin {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopRecording();
|
||||
this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,19 +43,19 @@ import org.apache.cordova.AudioPlayer.MODE;
|
||||
* sdcard: file name is just sound.mp3
|
||||
*/
|
||||
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
|
||||
|
||||
|
||||
// AudioPlayer modes
|
||||
public enum MODE { NONE, PLAY, RECORD };
|
||||
|
||||
|
||||
// AudioPlayer states
|
||||
public enum STATE { MEDIA_NONE,
|
||||
MEDIA_LOADING,
|
||||
MEDIA_STARTING,
|
||||
MEDIA_RUNNING,
|
||||
MEDIA_PAUSED,
|
||||
MEDIA_STOPPED
|
||||
MEDIA_STOPPED,
|
||||
MEDIA_LOADING
|
||||
};
|
||||
|
||||
|
||||
private static final String LOG_TAG = "AudioPlayer";
|
||||
|
||||
// AudioPlayer message ids
|
||||
@@ -70,7 +70,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
private static int MEDIA_ERR_NETWORK = 2;
|
||||
private static int MEDIA_ERR_DECODE = 3;
|
||||
private static int MEDIA_ERR_NONE_SUPPORTED = 4;
|
||||
|
||||
|
||||
private AudioHandler handler; // The AudioHandler object
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private MODE mode = MODE.NONE; // Playback or Recording mode
|
||||
@@ -81,26 +81,27 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
|
||||
|
||||
private MediaPlayer player = null; // Audio player object
|
||||
private boolean prepareOnly = true; // playback after file prepare flag
|
||||
private int seekOnPrepared = 0; // seek to this location once media is prepared
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param handler The audio handler object
|
||||
* @param id The id of this audio player
|
||||
*/
|
||||
public AudioPlayer(AudioHandler handler, String id, String file) {
|
||||
this.handler = handler;
|
||||
this.id = id;
|
||||
this.audioFile = file;
|
||||
this.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";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -127,7 +128,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
/**
|
||||
* Start recording the specified file.
|
||||
*
|
||||
*
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecording(String file) {
|
||||
@@ -137,26 +138,22 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
break;
|
||||
case NONE:
|
||||
// Make sure we're not already recording
|
||||
if (this.recorder == null) {
|
||||
this.audioFile = file;
|
||||
this.recorder = new MediaRecorder();
|
||||
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
||||
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
||||
this.recorder.setOutputFile(this.tempFile);
|
||||
try {
|
||||
this.recorder.prepare();
|
||||
this.recorder.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
this.audioFile = file;
|
||||
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
||||
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
||||
this.recorder.setOutputFile(this.tempFile);
|
||||
try {
|
||||
this.recorder.prepare();
|
||||
this.recorder.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
break;
|
||||
case RECORD:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
|
||||
@@ -191,6 +188,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
this.recorder.stop();
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
this.recorder.reset();
|
||||
this.moveFile(this.audioFile);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -202,18 +200,18 @@ 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.readyPlayer(file)) {
|
||||
if (this.readyPlayer(file) && this.player != null) {
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //insures this is always reset
|
||||
} else {
|
||||
//
|
||||
this.prepareOnly = false;
|
||||
}
|
||||
}
|
||||
@@ -238,7 +236,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void pausePlaying() {
|
||||
|
||||
// If playing, then pause
|
||||
if (this.state == STATE.MEDIA_RUNNING) {
|
||||
if (this.state == STATE.MEDIA_RUNNING && this.player != null) {
|
||||
this.player.pause();
|
||||
this.setState(STATE.MEDIA_PAUSED);
|
||||
}
|
||||
@@ -253,7 +251,9 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
*/
|
||||
public void stopPlaying() {
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
this.player.stop();
|
||||
this.player.pause();
|
||||
this.player.seekTo(0);
|
||||
Log.d(LOG_TAG, "stopPlaying is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
else {
|
||||
@@ -264,16 +264,17 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
/**
|
||||
* Callback to be invoked when playback of a media source has completed.
|
||||
*
|
||||
* @param player The MediaPlayer that reached the end of the file
|
||||
*
|
||||
* @param player The MediaPlayer that reached the end of the file
|
||||
*/
|
||||
public void onCompletion(MediaPlayer player) {
|
||||
Log.d(LOG_TAG, "on completion is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
*
|
||||
*
|
||||
* @return position in msec or -1 if not playing
|
||||
*/
|
||||
public long getCurrentPosition() {
|
||||
@@ -290,7 +291,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
/**
|
||||
* Determine if playback file is streaming or local.
|
||||
* It is streaming if file name starts with "http://"
|
||||
*
|
||||
*
|
||||
* @param file The file name
|
||||
* @return T=streaming, F=local
|
||||
*/
|
||||
@@ -305,7 +306,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
/**
|
||||
* Get the duration of the audio file.
|
||||
*
|
||||
*
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
* -1=can't be determined
|
||||
@@ -335,17 +336,20 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the media source is ready for playback.
|
||||
*
|
||||
* @param player The MediaPlayer that is ready for playback
|
||||
* Callback to be invoked when the media source is ready for playback.
|
||||
*
|
||||
* @param player The MediaPlayer that is ready for playback
|
||||
*/
|
||||
public void onPrepared(MediaPlayer player) {
|
||||
// Listen for playback completion
|
||||
this.player.setOnCompletionListener(this);
|
||||
// seek to any location received while not prepared
|
||||
this.seekToPlaying(this.seekOnPrepared);
|
||||
// If start playing after prepared
|
||||
if (!this.prepareOnly) {
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //reset only when played
|
||||
} else {
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
}
|
||||
@@ -353,17 +357,14 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
this.duration = getDurationInSeconds();
|
||||
// reset prepare only flag
|
||||
this.prepareOnly = true;
|
||||
// seek to any location received while not prepared
|
||||
this.seekToPlaying(this.seekOnPrepared);
|
||||
// reset seek location
|
||||
this.seekOnPrepared = 0;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
|
||||
}
|
||||
|
||||
/**
|
||||
* By default Android returns the length of audio in mills but we want seconds
|
||||
*
|
||||
*
|
||||
* @return length of clip in seconds
|
||||
*/
|
||||
private float getDurationInSeconds() {
|
||||
@@ -373,7 +374,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
/**
|
||||
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||
* (other errors will throw exceptions at method call time).
|
||||
*
|
||||
*
|
||||
* @param player the MediaPlayer the error pertains to
|
||||
* @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED)
|
||||
* @param arg2 an extra code, specific to the error.
|
||||
@@ -392,19 +393,19 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
/**
|
||||
* Set the state and send it to JavaScript.
|
||||
*
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
private void setState(STATE state) {
|
||||
if (this.state != state) {
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + this.state.ordinal() + ");");
|
||||
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) {
|
||||
@@ -414,10 +415,10 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the audio state.
|
||||
*
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getState() {
|
||||
@@ -432,7 +433,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void setVolume(float volume) {
|
||||
this.player.setVolume(volume, volume);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* attempts to put the player in play mode
|
||||
* @return true if in playmode, false otherwise
|
||||
@@ -451,7 +452,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* attempts to initialize the media player for playback
|
||||
* @param file the file to play
|
||||
@@ -467,12 +468,12 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
return false;
|
||||
case MEDIA_LOADING:
|
||||
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING insntead
|
||||
Log.d(LOG_TAG, "AudioPlayer Loading: startPlaying() called during media preparation: " + STATE.MEDIA_STARTING.ordinal());
|
||||
//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:
|
||||
@@ -492,7 +493,6 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
//if we had to prepare= the file, we won't be in the correct state for playback
|
||||
@@ -505,13 +505,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* load audio file
|
||||
* @throws IOException
|
||||
* @throws IllegalStateException
|
||||
* @throws SecurityException
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IOException
|
||||
* @throws IllegalStateException
|
||||
* @throws SecurityException
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void loadAudioFile(String file) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
|
||||
if (this.isStreaming(file)) {
|
||||
@@ -520,7 +520,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
//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.setOnPreparedListener(this);
|
||||
this.player.prepareAsync();
|
||||
}
|
||||
else {
|
||||
@@ -534,13 +534,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
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.setOnPreparedListener(this);
|
||||
this.player.prepare();
|
||||
|
||||
// Get duration
|
||||
|
||||
@@ -55,11 +55,13 @@ 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 {
|
||||
@@ -288,7 +277,9 @@ public class CallbackServer implements Runnable {
|
||||
//Log.d(LOG_TAG, "CallbackServer.stopServer()");
|
||||
if (this.active) {
|
||||
this.active = false;
|
||||
|
||||
|
||||
try { waitSocket.close(); } catch (IOException ignore) {}
|
||||
|
||||
// Break out of server wait
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
@@ -302,46 +293,10 @@ public class CallbackServer implements Runnable {
|
||||
public void destroy() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,20 +273,21 @@ 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;
|
||||
|
||||
@@ -387,7 +388,19 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
} else {
|
||||
// Get the path to the image. Makes loading so much easier.
|
||||
String imagePath = FileUtils.getRealPathFromURI(uri, this.cordova);
|
||||
Log.d(LOG_TAG, "Real path = " + imagePath);
|
||||
// If we don't have a valid image so quit.
|
||||
if (imagePath == null) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to retreive path to picture!");
|
||||
return;
|
||||
}
|
||||
Bitmap bitmap = getScaledBitmap(imagePath);
|
||||
if (bitmap == null) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to create bitmap!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.correctOrientation) {
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
@@ -450,8 +463,10 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
}
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
@@ -151,6 +152,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
|
||||
try {
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (!multiple) {
|
||||
@@ -163,6 +165,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
else {
|
||||
searchTerm = "%";
|
||||
}
|
||||
|
||||
|
||||
//Log.d(LOG_TAG, "Search Term = " + searchTerm);
|
||||
//Log.d(LOG_TAG, "Field Length = " + fields.length());
|
||||
@@ -182,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");
|
||||
@@ -259,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) {
|
||||
@@ -301,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));
|
||||
@@ -332,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)
|
||||
@@ -358,8 +443,9 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
|
||||
// Set the old contact ID
|
||||
oldContactId = contactId;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Push the last contact into the contacts array
|
||||
if (contacts.length() < limit) {
|
||||
contacts.put(populateContact(contact, organizations, addresses, phones,
|
||||
|
||||
@@ -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;
|
||||
@@ -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,17 +176,31 @@ 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);
|
||||
this.requestFocusFromTouch();
|
||||
@@ -189,7 +214,11 @@ public class CordovaWebView extends WebView {
|
||||
//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();
|
||||
@@ -208,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");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,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);
|
||||
}
|
||||
@@ -446,7 +491,7 @@ public class CordovaWebView extends WebView {
|
||||
// Load url
|
||||
this.loadUrlIntoView(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
@@ -454,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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,9 +754,6 @@ public class CordovaWebView extends WebView {
|
||||
@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
|
||||
@@ -749,9 +789,8 @@ public class CordovaWebView extends WebView {
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -785,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)
|
||||
@@ -171,9 +206,9 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
// If our app or file:, then load into a new Cordova webview container by starting a new instance of our activity.
|
||||
// Our app continues to run. When BACK is pressed, our app is redisplayed.
|
||||
if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) {
|
||||
if (url.startsWith("file://") || url.startsWith("data:") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) {
|
||||
//This will fix iFrames
|
||||
if (appView.useBrowserHistory)
|
||||
if (appView.useBrowserHistory || url.startsWith("data:"))
|
||||
return false;
|
||||
else
|
||||
this.appView.loadUrl(url);
|
||||
@@ -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 = "2.0.0"; // Cordova version
|
||||
public static String cordovaVersion = "2.1.0"; // 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;
|
||||
@@ -46,6 +47,7 @@ import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
@@ -600,6 +602,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,19 +612,9 @@ 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);
|
||||
}
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning) {
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.appView.pauseTimers();
|
||||
else
|
||||
{
|
||||
this.appView.handlePause(this.keepRunning);
|
||||
}
|
||||
|
||||
// hide the splash screen to avoid leaking a window
|
||||
@@ -633,11 +627,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
|
||||
@@ -647,6 +639,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;
|
||||
@@ -656,13 +649,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) {
|
||||
@@ -672,9 +659,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
this.keepRunning = this.activityResultKeepRunning;
|
||||
this.activityResultKeepRunning = false;
|
||||
}
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,17 +674,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
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();
|
||||
@@ -741,8 +715,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,10 +741,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;
|
||||
}
|
||||
@@ -839,9 +813,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))) {
|
||||
@@ -849,6 +820,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);
|
||||
}
|
||||
});
|
||||
@@ -1023,6 +996,20 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
this.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Determine if the focus is on the current view or not
|
||||
if (appView.getHitTestResult() != null &&
|
||||
appView.getHitTestResult().getType() == WebView.HitTestResult.EDIT_TEXT_TYPE &&
|
||||
keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
}
|
||||
else
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
*
|
||||
@@ -1060,5 +1047,4 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
57
framework/src/org/apache/cordova/Echo.java
Normal file
@@ -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 = "";
|
||||
String message = "Location API is not available for this device.";
|
||||
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());
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
254
framework/src/org/apache/cordova/NativeToJsMessageQueue.java
Executable file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class Notification 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.
|
||||
@@ -101,7 +101,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
@@ -138,7 +138,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Beep plays the default notification ringtone.
|
||||
*
|
||||
*
|
||||
* @param count Number of times to play notification
|
||||
*/
|
||||
public void beep(long count) {
|
||||
@@ -163,7 +163,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Vibrates the device for the specified amount of time.
|
||||
*
|
||||
*
|
||||
* @param time Time to vibrate in ms.
|
||||
*/
|
||||
public void vibrate(long time) {
|
||||
@@ -179,7 +179,7 @@ public class Notification extends Plugin {
|
||||
* Builds and shows a native Android alert with given Strings
|
||||
* @param message The message the alert should display
|
||||
* @param title The title of the alert
|
||||
* @param buttonLabel The label of the button
|
||||
* @param buttonLabel The label of the button
|
||||
* @param callbackId The callback id
|
||||
*/
|
||||
public synchronized void alert(final String message, final String title, final String buttonLabel, final String callbackId) {
|
||||
@@ -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) {
|
||||
@@ -212,7 +212,7 @@ public class Notification extends Plugin {
|
||||
* Builds and shows a native Android confirm dialog with given title, message, buttons.
|
||||
* This dialog only shows up to 3 buttons. Any labels after that will be ignored.
|
||||
* The index of the button pressed will be returned to the JavaScript callback identified by callbackId.
|
||||
*
|
||||
*
|
||||
* @param message The message the dialog should display
|
||||
* @param title The title of the dialog
|
||||
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
||||
@@ -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) {
|
||||
@@ -274,7 +274,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Show the spinner.
|
||||
*
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
@@ -310,7 +310,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Show the progress dialog.
|
||||
*
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
@@ -344,7 +344,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Set value of progress bar.
|
||||
*
|
||||
*
|
||||
* @param value 0-100
|
||||
*/
|
||||
public synchronized void progressValue(int value) {
|
||||
|
||||
@@ -2,8 +2,10 @@ 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;
|
||||
@@ -108,4 +110,22 @@ public class LegacyContext implements CordovaInterface {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,22 +105,61 @@ public class PluginManager {
|
||||
}
|
||||
XmlResourceParser xml = this.ctx.getActivity().getResources().getXml(id);
|
||||
int eventType = -1;
|
||||
String service = "", pluginClass = "";
|
||||
String service = "", pluginClass = "", paramType = "";
|
||||
boolean onload = false;
|
||||
PluginEntry entry = null;
|
||||
boolean insideFeature = false;
|
||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||
if (eventType == XmlResourceParser.START_TAG) {
|
||||
String strNode = xml.getName();
|
||||
//This is for the old scheme
|
||||
if (strNode.equals("plugin")) {
|
||||
service = xml.getAttributeValue(null, "name");
|
||||
pluginClass = xml.getAttributeValue(null, "value");
|
||||
// System.out.println("Plugin: "+name+" => "+value);
|
||||
onload = "true".equals(xml.getAttributeValue(null, "onload"));
|
||||
entry = new PluginEntry(service, pluginClass, onload);
|
||||
this.addService(entry);
|
||||
} else if (strNode.equals("url-filter")) {
|
||||
this.addService(entry);
|
||||
}
|
||||
//What is this?
|
||||
else if (strNode.equals("url-filter")) {
|
||||
this.urlMap.put(xml.getAttributeValue(null, "value"), service);
|
||||
}
|
||||
else if (strNode.equals("feature")) {
|
||||
insideFeature = true;
|
||||
//Check for supported feature sets (Accelerometer, Geolocation, etc)
|
||||
//Set the bit for reading params
|
||||
String uri = xml.getAttributeValue(null,"name");
|
||||
}
|
||||
else if(strNode.equals("param")) {
|
||||
if(insideFeature)
|
||||
{
|
||||
paramType = xml.getAttributeValue(null, "name");
|
||||
if(paramType.equals("service"))
|
||||
service = xml.getAttributeValue(null, "value");
|
||||
else if(paramType.equals("package"))
|
||||
pluginClass = xml.getAttributeValue(null, "value");
|
||||
if(service.length() > 0 && pluginClass.length() > 0)
|
||||
{
|
||||
onload = "true".equals(xml.getAttributeValue(null, "onload"));
|
||||
entry = new PluginEntry(service, pluginClass, onload);
|
||||
this.addService(entry);
|
||||
service = "";
|
||||
pluginClass = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (eventType == XmlResourceParser.END_TAG)
|
||||
{
|
||||
String strNode = xml.getName();
|
||||
if(strNode.equals("feature"))
|
||||
{
|
||||
//Empty the strings to prevent plugin loading bugs
|
||||
service = "";
|
||||
pluginClass = "";
|
||||
insideFeature = false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
eventType = xml.next();
|
||||
@@ -172,9 +211,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 {
|
||||
@@ -190,20 +229,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());
|
||||
@@ -212,14 +240,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -234,7 +262,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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 use the new cordova-1.7.0.js file
|
||||
6. Update the res/xml/plugins.xml to be the same as the one found in framework/res/xml/plugins.xml
|
||||
|
||||
|
||||
## 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 use the new cordova-1.6.0.js file
|
||||
6. Update the res/xml/plugins.xml to be the same as the one found in framework/res/xml/plugins.xml
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>PhoneGapViewTestActivity</name>
|
||||
<name>CordovaViewTestActivity</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
|
||||
@@ -8,4 +8,4 @@
|
||||
# project structure.
|
||||
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:15
|
||||
target=android-16
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
android:orientation="vertical" >
|
||||
|
||||
<org.apache.cordova.CordovaWebView
|
||||
android:id="@+id/phoneGapView"
|
||||
android:id="@+id/cordovaWebView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ import org.json.JSONException;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
|
||||
/**
|
||||
* This class provides a service.
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
package org.apache.cordova.test;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import com.phonegap.api.PluginManager;
|
||||
import org.apache.cordova.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);
|
||||
@@ -63,6 +67,23 @@ public class CordovaActivityTest extends ActivityInstrumentationTestCase2<Cordov
|
||||
String className = innerContainer.getClass().getSimpleName();
|
||||
assertTrue(className.equals("LinearLayoutSoftKeyboardDetect"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
package org.apache.cordova.test;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import com.phonegap.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
|
||||
import android.app.Instrumentation;
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.view.View;
|
||||
|
||||
@@ -33,13 +34,13 @@ public class CordovaTest extends
|
||||
private String rString;
|
||||
|
||||
public CordovaTest() {
|
||||
super("com.phonegap.test.activities", CordovaWebViewTestActivity.class);
|
||||
super("org.apache.cordova.test.activities", CordovaWebViewTestActivity.class);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
testActivity = this.getActivity();
|
||||
testView = testActivity.findViewById(R.id.phoneGapView);
|
||||
testView = testActivity.findViewById(R.id.cordovaWebView);
|
||||
}
|
||||
|
||||
public void testPreconditions() {
|
||||
@@ -98,6 +99,7 @@ public class CordovaTest extends
|
||||
assertTrue(url.equals("file:///android_asset/www/index.html"));
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
private void sleep() {
|
||||
try {
|
||||
|
||||
@@ -30,7 +30,7 @@ import android.os.Bundle;
|
||||
|
||||
public class CordovaWebViewTestActivity extends Activity implements CordovaInterface {
|
||||
|
||||
CordovaWebView phoneGap;
|
||||
CordovaWebView cordovaWebView;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
@Override
|
||||
@@ -39,17 +39,17 @@ public class CordovaWebViewTestActivity extends Activity implements CordovaInter
|
||||
|
||||
setContentView(R.layout.main);
|
||||
|
||||
phoneGap = (CordovaWebView) findViewById(R.id.phoneGapView);
|
||||
cordovaWebView = (CordovaWebView) findViewById(R.id.cordovaWebView);
|
||||
|
||||
phoneGap.loadUrl("file:///android_asset/www/index.html");
|
||||
cordovaWebView.loadUrl("file:///android_asset/www/index.html");
|
||||
|
||||
}
|
||||
|
||||
public void onDestroy()
|
||||
{
|
||||
super.onDestroy();
|
||||
if (phoneGap.pluginManager != null) {
|
||||
phoneGap.pluginManager.onDestroy();
|
||||
if (cordovaWebView.pluginManager != null) {
|
||||
cordovaWebView.pluginManager.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class GapClientTest extends ActivityInstrumentationTestCase2<CordovaWebVi
|
||||
private CordovaChromeClient appCode;
|
||||
|
||||
public GapClientTest() {
|
||||
super("com.phonegap.test.activities",CordovaWebViewTestActivity.class);
|
||||
super("org.apache.cordova.test.activities",CordovaWebViewTestActivity.class);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception{
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
package org.apache.cordova.test;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import com.phonegap.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
|
||||
import android.test.ActivityInstrumentationTestCase2;
|
||||
import android.view.View;
|
||||
@@ -37,7 +37,7 @@ public class PluginManagerTest extends ActivityInstrumentationTestCase2<CordovaW
|
||||
private PluginManager pMan;
|
||||
|
||||
public PluginManagerTest() {
|
||||
super("com.phonegap.test.activities",CordovaWebViewTestActivity.class);
|
||||
super("org.apache.cordova.test.activities",CordovaWebViewTestActivity.class);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception{
|
||||
|
||||