Compare commits
342 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3caa45d860 | ||
|
|
7c069f14f8 | ||
|
|
6efeb1471c | ||
|
|
01f062d2ba | ||
|
|
2a42c463d2 | ||
|
|
182843edf6 | ||
|
|
9a9d36e9d9 | ||
|
|
7d5249eea6 | ||
|
|
f7910c41c3 | ||
|
|
3973f4f952 | ||
|
|
8a19769a47 | ||
|
|
c0ee593c10 | ||
|
|
c806451b8a | ||
|
|
00e5ff1964 | ||
|
|
432aec62a9 | ||
|
|
2c202b82d7 | ||
|
|
a42dc08756 | ||
|
|
48f58110fe | ||
|
|
7b3724972b | ||
|
|
9ca2a16218 | ||
|
|
f1e8400abf | ||
|
|
11e0ffa90a | ||
|
|
2ee4326a4d | ||
|
|
226e72ac18 | ||
|
|
65c78b8f3f | ||
|
|
6137c7ca06 | ||
|
|
5bebf11b37 | ||
|
|
68161d2714 | ||
|
|
a6473cb826 | ||
|
|
0084c6f96a | ||
|
|
a87825dbee | ||
|
|
3566154cd0 | ||
|
|
92d69e320f | ||
|
|
08a190ef5b | ||
|
|
98339ee5d8 | ||
|
|
fa387fd758 | ||
|
|
54979f2fc4 | ||
|
|
538e90f23a | ||
|
|
d9107bcac6 | ||
|
|
3f3a0b9140 | ||
|
|
e1347e434e | ||
|
|
7657faa9c3 | ||
|
|
28ef765913 | ||
|
|
d2f59391a2 | ||
|
|
df90bdb350 | ||
|
|
c416c77d7a | ||
|
|
ce05a720d1 | ||
|
|
6c19a440f5 | ||
|
|
f4612fdb5d | ||
|
|
04b9a0b09e | ||
|
|
f93c438067 | ||
|
|
e1d608443a | ||
|
|
9233c3a898 | ||
|
|
dfa514334b | ||
|
|
5810a96e62 | ||
|
|
70473a80af | ||
|
|
525fd30cb2 | ||
|
|
5212cd4dcd | ||
|
|
e95bde62a2 | ||
|
|
4fe73cf6ad | ||
|
|
78b2835da4 | ||
|
|
f9a49efae9 | ||
|
|
b9ddc9e678 | ||
|
|
dc459c84a3 | ||
|
|
1d26239809 | ||
|
|
e51b4897a3 | ||
|
|
81f283e56f | ||
|
|
ccdd2fd2ca | ||
|
|
69f11a29e1 | ||
|
|
cf494f3238 | ||
|
|
d5895c635a | ||
|
|
2ac9873613 | ||
|
|
eb59e76cde | ||
|
|
d9db845b43 | ||
|
|
e55327b064 | ||
|
|
bdd5a4e053 | ||
|
|
ac2e2c9a42 | ||
|
|
76f9d49e24 | ||
|
|
6ec8ab95fc | ||
|
|
9c98625610 | ||
|
|
f270cde47d | ||
|
|
9de7efd072 | ||
|
|
7b81d317a0 | ||
|
|
876f975aa2 | ||
|
|
3c5815ac0f | ||
|
|
678ae2d684 | ||
|
|
e4f8f44fb0 | ||
|
|
49566d29f8 | ||
|
|
7f4ee7b20a | ||
|
|
32526a8c16 | ||
|
|
71a7f72ab9 | ||
|
|
4d0824f4a4 | ||
|
|
d56dd40d06 | ||
|
|
6aafd6dc3a | ||
|
|
011b512f28 | ||
|
|
aa2d17e489 | ||
|
|
0eee2293dc | ||
|
|
a2f35d2bda | ||
|
|
58f58d9ee8 | ||
|
|
412bb349ac | ||
|
|
652f15f893 | ||
|
|
8512ebb923 | ||
|
|
f0ac173ec8 | ||
|
|
bef0d47924 | ||
|
|
cba0d59021 | ||
|
|
7d3afcab94 | ||
|
|
5f1cda07e7 | ||
|
|
e11beade4b | ||
|
|
6a1e089b73 | ||
|
|
0aa98ac2da | ||
|
|
f9ef38cc7a | ||
|
|
a3a215a1ba | ||
|
|
d3ee322d7c | ||
|
|
7ec20e7752 | ||
|
|
08dfb13dbf | ||
|
|
6a5cddd907 | ||
|
|
dc5078306d | ||
|
|
1bc032853c | ||
|
|
e562e4e7b9 | ||
|
|
0ffffa9029 | ||
|
|
0f2303e8d5 | ||
|
|
31f7f8149e | ||
|
|
fe1f57c23f | ||
|
|
29a0b010da | ||
|
|
621e1163f8 | ||
|
|
17d64cfcbe | ||
|
|
7379d2135d | ||
|
|
c55fd06b99 | ||
|
|
d81727a08c | ||
|
|
b582e1592a | ||
|
|
dd8533a320 | ||
|
|
d72a8cbf89 | ||
|
|
fe0876ded6 | ||
|
|
fa15763c5d | ||
|
|
205215d409 | ||
|
|
076bfcde87 | ||
|
|
10510484b5 | ||
|
|
e1dea5b4d3 | ||
|
|
891f8d00cf | ||
|
|
0d409f0fe3 | ||
|
|
4e0c8982c9 | ||
|
|
a741c66c97 | ||
|
|
3e6a7cbdf5 | ||
|
|
5d34aa0afe | ||
|
|
979ae94698 | ||
|
|
8d7b85b26a | ||
|
|
686977a986 | ||
|
|
9c6c782146 | ||
|
|
ca9539b5b6 | ||
|
|
ff25be8839 | ||
|
|
d1ab1b59be | ||
|
|
05bc1865a6 | ||
|
|
6e6e0275ad | ||
|
|
ec3c5b2ca2 | ||
|
|
5289d569b0 | ||
|
|
6f873ff6b5 | ||
|
|
467cbe972c | ||
|
|
bfd1bfe9f0 | ||
|
|
3404a6c699 | ||
|
|
17a4b5155e | ||
|
|
d406e2ed22 | ||
|
|
0bfc9935b2 | ||
|
|
64c6cbe303 | ||
|
|
2245db3e80 | ||
|
|
6f19a50c98 | ||
|
|
c7ce9598a8 | ||
|
|
afcdccf783 | ||
|
|
1bf12842ca | ||
|
|
da8fbee256 | ||
|
|
4021f26e76 | ||
|
|
8eab8438cf | ||
|
|
1b4096b01d | ||
|
|
54caa6e438 | ||
|
|
486eb149f2 | ||
|
|
faa034a205 | ||
|
|
2cd3ebc7a8 | ||
|
|
7e3af6c235 | ||
|
|
dd4de16d1d | ||
|
|
ba8577fa5f | ||
|
|
6192319f8c | ||
|
|
fed368d553 | ||
|
|
20c885418e | ||
|
|
9318ee30bd | ||
|
|
8b6c9574df | ||
|
|
313148136a | ||
|
|
6e1fdc77ae | ||
|
|
2a9582ebb1 | ||
|
|
dd1cd46719 | ||
|
|
9961d9e54d | ||
|
|
7eb12110d1 | ||
|
|
3d62744601 | ||
|
|
17af417235 | ||
|
|
df9d314361 | ||
|
|
610e0c984a | ||
|
|
3688fca126 | ||
|
|
9bc89c784f | ||
|
|
79682f5d52 | ||
|
|
c206ac0335 | ||
|
|
34840175f3 | ||
|
|
6312457425 | ||
|
|
f71e664952 | ||
|
|
80d559f17e | ||
|
|
772aedc263 | ||
|
|
45d7c124c8 | ||
|
|
73abb20b3d | ||
|
|
0baf104a75 | ||
|
|
302d51cdfd | ||
|
|
d3cbfd5467 | ||
|
|
9e3e7e1820 | ||
|
|
18893bf6cd | ||
|
|
f53161d6f5 | ||
|
|
4c9a571106 | ||
|
|
365edcad16 | ||
|
|
ae9047a708 | ||
|
|
9c0e58df8d | ||
|
|
ee34f11c29 | ||
|
|
6ca6d88bff | ||
|
|
65a397fb63 | ||
|
|
0a669077fb | ||
|
|
451688a12e | ||
|
|
d181d89dd2 | ||
|
|
ac14b0d73b | ||
|
|
0f42c65792 | ||
|
|
37b3e980dc | ||
|
|
eb49e011e2 | ||
|
|
e0a73f72ee | ||
|
|
e217ab28c5 | ||
|
|
ca583865ea | ||
|
|
5e7efde311 | ||
|
|
2c7c13420b | ||
|
|
ac4fc3e54e | ||
|
|
46db36a05e | ||
|
|
3d073be990 | ||
|
|
1bc49fe450 | ||
|
|
1f7fe9abcc | ||
|
|
5217abf57a | ||
|
|
2ecbde891a | ||
|
|
bf7fc66646 | ||
|
|
5a94b38e2f | ||
|
|
1bc55f5937 | ||
|
|
04c9542f94 | ||
|
|
17e739f68a | ||
|
|
4f5515fde3 | ||
|
|
ae3ba129ea | ||
|
|
6b92a0fff7 | ||
|
|
d859bb8e67 | ||
|
|
f12bbf71ed | ||
|
|
b723beb545 | ||
|
|
47daaaf14f | ||
|
|
9ba5bae34d | ||
|
|
dbfa2d7994 | ||
|
|
8134f86d1f | ||
|
|
5c60b09bf4 | ||
|
|
20a19d67d0 | ||
|
|
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 | ||
|
|
06aafc96c9 | ||
|
|
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 | ||
|
|
93ec092eaf | ||
|
|
29ae492983 | ||
|
|
b9f6a59a20 | ||
|
|
d74551216f | ||
|
|
d4302ae51b |
25
.gitignore
vendored
@@ -4,18 +4,29 @@ gen
|
||||
assets/www/cordova.js
|
||||
framework/assets/www/.tmp*
|
||||
local.properties
|
||||
framework/proguard.cfg
|
||||
framework/cordova.jar
|
||||
framework/cordova-*.jar
|
||||
framework/phonegap-*.jar
|
||||
framework/lib
|
||||
proguard.cfg
|
||||
proguard.cfg
|
||||
proguard-project.txt
|
||||
framework/bin
|
||||
framework/test/org/apache/cordova/*.class
|
||||
framework/assets/www/.DS_Store
|
||||
framework/assets/www/cordova-*.js
|
||||
framework/assets/www/phonegap-*.js
|
||||
framework/libs
|
||||
test/libs
|
||||
example
|
||||
./test
|
||||
tmp
|
||||
*.tmp
|
||||
test/libs/*.jar
|
||||
test/bin
|
||||
test/assets/www/.tmp*
|
||||
tmp/**
|
||||
bin/node_modules
|
||||
.metadata
|
||||
tmp/**/*
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*.class
|
||||
*.jar
|
||||
|
||||
21
README.md
@@ -15,8 +15,8 @@ indicate that the project has yet to be fully endorsed by the ASF.
|
||||
Requires
|
||||
---
|
||||
|
||||
- Java JDK 1.5
|
||||
- Apache ANT
|
||||
- Java JDK 1.5 or greater
|
||||
- Apache ANT 1.8.0 or greater
|
||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
|
||||
|
||||
@@ -29,11 +29,11 @@ Building
|
||||
|
||||
To create your cordova.jar, copy the commons codec:
|
||||
|
||||
mv commons-codec-1.6.jar framework/libs
|
||||
mv commons-codec-1.7.jar framework/libs
|
||||
|
||||
then run in the framework directory:
|
||||
|
||||
android update project -p . -t android-15
|
||||
android update project -p . -t android-17
|
||||
ant jar
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Project Commands
|
||||
|
||||
These commands live in a generated Cordova Android project.
|
||||
|
||||
./cordovap/debug [path] ..................... install to first device
|
||||
./cordova/debug [path] ..................... install to first device
|
||||
./cordova/emulate .......................... start avd (emulator) named default
|
||||
./cordova/log .............................. starts logcat
|
||||
|
||||
@@ -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/)
|
||||
|
||||
128
bin/create
@@ -1,4 +1,4 @@
|
||||
#! /bin/sh
|
||||
#! /bin/bash
|
||||
# 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
|
||||
@@ -23,22 +23,22 @@
|
||||
#
|
||||
set -e
|
||||
|
||||
if [ -n "$1" ] && [ "$1" == "-h" ]
|
||||
if [ -z "$1" ] || [ "$1" == "-h" ]
|
||||
then
|
||||
echo 'usage: create path package activity'
|
||||
echo "Make sure the Android SDK tools folder is in your PATH!"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BUILD_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
VERSION=$(cat $BUILD_PATH/VERSION)
|
||||
BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||
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,102 +46,114 @@ 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 createAppInfoJar {
|
||||
(cd "$BUILD_PATH"/bin/templates/cordova/ApplicationInfo &&
|
||||
javac ApplicationInfo.java &&
|
||||
jar -cfe ../appinfo.jar ApplicationInfo ApplicationInfo.class
|
||||
)
|
||||
}
|
||||
|
||||
function on_error {
|
||||
echo "An error occured. Deleting project..."
|
||||
[ -d $PROJECT_PATH ] && rm -rf $PROJECT_PATH
|
||||
echo "An unexpected error occurred: $previous_command exited with $?"
|
||||
echo "Deleting project..."
|
||||
[ -d "$PROJECT_PATH" ] && rm -rf "$PROJECT_PATH"
|
||||
exit "$?"
|
||||
}
|
||||
|
||||
function replace {
|
||||
local pattern=$1
|
||||
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
|
||||
}
|
||||
|
||||
# we do not want the script to silently fail
|
||||
trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
|
||||
trap on_error ERR
|
||||
trap on_exit EXIT
|
||||
|
||||
ANDROID_BIN=$( which android )
|
||||
ANDROID_BIN="${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 ' ' )
|
||||
TARGET=$("$ANDROID_BIN" list targets | grep id: | tail -1 | cut -f 2 -d ' ' )
|
||||
API_LEVEL=$("$ANDROID_BIN" list target | grep "API level:" | tail -n 1 | cut -f 2 -d ':' | tr -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.7.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
|
||||
curl -OL http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip &> /dev/null
|
||||
unzip commons-codec-1.7-bin.zip &> /dev/null
|
||||
mkdir -p "$BUILD_PATH"/framework/libs
|
||||
cp commons-codec-1.7/commons-codec-1.7.jar "$BUILD_PATH"/framework/libs
|
||||
# cleanup yo
|
||||
rm commons-codec-1.6-bin.zip && rm -rf commons-codec-1.6
|
||||
rm commons-codec-1.7-bin.zip && rm -rf commons-codec-1.7
|
||||
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"
|
||||
replace "s/__APILEVEL__/${API_LEVEL}/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
|
||||
# creating cordova folder and copying run/build/log/launch scripts
|
||||
mkdir "$PROJECT_PATH"/cordova
|
||||
createAppInfoJar
|
||||
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/build "$PROJECT_PATH"/cordova/build
|
||||
cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release
|
||||
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/run "$PROJECT_PATH"/cordova/run
|
||||
|
||||
@@ -1,10 +1,27 @@
|
||||
:: 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.
|
||||
|
||||
@ECHO OFF
|
||||
IF NOT DEFINED JAVA_HOME GOTO MISSING
|
||||
FOR %%X in (java.exe ant.bat android.bat) do (
|
||||
FOR %%X in (java.exe javac.exe ant.bat android.bat) do (
|
||||
SET FOUND=%%~$PATH:X
|
||||
IF NOT DEFINED FOUND GOTO MISSING
|
||||
)
|
||||
cscript %~dp0\create.js %*
|
||||
cscript "%~dp0\create.js" %*
|
||||
GOTO END
|
||||
:MISSING
|
||||
ECHO Missing one of the following:
|
||||
|
||||
@@ -37,6 +37,10 @@ function setTarget() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s\d+/g);
|
||||
return targets[targets.length - 1].replace(/id: /, ""); // TODO: give users the option to set their target
|
||||
}
|
||||
function setApiLevel() {
|
||||
var targets = shell.Exec('android.bat list targets').StdOut.ReadAll().match(/API level:\s\d+/g);
|
||||
return targets[targets.length - 1].replace(/API level: /, "");
|
||||
}
|
||||
function write(filename, contents) {
|
||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||
var f=fso.OpenTextFile(filename, 2, true);
|
||||
@@ -49,10 +53,26 @@ function replaceInFile(filename, regexp, replacement) {
|
||||
function exec(command) {
|
||||
var oShell=shell.Exec(command);
|
||||
while (oShell.Status == 0) {
|
||||
if(!oShell.StdOut.AtEndOfStream) {
|
||||
var line = oShell.StdOut.ReadLine();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
function createAppInfoJar() {
|
||||
if(!fso.FileExists(ROOT+"\\bin\\templates\\cordova\\appinfo.jar")) {
|
||||
WScript.Echo("Creating appinfo.jar...");
|
||||
var cur = shell.CurrentDirectory;
|
||||
shell.CurrentDirectory = ROOT+"\\bin\\templates\\cordova\\ApplicationInfo";
|
||||
exec("javac ApplicationInfo.java");
|
||||
exec("jar -cfe ..\\appinfo.jar ApplicationInfo ApplicationInfo.class");
|
||||
shell.CurrentDirectory = cur;
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
// Cleanup
|
||||
// if(fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.6.jar')) {
|
||||
@@ -68,11 +88,11 @@ function cleanup() {
|
||||
}
|
||||
|
||||
function downloadCommonsCodec() {
|
||||
if (!fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.6.jar')) {
|
||||
if (!fso.FileExists(ROOT + '\\framework\\libs\\commons-codec-1.7.jar')) {
|
||||
// We need the .jar
|
||||
var url = 'http://mirror.symnds.com/software/Apache//commons/codec/binaries/commons-codec-1.6-bin.zip';
|
||||
var url = 'http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip';
|
||||
var libsPath = ROOT + '\\framework\\libs';
|
||||
var savePath = libsPath + '\\commons-codec-1.6-bin.zip';
|
||||
var savePath = libsPath + '\\commons-codec-1.7-bin.zip';
|
||||
if (!fso.FileExists(savePath)) {
|
||||
if(!fso.FolderExists(ROOT + '\\framework\\libs')) {
|
||||
fso.CreateFolder(libsPath);
|
||||
@@ -99,11 +119,11 @@ function downloadCommonsCodec() {
|
||||
target.CopyHere(source, 256);
|
||||
|
||||
// Move the jar into libs
|
||||
fso.MoveFile(ROOT + '\\framework\\libs\\commons-codec-1.6\\commons-codec-1.6.jar', ROOT + '\\framework\\libs\\commons-codec-1.6.jar');
|
||||
fso.MoveFile(ROOT + '\\framework\\libs\\commons-codec-1.7\\commons-codec-1.7.jar', ROOT + '\\framework\\libs\\commons-codec-1.7.jar');
|
||||
|
||||
// Clean up
|
||||
fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.6-bin.zip');
|
||||
fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.6', true);
|
||||
fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.7-bin.zip');
|
||||
fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.7', true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,60 +149,66 @@ var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
|
||||
var ACTIVITY_PATH=PROJECT_PATH+'\\src\\'+PACKAGE_AS_PATH+'\\'+ACTIVITY+'.java';
|
||||
var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
|
||||
var TARGET=setTarget();
|
||||
var API_LEVEL=setApiLevel();
|
||||
var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
|
||||
// create the project
|
||||
WScript.Echo("Creating new android project...");
|
||||
exec('android.bat create project --target '+TARGET+' --path '+PROJECT_PATH+' --package '+PACKAGE+' --activity '+ACTIVITY);
|
||||
|
||||
// build from source. distro should have these files
|
||||
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
|
||||
WScript.Echo("Building jar and js files...");
|
||||
// update the cordova framework project to a target that exists on this machine
|
||||
exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
|
||||
// pull down commons codec if necessary
|
||||
downloadCommonsCodec();
|
||||
exec('ant.bat -f '+ ROOT +'\\framework\\build.xml jar');
|
||||
exec('ant.bat -f \"'+ ROOT +'\\framework\\build.xml\" jar');
|
||||
}
|
||||
|
||||
// copy in the project template
|
||||
exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
|
||||
exec('%comspec% /c xcopy '+ ROOT + '\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /E /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
|
||||
WScript.Echo("Copying template files...");
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\res '+PROJECT_PATH+'\\res\\ /E /Y');
|
||||
exec('%comspec% /c xcopy "'+ ROOT + '"\\bin\\templates\\project\\assets '+PROJECT_PATH+'\\assets\\ /E /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\AndroidManifest.xml ' + PROJECT_PATH + '\\AndroidManifest.xml /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\project\\Activity.java '+ ACTIVITY_PATH +' /Y');
|
||||
|
||||
// check if we have the source or the distro files
|
||||
WScript.Echo("Copying js, jar & config.xml files...");
|
||||
if(fso.FolderExists(ROOT + '\\framework')) {
|
||||
exec('%comspec% /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||
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');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
||||
// copy in cordova.jar
|
||||
exec('%comspec% /c copy '+ROOT+'\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||
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
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\debug.bat ' + PROJECT_PATH + '\\cordova\\debug.bat /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\emulate.bat ' + PROJECT_PATH + '\\cordova\\emulate.bat /Y');
|
||||
exec('%comspec% /c copy '+ROOT+'\\bin\\templates\\cordova\\BOOM.bat ' + PROJECT_PATH + '\\cordova\\BOOM.bat /Y');
|
||||
createAppInfoJar();
|
||||
WScript.Echo("Copying cordova command tools...");
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\build.bat ' + PROJECT_PATH + '\\cordova\\build.bat /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\run.bat ' + PROJECT_PATH + '\\cordova\\run.bat /Y');
|
||||
|
||||
// interpolate the activity name and package
|
||||
WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
|
||||
replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
|
||||
|
||||
replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||
replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
|
||||
replaceInFile(MANIFEST_PATH, /__APILEVEL__/, API_LEVEL);
|
||||
|
||||
cleanup();
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
// 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.
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash $CORDOVA_PATH/cordova BOOM
|
||||
@@ -1 +0,0 @@
|
||||
%~dp0\cordova.bat BOOM
|
||||
24
bin/templates/cordova/build
Executable file
@@ -0,0 +1,24 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_PATH"/cordova build
|
||||
18
bin/templates/cordova/build.bat
Normal file
@@ -0,0 +1,18 @@
|
||||
:: 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.
|
||||
|
||||
%~dp0\cordova.bat build
|
||||
@@ -1,7 +1,24 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash $CORDOVA_PATH/cordova clean
|
||||
bash "$CORDOVA_PATH"/cordova clean
|
||||
|
||||
@@ -1 +1,18 @@
|
||||
:: 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.
|
||||
|
||||
%~dp0\cordova.bat clean
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
|
||||
function check_devices {
|
||||
local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}'`
|
||||
# FIXME
|
||||
local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device`
|
||||
if [ -z "$devices" ] ; then
|
||||
echo "1"
|
||||
else
|
||||
@@ -20,7 +37,6 @@ function emulate {
|
||||
|
||||
# Do not launch an emulator if there is already one running or if a device is attached
|
||||
if [ $(check_devices) == 0 ] ; then
|
||||
echo "Device attached or emulator already running"
|
||||
return
|
||||
fi
|
||||
|
||||
@@ -45,8 +61,9 @@ function emulate {
|
||||
do
|
||||
echo "$i) ${avd_list[$i]}"
|
||||
done
|
||||
echo -n "> "
|
||||
read avd_id
|
||||
read -t 5 -p "> " avd_id
|
||||
# default value if input timeout
|
||||
if [ $avd_id -eq 1000 ] ; then avd_id=0 ; fi
|
||||
done
|
||||
emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
|
||||
fi
|
||||
@@ -56,30 +73,87 @@ function emulate {
|
||||
function clean {
|
||||
ant clean
|
||||
}
|
||||
# has to be used independently and not in conjuction with other commands
|
||||
# has to be used independently and not in conjunction with other commands
|
||||
function log {
|
||||
adb logcat
|
||||
}
|
||||
|
||||
function debug {
|
||||
if [ $(check_devices) == 0 ] ; then
|
||||
ant debug install
|
||||
function run {
|
||||
clean && emulate && wait_for_device && install && launch
|
||||
}
|
||||
|
||||
function install {
|
||||
|
||||
declare -a devices=($(adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device | cut -f 1))
|
||||
local device_id="1000" #FIXME: hopefully user does not have 1000 AVDs
|
||||
|
||||
if [ ${#devices[@]} == 0 ]
|
||||
then
|
||||
# should not reach here. Emulator should launch or device should be attached
|
||||
echo "Emulator not running or device not attached. Could not install debug package"
|
||||
exit 70
|
||||
fi
|
||||
|
||||
if [ ${#devices[@]} == 1 ]
|
||||
then
|
||||
export ANDROID_SERIAL=${devices[0]}
|
||||
# User has more than 1 AVD
|
||||
elif [ ${#devices[@]} -gt 1 ]
|
||||
then
|
||||
while [ -z ${devices[$device_id]} ]
|
||||
do
|
||||
echo "Choose from one of the following devices/emulators [0 to $((${#devices[@]}-1))]:"
|
||||
for(( i = 0 ; i < ${#devices[@]} ; i++ ))
|
||||
do
|
||||
echo "$i) ${devices[$i]}"
|
||||
done
|
||||
read -t 5 -p "> " device_id
|
||||
# default value if input timeout
|
||||
if [ $device_id -eq 1000 ] ; then device_id=0 ; fi
|
||||
done
|
||||
export ANDROID_SERIAL=${devices[$device_id]}
|
||||
fi
|
||||
|
||||
ant debug install
|
||||
}
|
||||
|
||||
function build {
|
||||
ant debug
|
||||
}
|
||||
|
||||
function release {
|
||||
ant release
|
||||
}
|
||||
|
||||
function wait_for_device {
|
||||
local i="0"
|
||||
echo -n "Waiting for device..."
|
||||
|
||||
while [ $i -lt 300 ]
|
||||
do
|
||||
if [ $(check_devices) -eq 0 ]
|
||||
then
|
||||
break
|
||||
else
|
||||
sleep 1
|
||||
i=$[i+1]
|
||||
echo -n "."
|
||||
fi
|
||||
done
|
||||
# Device timeout: emulator has not started in time or device not attached
|
||||
if [ $i -eq 300 ]
|
||||
then
|
||||
echo "device timeout!"
|
||||
exit 69
|
||||
else
|
||||
ant debug
|
||||
echo "##################################################################"
|
||||
echo "# Plug in your device or launch an emulator with cordova/emulate #"
|
||||
echo "##################################################################"
|
||||
echo "connected!"
|
||||
fi
|
||||
}
|
||||
|
||||
function launch {
|
||||
local launch_str=$(java -jar $PROJECT_PATH/cordova/appinfo.jar $PROJECT_PATH/AndroidManifest.xml)
|
||||
local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||
adb shell am start -n $launch_str
|
||||
}
|
||||
|
||||
function BOOM {
|
||||
clean && debug && launch
|
||||
}
|
||||
|
||||
# TODO parse arguments
|
||||
(cd $PROJECT_PATH && $1)
|
||||
(cd "$PROJECT_PATH" && $1)
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
:: 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.
|
||||
|
||||
@ECHO OFF
|
||||
IF NOT DEFINED JAVA_HOME GOTO MISSING
|
||||
FOR %%X in (java.exe ant.bat android.bat) do (
|
||||
|
||||
63
bin/templates/cordova/cordova.js
vendored
@@ -1,3 +1,20 @@
|
||||
// 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 ROOT = WScript.ScriptFullName.split('\\cordova\\cordova.js').join(''),
|
||||
shell=WScript.CreateObject("WScript.Shell");
|
||||
|
||||
@@ -17,17 +34,19 @@ function exec(command) {
|
||||
return output;
|
||||
}
|
||||
|
||||
function emulator_running() {
|
||||
function device_running() {
|
||||
var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
|
||||
if(local_devices.match(/emulator/)) {
|
||||
if(local_devices.match(/\w+\tdevice/)) {
|
||||
WScript.Echo("Yes");
|
||||
return true;
|
||||
}
|
||||
WScript.Echo("No");
|
||||
return false;
|
||||
}
|
||||
function emulate() {
|
||||
// don't run emulator if a device is plugged in or if emulator is already running
|
||||
if(emulator_running()) {
|
||||
WScript.Echo("Device or Emulator already running!");
|
||||
if(device_running()) {
|
||||
//WScript.Echo("Device or Emulator already running!");
|
||||
return;
|
||||
}
|
||||
var oExec = shell.Exec("%comspec% /c android.bat list avd");
|
||||
@@ -67,18 +86,18 @@ function emulate() {
|
||||
}
|
||||
|
||||
function clean() {
|
||||
WScript.Echo("Cleaning project...");
|
||||
exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
|
||||
function debug() {
|
||||
if(emulator_running()) {
|
||||
exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
|
||||
} else {
|
||||
exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||
WScript.Echo("##################################################################");
|
||||
WScript.Echo("# Plug in your device or launch an emulator with cordova/emulate #");
|
||||
WScript.Echo("##################################################################");
|
||||
}
|
||||
function build() {
|
||||
WScript.Echo("Building project...");
|
||||
exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
|
||||
function install() {
|
||||
WScript.Echo("Building/Installing project...");
|
||||
exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
|
||||
}
|
||||
|
||||
function log() {
|
||||
@@ -86,14 +105,28 @@ function log() {
|
||||
}
|
||||
|
||||
function launch() {
|
||||
WScript.Echo("Launching app...");
|
||||
var launch_str=exec("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
|
||||
//WScript.Echo(launch_str);
|
||||
exec("%comspec% /c adb shell am start -n "+launch_str+" 2>&1");
|
||||
}
|
||||
|
||||
function BOOM() {
|
||||
function run() {
|
||||
var i=0;
|
||||
clean();
|
||||
debug();
|
||||
emulate();
|
||||
WScript.Stdout.Write('Waiting for device...');
|
||||
while(!device_running() && i < 300) {
|
||||
WScript.Stdout.Write('.');
|
||||
WScript.sleep(1000);
|
||||
i += 1;
|
||||
}
|
||||
if(i == 300) {
|
||||
WScript.Stderr.WriteLine("device/emulator timeout!");
|
||||
} else {
|
||||
WScript.Stdout.WriteLine("connected!");
|
||||
}
|
||||
install();
|
||||
launch();
|
||||
}
|
||||
var args = WScript.Arguments;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash $CORDOVA_PATH/cordova debug
|
||||
@@ -1 +0,0 @@
|
||||
%~dp0\cordova.bat debug
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash $CORDOVA_PATH/cordova emulate
|
||||
@@ -1 +0,0 @@
|
||||
%~dp0\cordova.bat emulate
|
||||
@@ -1,7 +1,24 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
|
||||
bash $PROJECT_PATH/cordova/cordova log
|
||||
bash "$CORDOVA_PATH"/cordova/cordova log
|
||||
|
||||
@@ -1 +1,18 @@
|
||||
:: 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.
|
||||
|
||||
%~dp0\cordova.bat log
|
||||
|
||||
24
bin/templates/cordova/release
Executable file
@@ -0,0 +1,24 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_PATH"/cordova release
|
||||
24
bin/templates/cordova/run
Executable file
@@ -0,0 +1,24 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||
|
||||
bash "$CORDOVA_PATH"/cordova run
|
||||
1
bin/templates/cordova/run.bat
Normal file
@@ -0,0 +1 @@
|
||||
%~dp0\cordova.bat run
|
||||
@@ -18,7 +18,7 @@
|
||||
under the License.
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
||||
package="__PACKAGE__" android:versionName="1.1" android:versionCode="5">
|
||||
package="__PACKAGE__" android:versionName="1.1" android:versionCode="5" android:hardwareAccelerated="true">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
@@ -47,9 +47,11 @@
|
||||
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:hardwareAccelerated="true"
|
||||
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:theme="@android:style/Theme.Black.NoTitleBar"
|
||||
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 +59,5 @@
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="5" />
|
||||
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="__APILEVEL__"/>
|
||||
</manifest>
|
||||
|
||||
115
bin/templates/project/assets/www/css/index.css
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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-tap-highlight-color: rgba(0,0,0,0); /* make transparent link selection, adjust last value opacity 0 to 1.0 */
|
||||
}
|
||||
|
||||
body {
|
||||
-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-user-select: none; /* prevent copy paste, to allow, change 'none' to 'text' */
|
||||
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%);
|
||||
background-image:-webkit-gradient(
|
||||
linear,
|
||||
left top,
|
||||
left bottom,
|
||||
color-stop(0, #A7A7A7),
|
||||
color-stop(0.51, #E4E4E4)
|
||||
);
|
||||
background-attachment:fixed;
|
||||
font-family:'HelveticaNeue-Light', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
font-size:12px;
|
||||
height:100%;
|
||||
margin:0px;
|
||||
padding:0px;
|
||||
text-transform:uppercase;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
/* Portrait layout (default) */
|
||||
.app {
|
||||
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:24px;
|
||||
font-weight:normal;
|
||||
margin:0px;
|
||||
overflow:visible;
|
||||
padding:0px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.event {
|
||||
border-radius:4px;
|
||||
-webkit-border-radius:4px;
|
||||
color:#FFFFFF;
|
||||
font-size:12px;
|
||||
margin:0px 30px;
|
||||
padding:2px 0px;
|
||||
}
|
||||
|
||||
.event.listening {
|
||||
background-color:#333333;
|
||||
display:block;
|
||||
}
|
||||
|
||||
.event.received {
|
||||
background-color:#4B946A;
|
||||
display:none;
|
||||
}
|
||||
|
||||
@keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
@-webkit-keyframes fade {
|
||||
from { opacity: 1.0; }
|
||||
50% { opacity: 0.4; }
|
||||
to { opacity: 1.0; }
|
||||
}
|
||||
|
||||
.blink {
|
||||
animation:fade 3000ms infinite;
|
||||
-webkit-animation:fade 3000ms infinite;
|
||||
}
|
||||
BIN
bin/templates/project/assets/www/img/cordova.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
bin/templates/project/assets/www/img/logo.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
@@ -1,60 +1,42 @@
|
||||
<!DOCTYPE HTML>
|
||||
<!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
|
||||
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
|
||||
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.
|
||||
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 name="viewport" content="width=320; user-scalable=no" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title">
|
||||
<script type="text/javascript" charset="utf-8" src="cordova-2.0.0rc1.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
<h1>Welcome to Cordova!</h1>
|
||||
<h2>this file is located at assets/www/index.html</h2>
|
||||
<div id="info">
|
||||
<h4>Platform: <span id="platform"> </span>, Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span>, Name: <span id="name"> </span></h4>
|
||||
<h4>Width: <span id="width"> </span>, Height: <span id="height">
|
||||
</span>, Color Depth: <span id="colorDepth"></span></h4>
|
||||
</div>
|
||||
<dl id="accel-data">
|
||||
<dt>X:</dt><dd id="x"> </dd>
|
||||
<dt>Y:</dt><dd id="y"> </dd>
|
||||
<dt>Z:</dt><dd id="z"> </dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="toggleAccel();">Toggle Accelerometer</a>
|
||||
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
|
||||
<a href="tel:411" class="btn large">Call 411</a>
|
||||
<a href="#" class="btn large" onclick="beep();">Beep</a>
|
||||
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
|
||||
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
|
||||
<a href="#" class="btn large" onclick="get_contacts();return false;">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();return false;">Check Network</a>
|
||||
<dl>
|
||||
<dt>Compass Heading:</dt><dd id="h">Off</dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="toggleCompass();return false;">Toggle Compass</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</body>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />
|
||||
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
||||
<title>Hello World</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<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.3.0.js"></script>
|
||||
<script type="text/javascript" src="js/index.js"></script>
|
||||
<script type="text/javascript">
|
||||
app.initialize();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
49
bin/templates/project/assets/www/js/index.js
Normal file
@@ -0,0 +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.bindEvents();
|
||||
},
|
||||
// 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 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');
|
||||
},
|
||||
// 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
@@ -1,3 +1,20 @@
|
||||
// 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 build_path = __dirname + '/../..',
|
||||
project_path = '/tmp/example',
|
||||
package_name = 'org.apache.cordova.example',
|
||||
@@ -58,7 +75,7 @@ create_project.on('exit', function(code) {
|
||||
// make sure main Activity was added
|
||||
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
|
||||
assert(exists, 'Activity did not get created');
|
||||
// TODO check that package name and activity name were substitued properly
|
||||
// TODO check that package name and activity name were substituted properly
|
||||
});
|
||||
|
||||
// make sure plugins.xml was added
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
// 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 build_path = __dirname + '/../..'
|
||||
project_path = process.env.Temp + '\\example',
|
||||
package_name = 'org.apache.cordova.example',
|
||||
@@ -68,17 +85,12 @@ create_project.on('exit', function(code) {
|
||||
// make sure main Activity was added
|
||||
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
|
||||
assert(exists, 'Activity did not get created');
|
||||
// TODO check that package name and activity name were substitued properly
|
||||
// TODO check that package name and activity name were substituted properly
|
||||
});
|
||||
|
||||
// make sure plugins.xml was added
|
||||
path.exists(util.format('%s/res/xml/plugins.xml', project_path), function(exists) {
|
||||
assert(exists, 'plugins.xml did not get created');
|
||||
});
|
||||
|
||||
// make sure cordova.xml was added
|
||||
path.exists(util.format('%s/res/xml/cordova.xml', project_path), function(exists) {
|
||||
assert(exists, 'plugins.xml did not get created');
|
||||
// make sure config.xml was added
|
||||
path.exists(util.format('%s/res/xml/config.xml', project_path), function(exists) {
|
||||
assert(exists, 'config.xml did not get created');
|
||||
});
|
||||
|
||||
// make sure cordova.jar was added
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="lib" path="libs/commons-codec-1.6.jar"/>
|
||||
<classpathentry kind="lib" path="libs/commons-codec-1.7.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>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="cordova-2.0.0rc1.js"></script>
|
||||
<script src="cordova-2.3.0.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
@@ -26,9 +26,37 @@
|
||||
</filterchain>
|
||||
</loadfile>
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<!-- check that the version of ant is at least 1.8.0, as is needed
|
||||
for the dblQuote property -->
|
||||
<antversion property="thisantversion" atleast="1.8.0" />
|
||||
<fail message="The required minimum version of ant is 1.8.0, you have ${ant.version}"
|
||||
unless="thisantversion" />
|
||||
|
||||
<!-- check that commons codec is available. You should copy the codec jar to
|
||||
framework/libs, as it is not included in the Cordova distribution.
|
||||
The name of the jar file in framework/libs does not matter. -->
|
||||
<available classname="org.apache.commons.codec.binary.Base64"
|
||||
property="exists.base64"
|
||||
ignoresystemclasses="true">
|
||||
<classpath>
|
||||
<pathelement path="${classpath}" />
|
||||
<fileset dir="libs">
|
||||
<include name="*.jar" />
|
||||
</fileset>
|
||||
</classpath>
|
||||
</available>
|
||||
<fail message="You need to put a copy of Apache Commons Codec jar in the framework/libs directory"
|
||||
unless="exists.base64" />
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android'
|
||||
tool. (For example "sdkdir/tools/android update project -p ." inside
|
||||
of this directory where the AndroidManifest.xml file exists. This
|
||||
properties file that gets built contains the path to the SDK. It
|
||||
should *NOT* be checked into Version Control Systems since it holds
|
||||
data about the local machine. -->
|
||||
<available file="local.properties" property="exists.local.properties" />
|
||||
<fail message="You need to create the file 'local.properties' by running 'android update project -p .' here."
|
||||
unless="exists.local.properties" />
|
||||
<loadproperties srcFile="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
@@ -66,13 +94,13 @@
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!-- version-tag: custom -->
|
||||
<!-- version-tag: custom -->
|
||||
<!-- extension targets. Uncomment the ones where you want to do custom work
|
||||
in between standard targets -->
|
||||
<!--
|
||||
@@ -106,7 +134,7 @@
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
<!-- Combine JavaScript files into one cordova-uncompressed.js file. -->
|
||||
<target name="build-javascript" depends="clean">
|
||||
|
||||
@@ -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:17
|
||||
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="useBrowserHistory" value="true" />
|
||||
<preference name="exit-on-suspend" value="false" />
|
||||
<plugins>
|
||||
<plugin name="App" value="org.apache.cordova.App"/>
|
||||
<plugin name="Geolocation" value="org.apache.cordova.GeoBroker"/>
|
||||
@@ -44,11 +45,13 @@
|
||||
<plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/>
|
||||
<plugin name="Notification" value="org.apache.cordova.Notification"/>
|
||||
<plugin name="Storage" value="org.apache.cordova.Storage"/>
|
||||
<plugin name="Temperature" value="org.apache.cordova.TempListener"/>
|
||||
<plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/>
|
||||
<plugin name="Capture" value="org.apache.cordova.Capture"/>
|
||||
<plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
|
||||
<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
|
||||
<plugin name="Echo" value="org.apache.cordova.Echo" />
|
||||
<plugin name="Globalization" value="org.apache.cordova.Globalization"/>
|
||||
<plugin name="InAppBrowser" value="org.apache.cordova.InAppBrowser"/>
|
||||
</plugins>
|
||||
</cordova>
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
-->
|
||||
<cordova>
|
||||
<!--
|
||||
access elements control the Android whitelist.
|
||||
Domains are assumed blocked unless set otherwise
|
||||
-->
|
||||
|
||||
<access origin="http://127.0.0.1*"/> <!-- allow local pages -->
|
||||
|
||||
<!-- <access origin="https://example.com" /> allow any secure requests to example.com -->
|
||||
<!-- <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www -->
|
||||
<!-- <access origin=".*"/> Allow all domains, suggested development use only -->
|
||||
|
||||
<log level="DEBUG"/>
|
||||
<preference name="useBrowserHistory" value="false" />
|
||||
</cordova>
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -19,24 +19,29 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* This class listens to the accelerometer sensor and stores the latest
|
||||
* acceleration values x,y,z.
|
||||
*/
|
||||
public class AccelListener extends Plugin implements SensorEventListener {
|
||||
public class AccelListener extends CordovaPlugin implements SensorEventListener {
|
||||
|
||||
public static int STOPPED = 0;
|
||||
public static int STARTING = 1;
|
||||
@@ -51,7 +56,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
private SensorManager sensorManager; // Sensor manager
|
||||
private Sensor mSensor; // Acceleration sensor returned by sensor manager
|
||||
|
||||
private String callbackId; // Keeps track of the single "start" callback ID passed in from JS
|
||||
private CallbackContext callbackContext; // Keeps track of the JS callback context.
|
||||
|
||||
/**
|
||||
* Create an accelerometer listener.
|
||||
@@ -69,29 +74,25 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The associated CordovaWebView.
|
||||
*/
|
||||
|
||||
public void setContext(CordovaInterface cordova) {
|
||||
super.setContext(cordova);
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* Executes the request.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param args The exec() arguments.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
* @return Whether the action was valid.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
||||
String message = "";
|
||||
PluginResult result = new PluginResult(status, message);
|
||||
result.setKeepCallback(true);
|
||||
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("start")) {
|
||||
this.callbackId = callbackId;
|
||||
this.callbackContext = callbackContext;
|
||||
if (this.status != AccelListener.RUNNING) {
|
||||
// If not running, then this is an async call, so don't worry about waiting
|
||||
// We drop the callback onto our stack, call start, and let start and the sensor callback fire off the callback down the road
|
||||
@@ -104,9 +105,13 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
}
|
||||
} else {
|
||||
// Unsupported action
|
||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
|
||||
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT, "");
|
||||
result.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,21 +152,15 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
|
||||
return this.status;
|
||||
}
|
||||
|
||||
// Wait until running
|
||||
long timeout = 2000;
|
||||
while ((this.status == STARTING) && (timeout > 0)) {
|
||||
timeout = timeout - 100;
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (timeout == 0) {
|
||||
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
|
||||
this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
|
||||
}
|
||||
|
||||
// Set a timeout callback on the main thread.
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
AccelListener.this.timeout();
|
||||
}
|
||||
}, 2000);
|
||||
|
||||
return this.status;
|
||||
}
|
||||
|
||||
@@ -176,6 +175,18 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an error if the sensor hasn't started.
|
||||
*
|
||||
* Called two seconds after starting the listener.
|
||||
*/
|
||||
private void timeout() {
|
||||
if (this.status == AccelListener.STARTING) {
|
||||
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
|
||||
this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the accuracy of the sensor has changed.
|
||||
*
|
||||
@@ -224,6 +235,16 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view navigates.
|
||||
*/
|
||||
@Override
|
||||
public void onReset() {
|
||||
if (this.status == AccelListener.RUNNING) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Sends an error back to JS
|
||||
private void fail(int code, String message) {
|
||||
// Error object
|
||||
@@ -236,16 +257,14 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
}
|
||||
PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
|
||||
err.setKeepCallback(true);
|
||||
|
||||
this.error(err, this.callbackId);
|
||||
callbackContext.sendPluginResult(err);
|
||||
}
|
||||
|
||||
private void win() {
|
||||
// Success return object
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
|
||||
result.setKeepCallback(true);
|
||||
|
||||
this.success(result, this.callbackId);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
private void setStatus(int status) {
|
||||
|
||||
@@ -19,28 +19,30 @@
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||
*/
|
||||
public class App extends Plugin {
|
||||
public class App extends CordovaPlugin {
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback context from which we were invoked.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
@@ -79,9 +81,11 @@ public class App extends Plugin {
|
||||
else if (action.equals("exitApp")) {
|
||||
this.exitApp();
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
callbackContext.sendPluginResult(new PluginResult(status, result));
|
||||
return true;
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,7 +180,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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -39,7 +42,7 @@ import java.util.HashMap;
|
||||
* android_asset: file name must start with /android_asset/sound.mp3
|
||||
* sdcard: file name is just sound.mp3
|
||||
*/
|
||||
public class AudioHandler extends Plugin {
|
||||
public class AudioHandler extends CordovaPlugin {
|
||||
|
||||
public static String TAG = "AudioHandler";
|
||||
HashMap<String, AudioPlayer> players; // Audio player object
|
||||
@@ -57,75 +60,64 @@ public class AudioHandler 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.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (action.equals("startRecordingAudio")) {
|
||||
this.startRecordingAudio(args.getString(0), 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));
|
||||
}
|
||||
else if (action.equals("seekToAudio")) {
|
||||
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||
}
|
||||
else if (action.equals("pausePlayingAudio")) {
|
||||
this.pausePlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("stopPlayingAudio")) {
|
||||
this.stopPlayingAudio(args.getString(0));
|
||||
} else if (action.equals("setVolume")) {
|
||||
try {
|
||||
this.setVolume(args.getString(0), Float.parseFloat(args.getString(1)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
//no-op
|
||||
}
|
||||
} else if (action.equals("getCurrentPositionAudio")) {
|
||||
float f = this.getCurrentPositionAudio(args.getString(0));
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
float f = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("create")) {
|
||||
String id = args.getString(0);
|
||||
String src = args.getString(1);
|
||||
AudioPlayer audio = new AudioPlayer(this, id, src);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
else if (action.equals("release")) {
|
||||
boolean b = this.release(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
if (action.equals("startRecordingAudio")) {
|
||||
this.startRecordingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (action.equals("getCurrentPositionAudio")) {
|
||||
else if (action.equals("stopRecordingAudio")) {
|
||||
this.stopRecordingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("startPlayingAudio")) {
|
||||
this.startPlayingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||
}
|
||||
else if (action.equals("seekToAudio")) {
|
||||
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||
}
|
||||
else if (action.equals("pausePlayingAudio")) {
|
||||
this.pausePlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("stopPlayingAudio")) {
|
||||
this.stopPlayingAudio(args.getString(0));
|
||||
} else if (action.equals("setVolume")) {
|
||||
try {
|
||||
this.setVolume(args.getString(0), Float.parseFloat(args.getString(1)));
|
||||
} catch (NumberFormatException nfe) {
|
||||
//no-op
|
||||
}
|
||||
} else if (action.equals("getCurrentPositionAudio")) {
|
||||
float f = this.getCurrentPositionAudio(args.getString(0));
|
||||
callbackContext.sendPluginResult(new PluginResult(status, f));
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
float f = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
callbackContext.sendPluginResult(new PluginResult(status, f));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
else if (action.equals("create")) {
|
||||
String id = args.getString(0);
|
||||
String src = FileUtils.stripFileProtocol(args.getString(1));
|
||||
AudioPlayer audio = new AudioPlayer(this, id, src);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
else if (action.equals("release")) {
|
||||
boolean b = this.release(args.getString(0));
|
||||
callbackContext.sendPluginResult(new PluginResult(status, b));
|
||||
return true;
|
||||
}
|
||||
else { // Unrecognized action.
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.sendPluginResult(new PluginResult(status, result));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,6 +130,14 @@ public class AudioHandler extends Plugin {
|
||||
this.players.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all audio players and recorders on navigate.
|
||||
*/
|
||||
@Override
|
||||
public void onReset() {
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
*
|
||||
@@ -198,12 +198,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 +214,6 @@ public class AudioHandler extends Plugin {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopRecording();
|
||||
this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +234,7 @@ public class AudioHandler extends Plugin {
|
||||
/**
|
||||
* Seek to a location.
|
||||
* @param id The id of the audio player
|
||||
* @param miliseconds int: number of milliseconds to skip 1000 = 1 second
|
||||
* @param milliseconds int: number of milliseconds to skip 1000 = 1 second
|
||||
*/
|
||||
public void seekToAudio(String id, int milliseconds) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
|
||||
@@ -31,8 +31,6 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.cordova.AudioPlayer.MODE;
|
||||
|
||||
/**
|
||||
* This class implements the audio playback and recording capabilities used by Cordova.
|
||||
* It is called by the AudioHandler Cordova class.
|
||||
@@ -43,19 +41,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 +68,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 +79,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,40 +126,36 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
/**
|
||||
* Start recording the specified file.
|
||||
*
|
||||
*
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecording(String file) {
|
||||
switch (this.mode) {
|
||||
case PLAY:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
this.handler.webView.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.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
break;
|
||||
case RECORD:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +186,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 +198,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;
|
||||
}
|
||||
}
|
||||
@@ -225,7 +221,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
if (this.readyPlayer(this.audioFile)) {
|
||||
this.player.seekTo(milliseconds);
|
||||
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
|
||||
}
|
||||
else {
|
||||
this.seekOnPrepared = milliseconds;
|
||||
@@ -238,13 +234,13 @@ 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);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state.ordinal());
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,33 +249,36 @@ 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 {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state.ordinal());
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when playback of a media source has completed.
|
||||
*
|
||||
* @param player The MediaPlayer that reached the end of the file
|
||||
*
|
||||
* @param player The MediaPlayer that reached the end of the file
|
||||
*/
|
||||
public void onCompletion(MediaPlayer player) {
|
||||
Log.d(LOG_TAG, "on completion is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
*
|
||||
*
|
||||
* @return position in msec or -1 if not playing
|
||||
*/
|
||||
public long getCurrentPosition() {
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
int curPos = this.player.getCurrentPosition();
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
||||
return curPos;
|
||||
}
|
||||
else {
|
||||
@@ -290,7 +289,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 +304,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 +334,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 +355,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 + ");");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
|
||||
}
|
||||
|
||||
/**
|
||||
* By default Android returns the length of audio in mills but we want seconds
|
||||
*
|
||||
*
|
||||
* @return length of clip in seconds
|
||||
*/
|
||||
private float getDurationInSeconds() {
|
||||
@@ -373,7 +372,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.
|
||||
@@ -386,38 +385,38 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
this.player.release();
|
||||
|
||||
// Send error notification to JavaScript
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":" + arg1 + "});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":" + arg1 + "});");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state and send it to JavaScript.
|
||||
*
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
private void setState(STATE state) {
|
||||
if (this.state != state) {
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + this.state.ordinal() + ");");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state.ordinal() + ");");
|
||||
}
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the mode and send it to JavaScript.
|
||||
*
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
private void setMode(MODE mode) {
|
||||
if (this.mode != mode) {
|
||||
//mode is not part of the expected behaviour, so no notification
|
||||
//this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + mode + ");");
|
||||
//mode is not part of the expected behavior, so no notification
|
||||
//this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + mode + ");");
|
||||
}
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the audio state.
|
||||
*
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getState() {
|
||||
@@ -432,7 +431,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
|
||||
@@ -446,12 +445,12 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
break;
|
||||
case RECORD:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
return false; //player is not ready
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* attempts to initialize the media player for playback
|
||||
* @param file the file to play
|
||||
@@ -467,12 +466,12 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
return false;
|
||||
case MEDIA_LOADING:
|
||||
//cordova js is not aware of MEDIA_LOADING, so we send MEDIA_STARTING 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,26 +491,25 @@ 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 + "});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
//if we had to prepare= the file, we won't be in the correct state for playback
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: " + this.state);
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* load audio file
|
||||
* @throws IOException
|
||||
* @throws IllegalStateException
|
||||
* @throws SecurityException
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IOException
|
||||
* @throws IllegalStateException
|
||||
* @throws SecurityException
|
||||
* @throws IllegalArgumentException
|
||||
*/
|
||||
private void loadAudioFile(String file) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
|
||||
if (this.isStreaming(file)) {
|
||||
@@ -520,13 +518,13 @@ 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 {
|
||||
if (file.startsWith("/android_asset/")) {
|
||||
String f = file.substring(15);
|
||||
android.content.res.AssetFileDescriptor fd = this.handler.ctx.getActivity().getAssets().openFd(f);
|
||||
android.content.res.AssetFileDescriptor fd = this.handler.cordova.getActivity().getAssets().openFd(f);
|
||||
this.player.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
|
||||
}
|
||||
else {
|
||||
@@ -534,13 +532,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
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -30,13 +31,13 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.util.Log;
|
||||
|
||||
public class BatteryListener extends Plugin {
|
||||
public class BatteryListener extends CordovaPlugin {
|
||||
|
||||
private static final String LOG_TAG = "BatteryManager";
|
||||
|
||||
BroadcastReceiver receiver;
|
||||
|
||||
private String batteryCallbackId = null;
|
||||
private CallbackContext batteryCallbackContext = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -46,22 +47,20 @@ public class BatteryListener extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
* Executes the request.
|
||||
*
|
||||
* @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.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false if not.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
|
||||
String result = "Unsupported Operation: " + action;
|
||||
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("start")) {
|
||||
if (this.batteryCallbackId != null) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, "Battery listener already running.");
|
||||
if (this.batteryCallbackContext != null) {
|
||||
callbackContext.error( "Battery listener already running.");
|
||||
return true;
|
||||
}
|
||||
this.batteryCallbackId = callbackId;
|
||||
this.batteryCallbackContext = callbackContext;
|
||||
|
||||
// We need to listen to power events to update battery status
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
@@ -79,17 +78,19 @@ public class BatteryListener extends Plugin {
|
||||
// Don't return any result now, since status results will be sent when events come in from broadcast receiver
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
pluginResult.setKeepCallback(true);
|
||||
return pluginResult;
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
else if (action.equals("stop")) {
|
||||
removeBatteryListener();
|
||||
this.sendUpdate(new JSONObject(), false); // release status callback in JS side
|
||||
this.batteryCallbackId = null;
|
||||
return new PluginResult(PluginResult.Status.OK);
|
||||
this.batteryCallbackContext = null;
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
return new PluginResult(status, result);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,6 +100,13 @@ public class BatteryListener extends Plugin {
|
||||
removeBatteryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop battery receiver.
|
||||
*/
|
||||
public void onReset() {
|
||||
removeBatteryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the battery receiver and set it to null.
|
||||
*/
|
||||
@@ -146,10 +154,10 @@ public class BatteryListener extends Plugin {
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(JSONObject info, boolean keepCallback) {
|
||||
if (this.batteryCallbackId != null) {
|
||||
if (this.batteryCallbackContext != null) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, info);
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.success(result, this.batteryCallbackId);
|
||||
this.batteryCallbackContext.sendPluginResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,426 +0,0 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* This class provides a way for Java to run JavaScript in the web page that has loaded Cordova.
|
||||
* The CallbackServer class implements an XHR server and a polling server with a list of JavaScript
|
||||
* statements that are to be executed on the web page.
|
||||
*
|
||||
* The process flow for XHR is:
|
||||
* 1. JavaScript makes an async XHR call.
|
||||
* 2. The server holds the connection open until data is available.
|
||||
* 3. The server writes the data to the client and closes the connection.
|
||||
* 4. The server immediately starts listening for the next XHR call.
|
||||
* 5. The client receives this XHR response, processes it.
|
||||
* 6. The client sends a new async XHR request.
|
||||
*
|
||||
* The CallbackServer class requires the following permission in Android manifest file
|
||||
* <uses-permission android:name="android.permission.INTERNET" />
|
||||
*
|
||||
* If the device has a proxy set, then XHR cannot be used, so polling must be used instead.
|
||||
* This can be determined by the client by calling CallbackServer.usePolling().
|
||||
*
|
||||
* The process flow for polling is:
|
||||
* 1. The client calls CallbackServer.getJavascript() to retrieve next statement.
|
||||
* 2. If statement available, then client processes it.
|
||||
* 3. The client repeats #1 in loop.
|
||||
*/
|
||||
public class CallbackServer implements Runnable {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "CallbackServer";
|
||||
|
||||
/**
|
||||
* The list of JavaScript statements to be sent to JavaScript.
|
||||
*/
|
||||
private LinkedList<String> javascript;
|
||||
|
||||
/**
|
||||
* The port to listen on.
|
||||
*/
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* The server thread.
|
||||
*/
|
||||
private Thread serverThread;
|
||||
|
||||
/**
|
||||
* Indicates the server is running.
|
||||
*/
|
||||
private boolean active;
|
||||
|
||||
/**
|
||||
* Indicates that the JavaScript statements list is empty
|
||||
*/
|
||||
private boolean empty;
|
||||
|
||||
/**
|
||||
* Indicates that polling should be used instead of XHR.
|
||||
*/
|
||||
private boolean usePolling = true;
|
||||
|
||||
/**
|
||||
* Security token to prevent other apps from accessing this callback server via XHR
|
||||
*/
|
||||
private String token;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CallbackServer() {
|
||||
//Log.d(LOG_TAG, "CallbackServer()");
|
||||
this.active = false;
|
||||
this.empty = true;
|
||||
this.port = 0;
|
||||
this.javascript = new LinkedList<String>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Init callback server and start XHR if running local app.
|
||||
*
|
||||
* If Cordova app is loaded from file://, then we can use XHR
|
||||
* otherwise we have to use polling due to cross-domain security restrictions.
|
||||
*
|
||||
* @param url The URL of the Cordova app being loaded
|
||||
*/
|
||||
public void init(String url) {
|
||||
//System.out.println("CallbackServer.start("+url+")");
|
||||
this.active = false;
|
||||
this.empty = true;
|
||||
this.port = 0;
|
||||
this.javascript = new LinkedList<String>();
|
||||
|
||||
// Determine if XHR or polling is to be used
|
||||
if ((url != null) && !url.startsWith("file://")) {
|
||||
this.usePolling = true;
|
||||
this.stopServer();
|
||||
}
|
||||
else if (android.net.Proxy.getDefaultHost() != null) {
|
||||
this.usePolling = true;
|
||||
this.stopServer();
|
||||
}
|
||||
else {
|
||||
this.usePolling = false;
|
||||
this.startServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public boolean usePolling() {
|
||||
return this.usePolling;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port that this server is running on.
|
||||
* @return
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the security token that this server requires when calling getJavascript().
|
||||
* @return
|
||||
*/
|
||||
public String getToken() {
|
||||
return this.token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the server on a new thread.
|
||||
*/
|
||||
public void startServer() {
|
||||
//Log.d(LOG_TAG, "CallbackServer.startServer()");
|
||||
this.active = false;
|
||||
|
||||
// Start server on new thread
|
||||
this.serverThread = new Thread(this);
|
||||
this.serverThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart the server on a new thread.
|
||||
*/
|
||||
public void restartServer() {
|
||||
|
||||
// Stop server
|
||||
this.stopServer();
|
||||
|
||||
// Start server again
|
||||
this.startServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start running the server.
|
||||
* This is called automatically when the server thread is started.
|
||||
*/
|
||||
public void run() {
|
||||
|
||||
// Start server
|
||||
try {
|
||||
this.active = true;
|
||||
String request;
|
||||
ServerSocket waitSocket = new ServerSocket(0);
|
||||
this.port = waitSocket.getLocalPort();
|
||||
//Log.d(LOG_TAG, "CallbackServer -- using port " +this.port);
|
||||
this.token = java.util.UUID.randomUUID().toString();
|
||||
//Log.d(LOG_TAG, "CallbackServer -- using token "+this.token);
|
||||
|
||||
while (this.active) {
|
||||
//Log.d(LOG_TAG, "CallbackServer: Waiting for data on socket");
|
||||
Socket connection = waitSocket.accept();
|
||||
BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()), 40);
|
||||
DataOutputStream output = new DataOutputStream(connection.getOutputStream());
|
||||
request = xhrReader.readLine();
|
||||
String response = "";
|
||||
//Log.d(LOG_TAG, "CallbackServerRequest="+request);
|
||||
if (this.active && (request != null)) {
|
||||
if (request.contains("GET")) {
|
||||
|
||||
// Get requested file
|
||||
String[] requestParts = request.split(" ");
|
||||
|
||||
// Must have security token
|
||||
if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
|
||||
//Log.d(LOG_TAG, "CallbackServer -- Processing GET request");
|
||||
|
||||
// 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) {
|
||||
try {
|
||||
this.wait(10000); // prevent timeout from happening
|
||||
//Log.d(LOG_TAG, "CallbackServer>>> break <<<");
|
||||
break;
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If server is still running
|
||||
if (this.active) {
|
||||
|
||||
// If no data, then send 404 back to client before it times out
|
||||
if (this.empty) {
|
||||
//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");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
response = "HTTP/1.1 403 Forbidden\r\n\r\n ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
|
||||
}
|
||||
//Log.d(LOG_TAG, "CallbackServer: response="+response);
|
||||
//Log.d(LOG_TAG, "CallbackServer: closing output");
|
||||
output.writeBytes(response);
|
||||
output.flush();
|
||||
}
|
||||
output.close();
|
||||
xhrReader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.active = false;
|
||||
//Log.d(LOG_TAG, "CallbackServer.startServer() - EXIT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop server.
|
||||
* This stops the thread that the server is running on.
|
||||
*/
|
||||
public void stopServer() {
|
||||
//Log.d(LOG_TAG, "CallbackServer.stopServer()");
|
||||
if (this.active) {
|
||||
this.active = false;
|
||||
|
||||
// Break out of server wait
|
||||
synchronized (this) {
|
||||
this.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy
|
||||
*/
|
||||
public void destroy() {
|
||||
this.stopServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of JavaScript statements.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getSize() {
|
||||
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.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/* The Following code has been modified from original implementation of URLEncoder */
|
||||
|
||||
/* start */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static final String digits = "0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* This will encode the return value to JavaScript. We revert the encoding for
|
||||
* common characters that don't require encoding to reduce the size of the string
|
||||
* being passed to JavaScript.
|
||||
*
|
||||
* @param s to be encoded
|
||||
* @param enc encoding type
|
||||
* @return encoded string
|
||||
*/
|
||||
public static String encode(String s, String enc) throws UnsupportedEncodingException {
|
||||
if (s == null || enc == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
// check for UnsupportedEncodingException
|
||||
"".getBytes(enc);
|
||||
|
||||
// Guess a bit bigger for encoded form
|
||||
StringBuilder buf = new StringBuilder(s.length() + 16);
|
||||
int start = -1;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) {
|
||||
if (start >= 0) {
|
||||
convert(s.substring(start, i), buf, enc);
|
||||
start = -1;
|
||||
}
|
||||
if (ch != ' ') {
|
||||
buf.append(ch);
|
||||
} else {
|
||||
buf.append(' ');
|
||||
}
|
||||
} else {
|
||||
if (start < 0) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start >= 0) {
|
||||
convert(s.substring(start, s.length()), buf, enc);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException {
|
||||
byte[] bytes = s.getBytes(enc);
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
buf.append('%');
|
||||
buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
|
||||
buf.append(digits.charAt(bytes[j] & 0xf));
|
||||
}
|
||||
}
|
||||
|
||||
/* end */
|
||||
}
|
||||
@@ -27,8 +27,9 @@ import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -53,7 +54,7 @@ import android.util.Log;
|
||||
* and returns the captured image. When the camera view is closed, the screen displayed before
|
||||
* the camera view was shown is redisplayed.
|
||||
*/
|
||||
public class CameraLauncher extends Plugin implements MediaScannerConnectionClient {
|
||||
public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient {
|
||||
|
||||
private static final int DATA_URL = 0; // Return base64 encoded string
|
||||
private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android)
|
||||
@@ -82,9 +83,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
private int mediaType; // What type of media to retrieve
|
||||
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
|
||||
private boolean correctOrientation; // Should the pictures orientation be corrected
|
||||
private boolean allowEdit; // Should we allow the user to crop the image
|
||||
//private boolean allowEdit; // Should we allow the user to crop the image. UNUSED.
|
||||
|
||||
public String callbackId;
|
||||
public CallbackContext callbackContext;
|
||||
private int numPics;
|
||||
|
||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||
@@ -110,62 +111,56 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
/**
|
||||
* 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.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
this.callbackId = callbackId;
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
|
||||
try {
|
||||
if (action.equals("takePicture")) {
|
||||
int srcType = CAMERA;
|
||||
int destType = FILE_URI;
|
||||
this.saveToPhotoAlbum = false;
|
||||
this.targetHeight = 0;
|
||||
this.targetWidth = 0;
|
||||
this.encodingType = JPEG;
|
||||
this.mediaType = PICTURE;
|
||||
this.mQuality = 80;
|
||||
if (action.equals("takePicture")) {
|
||||
int srcType = CAMERA;
|
||||
int destType = FILE_URI;
|
||||
this.saveToPhotoAlbum = false;
|
||||
this.targetHeight = 0;
|
||||
this.targetWidth = 0;
|
||||
this.encodingType = JPEG;
|
||||
this.mediaType = PICTURE;
|
||||
this.mQuality = 80;
|
||||
|
||||
this.mQuality = args.getInt(0);
|
||||
destType = args.getInt(1);
|
||||
srcType = args.getInt(2);
|
||||
this.targetWidth = args.getInt(3);
|
||||
this.targetHeight = args.getInt(4);
|
||||
this.encodingType = args.getInt(5);
|
||||
this.mediaType = args.getInt(6);
|
||||
this.allowEdit = args.getBoolean(7);
|
||||
this.correctOrientation = args.getBoolean(8);
|
||||
this.saveToPhotoAlbum = args.getBoolean(9);
|
||||
this.mQuality = args.getInt(0);
|
||||
destType = args.getInt(1);
|
||||
srcType = args.getInt(2);
|
||||
this.targetWidth = args.getInt(3);
|
||||
this.targetHeight = args.getInt(4);
|
||||
this.encodingType = args.getInt(5);
|
||||
this.mediaType = args.getInt(6);
|
||||
//this.allowEdit = args.getBoolean(7); // This field is unused.
|
||||
this.correctOrientation = args.getBoolean(8);
|
||||
this.saveToPhotoAlbum = args.getBoolean(9);
|
||||
|
||||
// If the user specifies a 0 or smaller width/height
|
||||
// make it -1 so later comparrisions succeed
|
||||
if (this.targetWidth < 1) {
|
||||
this.targetWidth = -1;
|
||||
}
|
||||
if (this.targetHeight < 1) {
|
||||
this.targetHeight = -1;
|
||||
}
|
||||
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
}
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
// If the user specifies a 0 or smaller width/height
|
||||
// make it -1 so later comparisons succeed
|
||||
if (this.targetWidth < 1) {
|
||||
this.targetWidth = -1;
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
if (this.targetHeight < 1) {
|
||||
this.targetHeight = -1;
|
||||
}
|
||||
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(destType, encodingType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(srcType, destType);
|
||||
}
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
callbackContext.sendPluginResult(r);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -199,7 +194,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((Plugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||
}
|
||||
// else
|
||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
||||
@@ -251,7 +246,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((Plugin) this, Intent.createChooser(intent,
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
||||
new String(title)), (srcType + 1) * 16 + returnType + 1);
|
||||
}
|
||||
}
|
||||
@@ -273,26 +268,38 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
// If CAMERA
|
||||
if (srcType == CAMERA) {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
// If image available
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/.Pic.jpg");
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Bitmap bitmap = null;
|
||||
Uri uri = null;
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
if (bitmap == null) {
|
||||
// Try to get the bitmap from intent.
|
||||
bitmap = (Bitmap)intent.getExtras().get("data");
|
||||
}
|
||||
|
||||
// Double-check the bitmap.
|
||||
if (bitmap == null) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to create bitmap!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
@@ -318,7 +325,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && rotate == 0) {
|
||||
writeUncompressedImage(uri);
|
||||
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
|
||||
@@ -345,7 +352,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
}
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
|
||||
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
|
||||
@@ -376,18 +383,33 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
// If you ask for video or all media type you will automatically get back a file URI
|
||||
// and there will be no attempt to resize any returned data
|
||||
if (this.mediaType != PICTURE) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
else {
|
||||
// This is a special case to just return the path as no scaling,
|
||||
// rotating or compression needs to be done
|
||||
if (this.targetHeight == -1 && this.targetWidth == -1 &&
|
||||
this.mQuality == 100 && destType == FILE_URI && !this.correctOrientation) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
// Get the path to the image. Makes loading so much easier.
|
||||
String imagePath = FileUtils.getRealPathFromURI(uri, this.cordova);
|
||||
String mimeType = FileUtils.getMimeType(imagePath);
|
||||
// Log.d(LOG_TAG, "Real path = " + imagePath);
|
||||
// Log.d(LOG_TAG, "mime type = " + mimeType);
|
||||
// If we don't have a valid image so quit.
|
||||
if (imagePath == null || mimeType == null ||
|
||||
!(mimeType.equalsIgnoreCase("image/jpeg") || mimeType.equalsIgnoreCase("image/png"))) {
|
||||
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||
this.failPicture("Unable to retrieve 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 };
|
||||
@@ -440,18 +462,20 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
|
||||
// The resized image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.success(new PluginResult(PluginResult.Status.OK, ("file://" + resizePath + "?" + System.currentTimeMillis())), this.callbackId);
|
||||
this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
}
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
@@ -554,6 +578,9 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options);
|
||||
if (unscaledBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
|
||||
}
|
||||
@@ -714,7 +741,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
byte[] code = jpeg_data.toByteArray();
|
||||
byte[] output = Base64.encodeBase64(code);
|
||||
String js_out = new String(output);
|
||||
this.success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId);
|
||||
this.callbackContext.success(js_out);
|
||||
js_out = null;
|
||||
output = null;
|
||||
code = null;
|
||||
@@ -731,7 +758,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
* @param err
|
||||
*/
|
||||
public void failPicture(String err) {
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
this.callbackContext.error(err);
|
||||
}
|
||||
|
||||
private void scanForGallery(Uri newImage) {
|
||||
@@ -739,7 +766,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
if(this.conn != null) {
|
||||
this.conn.disconnect();
|
||||
}
|
||||
this.conn = new MediaScannerConnection(this.ctx.getActivity().getApplicationContext(), this);
|
||||
this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
|
||||
conn.connect();
|
||||
}
|
||||
|
||||
@@ -747,7 +774,7 @@ public class CameraLauncher extends Plugin implements MediaScannerConnectionClie
|
||||
try{
|
||||
this.conn.scanFile(this.scanMe.toString(), "image/*");
|
||||
} catch (java.lang.IllegalStateException e){
|
||||
LOG.e(LOG_TAG, "Can't scan file in MediaScanner aftering taking picture");
|
||||
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,8 +23,9 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -41,7 +42,7 @@ import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
public class Capture extends Plugin {
|
||||
public class Capture extends CordovaPlugin {
|
||||
|
||||
private static final String VIDEO_3GPP = "video/3gpp";
|
||||
private static final String VIDEO_MP4 = "video/mp4";
|
||||
@@ -57,13 +58,11 @@ public class Capture extends Plugin {
|
||||
// private static final int CAPTURE_APPLICATION_BUSY = 1;
|
||||
// private static final int CAPTURE_INVALID_ARGUMENT = 2;
|
||||
private static final int CAPTURE_NO_MEDIA_FILES = 3;
|
||||
private static final int CAPTURE_NOT_SUPPORTED = 20;
|
||||
|
||||
private String callbackId; // The ID of the callback to be invoked with our result
|
||||
private CallbackContext callbackContext; // The callback context from which we were invoked.
|
||||
private long limit; // the number of pics/vids/clips to take
|
||||
private double duration; // optional duration parameter for video recording
|
||||
private JSONArray results; // The array of results to be returned to the user
|
||||
private Uri imageUri; // Uri of captured image
|
||||
private int numPics; // Number of pictures before capture activity
|
||||
|
||||
//private CordovaInterface cordova;
|
||||
@@ -77,8 +76,8 @@ public class Capture extends Plugin {
|
||||
// }
|
||||
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
this.callbackId = callbackId;
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
this.limit = 1;
|
||||
this.duration = 0.0f;
|
||||
this.results = new JSONArray();
|
||||
@@ -90,12 +89,9 @@ public class Capture extends Plugin {
|
||||
}
|
||||
|
||||
if (action.equals("getFormatData")) {
|
||||
try {
|
||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||
return new PluginResult(PluginResult.Status.OK, obj);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR);
|
||||
}
|
||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||
callbackContext.success(obj);
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("captureAudio")) {
|
||||
this.captureAudio();
|
||||
@@ -106,10 +102,11 @@ public class Capture extends Plugin {
|
||||
else if (action.equals("captureVideo")) {
|
||||
this.captureVideo(duration);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -119,34 +116,30 @@ public class Capture extends Plugin {
|
||||
* @param mimeType of the file
|
||||
* @return a MediaFileData object
|
||||
*/
|
||||
private JSONObject getFormatData(String filePath, String mimeType) {
|
||||
private JSONObject getFormatData(String filePath, String mimeType) throws JSONException {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
// setup defaults
|
||||
obj.put("height", 0);
|
||||
obj.put("width", 0);
|
||||
obj.put("bitrate", 0);
|
||||
obj.put("duration", 0);
|
||||
obj.put("codecs", "");
|
||||
// setup defaults
|
||||
obj.put("height", 0);
|
||||
obj.put("width", 0);
|
||||
obj.put("bitrate", 0);
|
||||
obj.put("duration", 0);
|
||||
obj.put("codecs", "");
|
||||
|
||||
// If the mimeType isn't set the rest will fail
|
||||
// so let's see if we can determine it.
|
||||
if (mimeType == null || mimeType.equals("")) {
|
||||
mimeType = FileUtils.getMimeType(filePath);
|
||||
}
|
||||
Log.d(LOG_TAG, "Mime type = " + mimeType);
|
||||
// If the mimeType isn't set the rest will fail
|
||||
// so let's see if we can determine it.
|
||||
if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) {
|
||||
mimeType = FileUtils.getMimeType(filePath);
|
||||
}
|
||||
Log.d(LOG_TAG, "Mime type = " + mimeType);
|
||||
|
||||
if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
|
||||
obj = getImageData(filePath, obj);
|
||||
}
|
||||
else if (mimeType.endsWith(AUDIO_3GPP)) {
|
||||
obj = getAudioVideoData(filePath, obj, false);
|
||||
}
|
||||
else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
|
||||
obj = getAudioVideoData(filePath, obj, true);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Error: setting media file data object");
|
||||
if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
|
||||
obj = getImageData(filePath, obj);
|
||||
}
|
||||
else if (mimeType.endsWith(AUDIO_3GPP)) {
|
||||
obj = getAudioVideoData(filePath, obj, false);
|
||||
}
|
||||
else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
|
||||
obj = getAudioVideoData(filePath, obj, true);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
@@ -199,7 +192,7 @@ public class Capture extends Plugin {
|
||||
private void captureAudio() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
|
||||
this.cordova.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,9 +207,8 @@ public class Capture extends Plugin {
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), "Capture.jpg");
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
this.cordova.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -227,7 +219,7 @@ public class Capture extends Plugin {
|
||||
// Introduced in API 8
|
||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
||||
|
||||
this.cordova.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +244,7 @@ public class Capture extends Plugin {
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for listening to audio
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more audio clips
|
||||
captureAudio();
|
||||
@@ -298,7 +290,7 @@ public class Capture extends Plugin {
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more images
|
||||
captureImage();
|
||||
@@ -315,7 +307,7 @@ public class Capture extends Plugin {
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
@@ -326,7 +318,7 @@ public class Capture extends Plugin {
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
}
|
||||
// user canceled the action
|
||||
else {
|
||||
@@ -337,7 +329,7 @@ public class Capture extends Plugin {
|
||||
else {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId);
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, results));
|
||||
}
|
||||
// something bad happened
|
||||
else {
|
||||
@@ -401,7 +393,7 @@ public class Capture extends Plugin {
|
||||
* @param err
|
||||
*/
|
||||
public void fail(JSONObject err) {
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
this.callbackContext.error(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -20,8 +20,9 @@ package org.apache.cordova;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -33,10 +34,13 @@ import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
/**
|
||||
* This class listens to the compass sensor and stores the latest heading value.
|
||||
*/
|
||||
public class CompassListener extends Plugin implements SensorEventListener {
|
||||
public class CompassListener extends CordovaPlugin implements SensorEventListener {
|
||||
|
||||
public static int STOPPED = 0;
|
||||
public static int STARTING = 1;
|
||||
@@ -54,6 +58,8 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
||||
private SensorManager sensorManager;// Sensor manager
|
||||
Sensor mSensor; // Compass sensor returned by sensor manager
|
||||
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@@ -68,95 +74,62 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void setContext(CordovaInterface cordova) {
|
||||
super.setContext(cordova);
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackS=Context The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (action.equals("start")) {
|
||||
this.start();
|
||||
}
|
||||
else if (action.equals("stop")) {
|
||||
this.stop();
|
||||
}
|
||||
else if (action.equals("getStatus")) {
|
||||
int i = this.getStatus();
|
||||
return new PluginResult(status, i);
|
||||
}
|
||||
else if (action.equals("getHeading")) {
|
||||
// If not running, then this is an async call, so don't worry about waiting
|
||||
if (this.status != CompassListener.RUNNING) {
|
||||
int r = this.start();
|
||||
if (r == CompassListener.ERROR_FAILED_TO_START) {
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
// Wait until running
|
||||
long timeout = 2000;
|
||||
while ((this.status == STARTING) && (timeout > 0)) {
|
||||
timeout = timeout - 100;
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (timeout == 0) {
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
}
|
||||
return new PluginResult(status, getCompassHeading());
|
||||
}
|
||||
else if (action.equals("setTimeout")) {
|
||||
this.setTimeout(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
long l = this.getTimeout();
|
||||
return new PluginResult(status, l);
|
||||
} else {
|
||||
// Unsupported action
|
||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("start")) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (action.equals("getStatus")) {
|
||||
return true;
|
||||
else if (action.equals("stop")) {
|
||||
this.stop();
|
||||
}
|
||||
else if (action.equals("getStatus")) {
|
||||
int i = this.getStatus();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i));
|
||||
}
|
||||
else if (action.equals("getHeading")) {
|
||||
// Can only return value if RUNNING
|
||||
if (this.status == CompassListener.RUNNING) {
|
||||
return true;
|
||||
// If not running, then this is an async call, so don't worry about waiting
|
||||
if (this.status != CompassListener.RUNNING) {
|
||||
int r = this.start();
|
||||
if (r == CompassListener.ERROR_FAILED_TO_START) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, CompassListener.ERROR_FAILED_TO_START));
|
||||
return true;
|
||||
}
|
||||
// Set a timeout callback on the main thread.
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
CompassListener.this.timeout();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, getCompassHeading()));
|
||||
}
|
||||
else if (action.equals("setTimeout")) {
|
||||
this.setTimeout(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
return true;
|
||||
long l = this.getTimeout();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l));
|
||||
} else {
|
||||
// Unsupported action
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -166,6 +139,13 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when app has navigated and JS listeners have been destroyed.
|
||||
*/
|
||||
public void onReset() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -216,6 +196,18 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a delay to time out if the listener has not attached fast enough.
|
||||
*/
|
||||
private void timeout() {
|
||||
if (this.status == CompassListener.STARTING) {
|
||||
this.setStatus(CompassListener.ERROR_FAILED_TO_START);
|
||||
if (this.callbackContext != null) {
|
||||
this.callbackContext.error("Compass listener failed to start.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sensor listener event.
|
||||
*
|
||||
@@ -287,19 +279,15 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
||||
*
|
||||
* @return a compass heading
|
||||
*/
|
||||
private JSONObject getCompassHeading() {
|
||||
private JSONObject getCompassHeading() throws JSONException {
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
obj.put("magneticHeading", this.getHeading());
|
||||
obj.put("trueHeading", this.getHeading());
|
||||
// Since the magnetic and true heading are always the same our and accuracy
|
||||
// is defined as the difference between true and magnetic always return zero
|
||||
obj.put("headingAccuracy", 0);
|
||||
obj.put("timestamp", this.timeStamp);
|
||||
} catch (JSONException e) {
|
||||
// Should never happen
|
||||
}
|
||||
obj.put("magneticHeading", this.getHeading());
|
||||
obj.put("trueHeading", this.getHeading());
|
||||
// Since the magnetic and true heading are always the same our and accuracy
|
||||
// is defined as the difference between true and magnetic always return zero
|
||||
obj.put("headingAccuracy", 0);
|
||||
obj.put("timestamp", this.timeStamp);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,25 @@
|
||||
|
||||
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.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 +50,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 +151,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
|
||||
try {
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (!multiple) {
|
||||
@@ -163,6 +164,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 +184,89 @@ 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._ID);
|
||||
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,21 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
JSONArray websites = new JSONArray();
|
||||
JSONArray photos = new JSONArray();
|
||||
|
||||
// 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 +383,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 +415,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 +441,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,
|
||||
@@ -709,10 +793,10 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
formatted.append(middleName + " ");
|
||||
}
|
||||
if (familyName != null) {
|
||||
formatted.append(familyName + " ");
|
||||
formatted.append(familyName);
|
||||
}
|
||||
if (honorificSuffix != null) {
|
||||
formatted.append(honorificSuffix + " ");
|
||||
formatted.append(" " + honorificSuffix);
|
||||
}
|
||||
|
||||
contactName.put("familyName", familyName);
|
||||
@@ -776,7 +860,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
im.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im._ID)));
|
||||
im.put("pref", false); // Android does not store pref attribute
|
||||
im.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
|
||||
im.put("type", getContactType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.TYPE))));
|
||||
String type = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.PROTOCOL));
|
||||
im.put("type", getImType(new Integer(type).intValue()));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
@@ -1162,7 +1247,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
|
||||
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Im.TYPE, getImType(getJsonString(im, "type")));
|
||||
|
||||
ops.add(ContentProviderOperation.newInsert(
|
||||
ContactsContract.Data.CONTENT_URI).withValues(contentValues).build());
|
||||
@@ -1326,7 +1411,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
retVal = false;
|
||||
}
|
||||
|
||||
// if the save was a succes return the contact ID
|
||||
// if the save was a success return the contact ID
|
||||
if (retVal) {
|
||||
return id;
|
||||
} else {
|
||||
@@ -1361,7 +1446,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Im.DATA, getJsonString(im, "value"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, getContactType(getJsonString(im, "type")))
|
||||
.withValue(ContactsContract.CommonDataKinds.Im.TYPE, getImType(getJsonString(im, "type")))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -1416,7 +1501,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Email.DATA, getJsonString(email, "value"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, getPhoneType(getJsonString(email, "type")))
|
||||
.withValue(ContactsContract.CommonDataKinds.Email.TYPE, getContactType(getJsonString(email, "type")))
|
||||
.build());
|
||||
}
|
||||
|
||||
@@ -1495,7 +1580,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
Uri uri = Uri.parse(path);
|
||||
return mApp.getActivity().getContentResolver().openInputStream(uri);
|
||||
}
|
||||
if (path.startsWith("http:") || path.startsWith("file:")) {
|
||||
if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("file:")) {
|
||||
URL url = new URL(path);
|
||||
return url.openStream();
|
||||
}
|
||||
@@ -2005,5 +2090,86 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getImType(String string) {
|
||||
int type = ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM;
|
||||
if (string != null) {
|
||||
if ("aim".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM;
|
||||
}
|
||||
else if ("google talk".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK;
|
||||
}
|
||||
else if ("icq".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ;
|
||||
}
|
||||
else if ("jabber".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER;
|
||||
}
|
||||
else if ("msn".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN;
|
||||
}
|
||||
else if ("netmeeting".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_NETMEETING;
|
||||
}
|
||||
else if ("qq".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ;
|
||||
}
|
||||
else if ("skype".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE;
|
||||
}
|
||||
else if ("yahoo".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getImType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_AIM:
|
||||
stringType = "AIM";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_GOOGLE_TALK:
|
||||
stringType = "Google Talk";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_ICQ:
|
||||
stringType = "ICQ";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_JABBER:
|
||||
stringType = "Jabber";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_MSN:
|
||||
stringType = "MSN";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_NETMEETING:
|
||||
stringType = "NetMeeting";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_QQ:
|
||||
stringType = "QQ";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_SKYPE:
|
||||
stringType = "Skype";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Im.PROTOCOL_YAHOO:
|
||||
stringType = "Yahoo";
|
||||
break;
|
||||
default:
|
||||
stringType = "custom";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,15 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import android.util.Log;
|
||||
|
||||
public class ContactManager extends Plugin {
|
||||
public class ContactManager extends CordovaPlugin {
|
||||
|
||||
private ContactAccessor contactAccessor;
|
||||
private static final String LOG_TAG = "Contact Query";
|
||||
@@ -47,21 +48,19 @@ public class ContactManager 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.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArray of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false otherwise.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
/**
|
||||
* Check to see if we are on an Android 1.X device. If we are return an error as we
|
||||
* do not support this as of Cordova 1.0.
|
||||
*/
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, ContactManager.NOT_SUPPORTED_ERROR);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ContactManager.NOT_SUPPORTED_ERROR));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,30 +71,52 @@ public class ContactManager extends Plugin {
|
||||
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.cordova);
|
||||
}
|
||||
|
||||
try {
|
||||
if (action.equals("search")) {
|
||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
|
||||
return new PluginResult(status, res);
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
String id = contactAccessor.save(args.getJSONObject(0));
|
||||
if (id != null) {
|
||||
JSONObject res = contactAccessor.getContactById(id);
|
||||
if (action.equals("search")) {
|
||||
final JSONArray filter = args.getJSONArray(0);
|
||||
final JSONObject options = args.getJSONObject(1);
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
JSONArray res = contactAccessor.search(filter, options);
|
||||
callbackContext.success(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
final JSONObject contact = args.getJSONObject(0);
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
JSONObject res = null;
|
||||
String id = contactAccessor.save(contact);
|
||||
if (id != null) {
|
||||
try {
|
||||
res = contactAccessor.getContactById(id);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, "JSON fail.", e);
|
||||
}
|
||||
}
|
||||
if (res != null) {
|
||||
return new PluginResult(status, res);
|
||||
callbackContext.success(res);
|
||||
} else {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
if (contactAccessor.remove(args.getString(0))) {
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
}
|
||||
// If we get to this point an error has occurred
|
||||
return new PluginResult(PluginResult.Status.ERROR, ContactManager.UNKNOWN_ERROR);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
});
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
final String contactId = args.getString(0);
|
||||
this.cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
if (contactAccessor.remove(contactId)) {
|
||||
callbackContext.success();
|
||||
} else {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, UNKNOWN_ERROR));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,13 @@ import org.apache.cordova.api.LOG;
|
||||
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.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
//import android.view.View;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.webkit.ConsoleMessage;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
@@ -37,6 +38,9 @@ import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
/**
|
||||
* This class is the WebChromeClient that implements callbacks for our web view.
|
||||
@@ -48,6 +52,9 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
private CordovaInterface cordova;
|
||||
private CordovaWebView appView;
|
||||
|
||||
// the video progress view
|
||||
private View mVideoProgressView;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -101,7 +108,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
dlg.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
result.confirm();
|
||||
result.cancel();
|
||||
}
|
||||
});
|
||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
@@ -203,18 +210,23 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
String service = array.getString(0);
|
||||
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);
|
||||
String r = this.appView.exposedJsApi.exec(service, action, callbackId, message);
|
||||
result.confirm(r == null ? "" : r);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the native->JS bridge mode.
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_bridge_mode:")) {
|
||||
this.appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
|
||||
result.confirm("");
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
||||
String r = this.appView.callbackServer.getJavascript();
|
||||
result.confirm(r);
|
||||
String r = this.appView.exposedJsApi.retrieveJsMessages();
|
||||
result.confirm(r == null ? "" : r);
|
||||
}
|
||||
|
||||
// Do NO-OP so older code doesn't display dialog
|
||||
@@ -222,24 +234,6 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
result.confirm("OK");
|
||||
}
|
||||
|
||||
// Calling into CallbackServer
|
||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_callbackServer:")) {
|
||||
String r = "";
|
||||
if (message.equals("usePolling")) {
|
||||
r = "" + this.appView.callbackServer.usePolling();
|
||||
}
|
||||
else if (message.equals("restartServer")) {
|
||||
this.appView.callbackServer.restartServer();
|
||||
}
|
||||
else if (message.equals("getPort")) {
|
||||
r = Integer.toString(this.appView.callbackServer.getPort());
|
||||
}
|
||||
else if (message.equals("getToken")) {
|
||||
r = this.appView.callbackServer.getToken();
|
||||
}
|
||||
result.confirm(r);
|
||||
}
|
||||
|
||||
// Show dialog
|
||||
else {
|
||||
final JsPromptResult res = result;
|
||||
@@ -302,14 +296,20 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
}
|
||||
|
||||
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
||||
// Expect this to not compile in a future Android release!
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
||||
{
|
||||
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||
//This is only for Android 2.1
|
||||
if(android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.ECLAIR_MR1)
|
||||
{
|
||||
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(8)
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
||||
{
|
||||
@@ -329,4 +329,45 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
callback.invoke(origin, true, false);
|
||||
}
|
||||
|
||||
// API level 7 is required for this, see if we could lower this using something else
|
||||
@Override
|
||||
public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
|
||||
this.appView.showCustomView(view, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHideCustomView() {
|
||||
this.appView.hideCustomView();
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Ask the host application for a custom progress view to show while
|
||||
* a <video> is loading.
|
||||
* @return View The progress view.
|
||||
*/
|
||||
public View getVideoLoadingProgressView() {
|
||||
|
||||
if (mVideoProgressView == null) {
|
||||
// Create a new Loading view programmatically.
|
||||
|
||||
// create the linear layout
|
||||
LinearLayout layout = new LinearLayout(this.appView.getContext());
|
||||
layout.setOrientation(LinearLayout.VERTICAL);
|
||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
|
||||
layout.setLayoutParams(layoutParams);
|
||||
// the proress bar
|
||||
ProgressBar bar = new ProgressBar(this.appView.getContext());
|
||||
LinearLayout.LayoutParams barLayoutParams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
barLayoutParams.gravity = Gravity.CENTER;
|
||||
bar.setLayoutParams(barLayoutParams);
|
||||
layout.addView(bar);
|
||||
|
||||
mVideoProgressView = layout;
|
||||
}
|
||||
return mVideoProgressView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,8 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
|
||||
import android.location.Location;
|
||||
import android.location.LocationListener;
|
||||
@@ -39,8 +40,8 @@ public class CordovaLocationListener implements LocationListener {
|
||||
private GeoBroker owner;
|
||||
protected boolean running = false;
|
||||
|
||||
public HashMap<String, String> watches = new HashMap<String, String>();
|
||||
private List<String> callbacks = new ArrayList<String>();
|
||||
public HashMap<String, CallbackContext> watches = new HashMap<String, CallbackContext>();
|
||||
private List<CallbackContext> callbacks = new ArrayList<CallbackContext>();
|
||||
|
||||
private String TAG = "[Cordova Location Listener]";
|
||||
|
||||
@@ -51,30 +52,38 @@ public class CordovaLocationListener implements LocationListener {
|
||||
}
|
||||
|
||||
protected void fail(int code, String message) {
|
||||
for (String callbackId: this.callbacks)
|
||||
for (CallbackContext callbackContext: this.callbacks)
|
||||
{
|
||||
this.owner.fail(code, message, callbackId);
|
||||
this.owner.fail(code, message, callbackContext);
|
||||
}
|
||||
if(this.owner.isGlobalListener(this))
|
||||
{
|
||||
Log.d(TAG, "Stopping global listener");
|
||||
this.stop();
|
||||
}
|
||||
this.callbacks.clear();
|
||||
|
||||
Iterator it = this.watches.entrySet().iterator();
|
||||
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry pairs = (Map.Entry)it.next();
|
||||
this.owner.fail(code, message, (String)pairs.getValue());
|
||||
this.owner.fail(code, message, it.next());
|
||||
}
|
||||
}
|
||||
|
||||
private void win(Location loc) {
|
||||
for (String callbackId: this.callbacks)
|
||||
for (CallbackContext callbackContext: this.callbacks)
|
||||
{
|
||||
this.owner.win(loc, callbackId);
|
||||
this.owner.win(loc, callbackContext);
|
||||
}
|
||||
if(this.owner.isGlobalListener(this))
|
||||
{
|
||||
Log.d(TAG, "Stopping global listener");
|
||||
this.stop();
|
||||
}
|
||||
this.callbacks.clear();
|
||||
|
||||
Iterator it = this.watches.entrySet().iterator();
|
||||
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||
while (it.hasNext()) {
|
||||
Map.Entry pairs = (Map.Entry)it.next();
|
||||
this.owner.win(loc, (String)pairs.getValue());
|
||||
this.owner.win(loc, it.next());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,14 +149,14 @@ public class CordovaLocationListener implements LocationListener {
|
||||
return this.watches.size() + this.callbacks.size();
|
||||
}
|
||||
|
||||
public void addWatch(String timerId, String callbackId) {
|
||||
this.watches.put(timerId, callbackId);
|
||||
public void addWatch(String timerId, CallbackContext callbackContext) {
|
||||
this.watches.put(timerId, callbackContext);
|
||||
if (this.size() == 1) {
|
||||
this.start();
|
||||
}
|
||||
}
|
||||
public void addCallback(String callbackId) {
|
||||
this.callbacks.add(callbackId);
|
||||
public void addCallback(CallbackContext callbackContext) {
|
||||
this.callbacks.add(callbackContext);
|
||||
if (this.size() == 1) {
|
||||
this.start();
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
@@ -30,20 +32,34 @@ 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.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
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;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.WebBackForwardList;
|
||||
import android.webkit.WebHistoryItem;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
public class CordovaWebView extends WebView {
|
||||
|
||||
@@ -56,31 +72,46 @@ public class CordovaWebView extends WebView {
|
||||
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
|
||||
|
||||
public PluginManager pluginManager;
|
||||
public CallbackServer callbackServer;
|
||||
private boolean paused;
|
||||
|
||||
private BroadcastReceiver receiver;
|
||||
|
||||
|
||||
/** Actvities and other important classes **/
|
||||
/** Activities and other important classes **/
|
||||
private CordovaInterface cordova;
|
||||
CordovaWebViewClient viewClient;
|
||||
@SuppressWarnings("unused")
|
||||
private CordovaChromeClient chromeClient;
|
||||
|
||||
//This is for the polyfil history
|
||||
//This is for the polyfill history
|
||||
private String url;
|
||||
String baseUrl;
|
||||
private Stack<String> urls = new Stack<String>();
|
||||
|
||||
boolean useBrowserHistory = false;
|
||||
boolean useBrowserHistory = true;
|
||||
|
||||
// Flag to track that a loadUrl timeout occurred
|
||||
int loadUrlTimeout = 0;
|
||||
|
||||
private boolean bound;
|
||||
|
||||
private boolean volumedownBound;
|
||||
private boolean handleButton = false;
|
||||
|
||||
private long lastMenuEventTime = 0;
|
||||
|
||||
private boolean volumeupBound;
|
||||
NativeToJsMessageQueue jsMessageQueue;
|
||||
ExposedJsApi exposedJsApi;
|
||||
|
||||
/** custom view created by the browser (a video player for example) */
|
||||
private View mCustomView;
|
||||
private WebChromeClient.CustomViewCallback mCustomViewCallback;
|
||||
|
||||
static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
|
||||
new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
Gravity.CENTER);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -117,7 +148,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 +172,6 @@ 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.loadConfiguration();
|
||||
this.setup();
|
||||
}
|
||||
@@ -154,6 +184,7 @@ public class CordovaWebView extends WebView {
|
||||
* @param defStyle
|
||||
* @param 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 +196,29 @@ 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() {
|
||||
|
||||
this.setInitialScale(0);
|
||||
this.setVerticalScrollBarEnabled(false);
|
||||
this.requestFocusFromTouch();
|
||||
@@ -185,10 +228,28 @@ public class CordovaWebView extends WebView {
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
|
||||
|
||||
// Set the nav dump for HTC 2.x devices (disabling for ICS, deprecated entirely for Jellybean 4.2)
|
||||
try {
|
||||
Method gingerbread_getMethod = WebSettings.class.getMethod("setNavDump", new Class[] { boolean.class });
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
gingerbread_getMethod.invoke(settings, true);
|
||||
}
|
||||
} catch (NoSuchMethodException e) {
|
||||
Log.d(TAG, "We are on a modern version of Android, we will deprecate HTC 2.3 devices in 2.8");
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "Doing the NavDump failed with bad arguments");
|
||||
} catch (IllegalAccessException e) {
|
||||
Log.d(TAG, "This should never happen: IllegalAccessException means this isn't Android anymore");
|
||||
} catch (InvocationTargetException e) {
|
||||
Log.d(TAG, "This should never happen: InvocationTargetException means this isn't Android anymore.");
|
||||
}
|
||||
|
||||
//Set the nav dump for HTC
|
||||
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();
|
||||
@@ -199,14 +260,49 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
// Enable built-in geolocation
|
||||
settings.setGeolocationEnabled(true);
|
||||
|
||||
//Start up the plugin manager
|
||||
try {
|
||||
this.pluginManager = new PluginManager(this, this.cordova);
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
|
||||
// Fix for CB-1405
|
||||
// Google issue 4641
|
||||
this.updateUserAgentString();
|
||||
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
|
||||
if (this.receiver == null) {
|
||||
this.receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateUserAgentString();
|
||||
}
|
||||
};
|
||||
this.cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
}
|
||||
// end CB-1405
|
||||
|
||||
pluginManager = new PluginManager(this, this.cordova);
|
||||
jsMessageQueue = new NativeToJsMessageQueue(this, cordova);
|
||||
exposedJsApi = new ExposedJsApi(pluginManager, jsMessageQueue);
|
||||
exposeJsInterface();
|
||||
}
|
||||
|
||||
private void updateUserAgentString() {
|
||||
this.getSettings().getUserAgentString();
|
||||
}
|
||||
|
||||
private void exposeJsInterface() {
|
||||
int SDK_INT = Build.VERSION.SDK_INT;
|
||||
boolean isHoneycomb = (SDK_INT >= Build.VERSION_CODES.HONEYCOMB && SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2);
|
||||
if (isHoneycomb || (SDK_INT < Build.VERSION_CODES.GINGERBREAD)) {
|
||||
Log.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
|
||||
// Bug being that Java Strings do not get converted to JS strings automatically.
|
||||
// This isn't hard to work-around on the JS side, but it's easier to just
|
||||
// use the prompt bridge instead.
|
||||
return;
|
||||
} else if (SDK_INT < Build.VERSION_CODES.HONEYCOMB && Build.MANUFACTURER.equals("unknown")) {
|
||||
// addJavascriptInterface crashes on the 2.3 emulator.
|
||||
Log.i(TAG, "Disabled addJavascriptInterface() bridge callback due to a bug on the 2.3 emulator");
|
||||
return;
|
||||
}
|
||||
this.addJavascriptInterface(exposedJsApi, "_cordovaNative");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -414,9 +510,13 @@ public class CordovaWebView extends WebView {
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
private void loadUrlNow(String url) {
|
||||
LOG.d(TAG, ">>> loadUrlNow()");
|
||||
super.loadUrl(url);
|
||||
void loadUrlNow(String url) {
|
||||
if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) {
|
||||
LOG.d(TAG, ">>> loadUrlNow()");
|
||||
}
|
||||
if (url.startsWith("file://") || url.indexOf(this.baseUrl) == 0 || url.startsWith("javascript:") || this.isUrlWhiteListed(url)) {
|
||||
super.loadUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -445,7 +545,7 @@ public class CordovaWebView extends WebView {
|
||||
// Load url
|
||||
this.loadUrlIntoView(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
@@ -453,9 +553,17 @@ public class CordovaWebView extends WebView {
|
||||
* @param message
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.sendJavascript(statement);
|
||||
}
|
||||
this.jsMessageQueue.addJavaScript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a plugin result back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public void sendPluginResult(PluginResult result, String callbackId) {
|
||||
this.jsMessageQueue.addPluginResult(result, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -500,12 +608,14 @@ public class CordovaWebView extends WebView {
|
||||
// Check webview first to see if there is a history
|
||||
// This is needed to support curPage#diffLink, since they are added to appView's history, but not our history url array (JQMobile behavior)
|
||||
if (super.canGoBack()) {
|
||||
printBackForwardList();
|
||||
super.goBack();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// If our managed history has prev url
|
||||
if (this.urls.size() > 1) {
|
||||
if (this.urls.size() > 1 && !this.useBrowserHistory) {
|
||||
this.urls.pop(); // Pop current url
|
||||
String url = this.urls.pop(); // Pop prev url that we want to load, since it will be added back by loadUrl()
|
||||
this.loadUrl(url);
|
||||
@@ -596,6 +706,12 @@ public class CordovaWebView extends WebView {
|
||||
* <log level="DEBUG" />
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
Activity action = this.cordova.getActivity();
|
||||
if(action == null)
|
||||
{
|
||||
LOG.i("CordovaLog", "There is no activity. Is this on the lock screen?");
|
||||
return;
|
||||
}
|
||||
int id = getResources().getIdentifier("config", "xml", this.cordova.getActivity().getPackageName());
|
||||
if(id == 0)
|
||||
{
|
||||
@@ -645,14 +761,13 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
// Init preferences
|
||||
if ("true".equals(this.getProperty("useBrowserHistory", "false"))) {
|
||||
this.useBrowserHistory = true;
|
||||
}
|
||||
else {
|
||||
if("false".equals(this.getProperty("useBrowserHistory", "true")))
|
||||
{
|
||||
//Switch back to the old browser history and state the six month policy
|
||||
this.useBrowserHistory = false;
|
||||
Log.w(TAG, "useBrowserHistory=false is deprecated as of Cordova 2.2.0 and will be removed six months after the 2.2.0 release. Please use the browser history and use history.back().");
|
||||
}
|
||||
|
||||
|
||||
if ("true".equals(this.getProperty("fullscreen", "false"))) {
|
||||
this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
this.cordova.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
@@ -687,7 +802,7 @@ public class CordovaWebView extends WebView {
|
||||
if(keyDownCodes.contains(keyCode))
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
|
||||
// only override default behaviour is event bound
|
||||
// only override default behavior is event bound
|
||||
LOG.d(TAG, "Down Key Hit");
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('volumedownbutton');");
|
||||
return true;
|
||||
@@ -703,6 +818,17 @@ public class CordovaWebView extends WebView {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
else if(keyCode == KeyEvent.KEYCODE_BACK)
|
||||
{
|
||||
//Because exit is fired on the keyDown and not the key up on Android 4.x
|
||||
//we need to check for this.
|
||||
//Also, I really wished "canGoBack" worked!
|
||||
if(this.useBrowserHistory)
|
||||
return !(this.startOfHistory()) || this.bound;
|
||||
else
|
||||
return this.urls.size() > 1 || this.bound;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@@ -710,31 +836,37 @@ 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
|
||||
if (this.bound) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
|
||||
return true;
|
||||
// A custom view is currently displayed (e.g. playing a video)
|
||||
if(mCustomView != null) {
|
||||
this.hideCustomView();
|
||||
} else {
|
||||
// If not bound
|
||||
// Go to previous page in webview if it is possible to go back
|
||||
if (this.backHistory()) {
|
||||
// The webview is currently displayed
|
||||
// If back key is bound, then send event to JavaScript
|
||||
if (this.bound) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('backbutton');");
|
||||
return true;
|
||||
}
|
||||
// If not, then invoke default behaviour
|
||||
else {
|
||||
//this.activityState = ACTIVITY_EXITING;
|
||||
return false;
|
||||
} else {
|
||||
// If not bound
|
||||
// Go to previous page in webview if it is possible to go back
|
||||
if (this.backHistory()) {
|
||||
return true;
|
||||
}
|
||||
// If not, then invoke default behaviour
|
||||
else {
|
||||
//this.activityState = ACTIVITY_EXITING;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Legacy
|
||||
else if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');");
|
||||
if (this.lastMenuEventTime < event.getEventTime()) {
|
||||
this.loadUrl("javascript:cordova.fireDocumentEvent('menubutton');");
|
||||
}
|
||||
this.lastMenuEventTime = event.getEventTime();
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
// If search key
|
||||
@@ -748,9 +880,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 behavior?
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
@@ -784,4 +915,166 @@ 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();
|
||||
}
|
||||
|
||||
// unregister the receiver
|
||||
if (this.receiver != null) {
|
||||
try {
|
||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error unregistering configuration receiver: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void printBackForwardList() {
|
||||
WebBackForwardList currentList = this.copyBackForwardList();
|
||||
int currentSize = currentList.getSize();
|
||||
for(int i = 0; i < currentSize; ++i)
|
||||
{
|
||||
WebHistoryItem item = currentList.getItemAtIndex(i);
|
||||
String url = item.getUrl();
|
||||
LOG.d(TAG, "The URL at index: " + Integer.toString(i) + "is " + url );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Can Go Back is BROKEN!
|
||||
public boolean startOfHistory()
|
||||
{
|
||||
WebBackForwardList currentList = this.copyBackForwardList();
|
||||
WebHistoryItem item = currentList.getItemAtIndex(0);
|
||||
String url = item.getUrl();
|
||||
String currentUrl = this.getUrl();
|
||||
LOG.d(TAG, "The current URL is: " + currentUrl);
|
||||
LOG.d(TAG, "The URL at item 0 is:" + url);
|
||||
return currentUrl.equals(url);
|
||||
}
|
||||
|
||||
public void showCustomView(View view, WebChromeClient.CustomViewCallback callback) {
|
||||
// This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0
|
||||
Log.d(TAG, "showing Custom View");
|
||||
// if a view already exists then immediately terminate the new one
|
||||
if (mCustomView != null) {
|
||||
callback.onCustomViewHidden();
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the view and its callback for later (to kill it properly)
|
||||
mCustomView = view;
|
||||
mCustomViewCallback = callback;
|
||||
|
||||
// Add the custom view to its container.
|
||||
ViewGroup parent = (ViewGroup) this.getParent();
|
||||
parent.addView(view, COVER_SCREEN_GRAVITY_CENTER);
|
||||
|
||||
// Hide the content view.
|
||||
this.setVisibility(View.GONE);
|
||||
|
||||
// Finally show the custom view container.
|
||||
parent.setVisibility(View.VISIBLE);
|
||||
parent.bringToFront();
|
||||
}
|
||||
|
||||
public void hideCustomView() {
|
||||
// This code is adapted from the original Android Browser code, licensed under the Apache License, Version 2.0
|
||||
Log.d(TAG, "Hidding Custom View");
|
||||
if (mCustomView == null) return;
|
||||
|
||||
// Hide the custom view.
|
||||
mCustomView.setVisibility(View.GONE);
|
||||
|
||||
// Remove the custom view from its container.
|
||||
ViewGroup parent = (ViewGroup) this.getParent();
|
||||
parent.removeView(mCustomView);
|
||||
mCustomView = null;
|
||||
mCustomViewCallback.onCustomViewHidden();
|
||||
|
||||
// Show the content view.
|
||||
this.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* if the video overlay is showing then we need to know
|
||||
* as it effects back button handling
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isCustomViewShowing() {
|
||||
return mCustomView != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,26 +21,24 @@ package org.apache.cordova;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Context;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
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;
|
||||
import android.webkit.WebResourceResponse;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
@@ -49,7 +47,8 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String TAG = "Cordova";
|
||||
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||
CordovaInterface cordova;
|
||||
CordovaWebView appView;
|
||||
private boolean doClearHistory = false;
|
||||
@@ -86,6 +85,25 @@ 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);
|
||||
appView.pluginManager.exec(service, action, callbackId, jsonArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +112,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 (NativeToJsMessageQueue.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 +193,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,17 +252,16 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
this.doClearHistory = true;
|
||||
}
|
||||
|
||||
// Create callback server and plugin manager
|
||||
if (this.appView.callbackServer == null) {
|
||||
this.appView.callbackServer = new CallbackServer();
|
||||
this.appView.callbackServer.init(url);
|
||||
}
|
||||
else {
|
||||
this.appView.callbackServer.reinit(url);
|
||||
}
|
||||
// Flush stale messages.
|
||||
this.appView.jsMessageQueue.reset();
|
||||
|
||||
// Broadcast message that page has loaded
|
||||
this.appView.postMessage("onPageStarted", url);
|
||||
|
||||
// Notify all plugins of the navigation, so they can clean up if necessary.
|
||||
if (this.appView.pluginManager != null) {
|
||||
this.appView.pluginManager.onReset();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,9 +322,6 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
// Shutdown if blank loaded
|
||||
if (url.equals("about:blank")) {
|
||||
if (this.appView.callbackServer != null) {
|
||||
this.appView.callbackServer.destroy();
|
||||
}
|
||||
appView.postMessage("exit", null);
|
||||
}
|
||||
}
|
||||
@@ -346,6 +364,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 +478,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,10 @@ package org.apache.cordova;
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -35,10 +36,10 @@ import android.content.IntentFilter;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends Plugin {
|
||||
public class Device extends CordovaPlugin {
|
||||
public static final String TAG = "Device";
|
||||
|
||||
public static String cordovaVersion = "2.0.0rc1"; // Cordova version
|
||||
public static String cordovaVersion = "2.3.0"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
@@ -55,9 +56,10 @@ public class Device extends Plugin {
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void setContext(CordovaInterface cordova) {
|
||||
super.setContext(cordova);
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
Device.uuid = getUuid();
|
||||
this.initTelephonyReceiver();
|
||||
}
|
||||
@@ -65,45 +67,26 @@ public class Device 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.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false if not.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (action.equals("getDeviceInfo")) {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("uuid", Device.uuid);
|
||||
r.put("version", this.getOSVersion());
|
||||
r.put("platform", Device.platform);
|
||||
r.put("name", this.getProductName());
|
||||
r.put("cordova", Device.cordovaVersion);
|
||||
//JSONObject pg = new JSONObject();
|
||||
//pg.put("version", Device.CordovaVersion);
|
||||
//r.put("cordova", pg);
|
||||
return new PluginResult(status, r);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} 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) {
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("getDeviceInfo")) {
|
||||
return true;
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("uuid", Device.uuid);
|
||||
r.put("version", this.getOSVersion());
|
||||
r.put("platform", Device.platform);
|
||||
r.put("name", this.getProductName());
|
||||
r.put("cordova", Device.cordovaVersion);
|
||||
r.put("model", this.getModel());
|
||||
callbackContext.success(r);
|
||||
}
|
||||
return false;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,10 +19,12 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.cordova.api.IPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@@ -46,6 +48,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;
|
||||
|
||||
@@ -140,6 +143,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
protected LinearLayout root;
|
||||
protected boolean cancelLoadUrl = false;
|
||||
protected ProgressDialog spinnerDialog = null;
|
||||
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||
|
||||
|
||||
// The initial URL for our app
|
||||
// ie http://server/path/index.html#abc?query
|
||||
@@ -150,13 +155,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
private static int ACTIVITY_EXITING = 2;
|
||||
private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down
|
||||
|
||||
// The base of the initial URL for our app.
|
||||
// Does not include file name. Ends with /
|
||||
// ie http://server/path/
|
||||
String baseUrl = null;
|
||||
|
||||
// Plugin to call when activity result is received
|
||||
protected IPlugin activityResultCallback = null;
|
||||
protected CordovaPlugin activityResultCallback = null;
|
||||
protected boolean activityResultKeepRunning;
|
||||
|
||||
// Default background color for activity
|
||||
@@ -295,7 +295,16 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
*/
|
||||
public void init() {
|
||||
CordovaWebView webView = new CordovaWebView(DroidGap.this);
|
||||
this.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
|
||||
CordovaWebViewClient webViewClient;
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
{
|
||||
webViewClient = new CordovaWebViewClient(this, webView);
|
||||
}
|
||||
else
|
||||
{
|
||||
webViewClient = new IceCreamCordovaWebViewClient(this, webView);
|
||||
}
|
||||
this.init(webView, webViewClient, new CordovaChromeClient(this, webView));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -404,6 +413,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
}
|
||||
|
||||
this.splashscreenTime = time;
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
this.appView.loadUrl(url, time);
|
||||
}
|
||||
|
||||
@@ -600,6 +611,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 +621,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 +636,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 +648,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 +658,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 +668,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 +683,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 +724,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.addJavaScript(statement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,10 +750,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;
|
||||
}
|
||||
@@ -793,7 +776,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
* @param intent The intent to start
|
||||
* @param requestCode The request code that is passed to callback to identify the activity
|
||||
*/
|
||||
public void startActivityForResult(IPlugin command, Intent intent, int requestCode) {
|
||||
public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode) {
|
||||
this.activityResultCallback = command;
|
||||
this.activityResultKeepRunning = this.keepRunning;
|
||||
|
||||
@@ -818,13 +801,13 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
*/
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
IPlugin callback = this.activityResultCallback;
|
||||
CordovaPlugin callback = this.activityResultCallback;
|
||||
if (callback != null) {
|
||||
callback.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
}
|
||||
|
||||
public void setActivityResultCallback(IPlugin plugin) {
|
||||
public void setActivityResultCallback(CordovaPlugin plugin) {
|
||||
this.activityResultCallback = plugin;
|
||||
}
|
||||
|
||||
@@ -839,16 +822,15 @@ 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))) {
|
||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||
|
||||
// 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);
|
||||
}
|
||||
});
|
||||
@@ -930,7 +912,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
this.postMessage("onPrepareOptionsMenu", menu);
|
||||
return super.onPrepareOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1023,6 +1005,39 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
this.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
if ((appView.isCustomViewShowing() || childView != null ) && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
} else {
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Android 2.x needs to be able to check where the cursor is. Android 4.x does not
|
||||
*
|
||||
* (non-Javadoc)
|
||||
* @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Get whatever has focus!
|
||||
View childView = appView.getFocusedChild();
|
||||
//Determine if the focus is on the current view or not
|
||||
if (childView != null && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return appView.onKeyDown(keyCode, event);
|
||||
}
|
||||
else
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
*
|
||||
@@ -1037,8 +1052,11 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
this.removeSplashScreen();
|
||||
}
|
||||
else {
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
// If the splash dialog is showing don't try to show it again
|
||||
if (this.splashDialog != null && !this.splashDialog.isShowing()) {
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
this.showSplashScreen(this.splashscreenTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ("spinner".equals(id)) {
|
||||
@@ -1061,4 +1079,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
return null;
|
||||
}
|
||||
|
||||
public ExecutorService getThreadPool() {
|
||||
return threadPool;
|
||||
}
|
||||
}
|
||||
|
||||
49
framework/src/com/phonegap/api/PluginResult.java → framework/src/org/apache/cordova/Echo.java
Executable file → Normal file
@@ -16,38 +16,29 @@
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class PluginResult extends org.apache.cordova.api.PluginResult {
|
||||
public class Echo extends CordovaPlugin {
|
||||
|
||||
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);
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final String result = args.isNull(0) ? null : args.getString(0);
|
||||
if ("echo".equals(action)) {
|
||||
callbackContext.success(result);
|
||||
return true;
|
||||
} else if ("echoAsync".equals(action)) {
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
callbackContext.success(result);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ import java.io.IOException;
|
||||
import android.media.ExifInterface;
|
||||
|
||||
public class ExifHelper {
|
||||
private String aperature = null;
|
||||
private String aperture = null;
|
||||
private String datetime = null;
|
||||
private String exposureTime = null;
|
||||
private String flash = null;
|
||||
@@ -70,7 +70,7 @@ public class ExifHelper {
|
||||
* Reads all the EXIF data from the input file.
|
||||
*/
|
||||
public void readExifData() {
|
||||
this.aperature = inFile.getAttribute(ExifInterface.TAG_APERTURE);
|
||||
this.aperture = inFile.getAttribute(ExifInterface.TAG_APERTURE);
|
||||
this.datetime = inFile.getAttribute(ExifInterface.TAG_DATETIME);
|
||||
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
|
||||
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
|
||||
@@ -102,8 +102,8 @@ public class ExifHelper {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.aperature != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperature);
|
||||
if (this.aperture != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperture);
|
||||
}
|
||||
if (this.datetime != null) {
|
||||
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime);
|
||||
|
||||
65
framework/src/org/apache/cordova/ExposedJsApi.java
Executable file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import android.webkit.JavascriptInterface;
|
||||
import org.apache.cordova.api.PluginManager;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONException;
|
||||
|
||||
/**
|
||||
* Contains APIs that the JS can call. All functions in here should also have
|
||||
* an equivalent entry in CordovaChromeClient.java, and be added to
|
||||
* cordova-js/lib/android/plugin/android/promptbasednativeapi.js
|
||||
*/
|
||||
/* package */ class ExposedJsApi {
|
||||
|
||||
private PluginManager pluginManager;
|
||||
private NativeToJsMessageQueue jsMessageQueue;
|
||||
|
||||
public ExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
||||
this.pluginManager = pluginManager;
|
||||
this.jsMessageQueue = jsMessageQueue;
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
||||
jsMessageQueue.setPaused(true);
|
||||
try {
|
||||
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
|
||||
String ret = "";
|
||||
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
|
||||
ret = jsMessageQueue.popAndEncode();
|
||||
}
|
||||
return ret;
|
||||
} finally {
|
||||
jsMessageQueue.setPaused(false);
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public void setNativeToJsBridgeMode(int value) {
|
||||
jsMessageQueue.setBridgeMode(value);
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
public String retrieveJsMessages() {
|
||||
return jsMessageQueue.popAndEncode();
|
||||
}
|
||||
}
|
||||
63
framework/src/org/apache/cordova/FileProgressResult.java
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Encapsulates in-progress status of uploading or downloading a file to a remote server.
|
||||
*/
|
||||
public class FileProgressResult {
|
||||
|
||||
private boolean lengthComputable = false; // declares whether total is known
|
||||
private long loaded = 0; // bytes sent so far
|
||||
private long total = 0; // bytes total, if known
|
||||
|
||||
public boolean getLengthComputable() {
|
||||
return lengthComputable;
|
||||
}
|
||||
|
||||
public void setLengthComputable(boolean computable) {
|
||||
this.lengthComputable = computable;
|
||||
}
|
||||
|
||||
public long getLoaded() {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public void setLoaded(long bytes) {
|
||||
this.loaded = bytes;
|
||||
}
|
||||
|
||||
public long getTotal() {
|
||||
return total;
|
||||
}
|
||||
|
||||
public void setTotal(long bytes) {
|
||||
this.total = bytes;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{loaded:" + loaded +
|
||||
",total:" + total +
|
||||
",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ public class FileUploadResult {
|
||||
private long bytesSent = 0; // bytes sent
|
||||
private int responseCode = -1; // HTTP response code
|
||||
private String response = null; // HTTP response
|
||||
private String objectId = null; // FileTransfer object id
|
||||
|
||||
public long getBytesSent() {
|
||||
return bytesSent;
|
||||
@@ -54,10 +55,19 @@ public class FileUploadResult {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public String getObjectId() {
|
||||
return objectId;
|
||||
}
|
||||
|
||||
public void setObjectId(String objectId) {
|
||||
this.objectId = objectId;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{bytesSent:" + bytesSent +
|
||||
",responseCode:" + responseCode +
|
||||
",response:" + JSONObject.quote(response) + "}");
|
||||
",response:" + JSONObject.quote(response) +
|
||||
",objectId:" + JSONObject.quote(objectId) + "}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ import java.net.URLDecoder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.file.EncodingException;
|
||||
import org.apache.cordova.file.FileExistsException;
|
||||
@@ -42,6 +43,7 @@ import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
//import android.app.Activity;
|
||||
@@ -50,7 +52,7 @@ import android.webkit.MimeTypeMap;
|
||||
* This class provides SD card file and directory services to JavaScript.
|
||||
* Only files on the SD card can be accessed.
|
||||
*/
|
||||
public class FileUtils extends Plugin {
|
||||
public class FileUtils extends CordovaPlugin {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOG_TAG = "FileUtils";
|
||||
private static final String _DATA = "_data"; // The column name where the file path is stored
|
||||
@@ -84,83 +86,78 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
* Executes the request and returns whether the action was valid.
|
||||
*
|
||||
* @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.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false otherwise.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
//System.out.println("FileUtils.execute("+action+")");
|
||||
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
try {
|
||||
if (action.equals("testSaveLocationExists")) {
|
||||
boolean b = DirectoryManager.testSaveLocationExists();
|
||||
return new PluginResult(status, b);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
|
||||
}
|
||||
else if (action.equals("getFreeDiskSpace")) {
|
||||
long l = DirectoryManager.getFreeDiskSpace(false);
|
||||
return new PluginResult(status, l);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l));
|
||||
}
|
||||
else if (action.equals("testFileExists")) {
|
||||
boolean b = DirectoryManager.testFileExists(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
|
||||
}
|
||||
else if (action.equals("testDirectoryExists")) {
|
||||
boolean b = DirectoryManager.testFileExists(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
|
||||
}
|
||||
else if (action.equals("readAsText")) {
|
||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, s);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, s));
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
String s = this.readAsDataURL(args.getString(0));
|
||||
return new PluginResult(status, s);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, s));
|
||||
}
|
||||
else if (action.equals("write")) {
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
|
||||
return new PluginResult(status, fileSize);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
|
||||
}
|
||||
else if (action.equals("truncate")) {
|
||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
||||
return new PluginResult(status, fileSize);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, fileSize));
|
||||
}
|
||||
else if (action.equals("requestFileSystem")) {
|
||||
long size = args.optLong(1);
|
||||
if (size != 0) {
|
||||
if (size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR);
|
||||
}
|
||||
if (size != 0 && size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) {
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR));
|
||||
} else {
|
||||
JSONObject obj = requestFileSystem(args.getInt(0));
|
||||
callbackContext.success(obj);
|
||||
}
|
||||
JSONObject obj = requestFileSystem(args.getInt(0));
|
||||
return new PluginResult(status, obj);
|
||||
}
|
||||
else if (action.equals("resolveLocalFileSystemURI")) {
|
||||
JSONObject obj = resolveLocalFileSystemURI(args.getString(0));
|
||||
return new PluginResult(status, obj);
|
||||
callbackContext.success(obj);
|
||||
}
|
||||
else if (action.equals("getMetadata")) {
|
||||
return new PluginResult(status, getMetadata(args.getString(0)));
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, getMetadata(args.getString(0))));
|
||||
}
|
||||
else if (action.equals("getFileMetadata")) {
|
||||
JSONObject obj = getFileMetadata(args.getString(0));
|
||||
return new PluginResult(status, obj);
|
||||
callbackContext.success(obj);
|
||||
}
|
||||
else if (action.equals("getParent")) {
|
||||
JSONObject obj = getParent(args.getString(0));
|
||||
return new PluginResult(status, obj);
|
||||
callbackContext.success(obj);
|
||||
}
|
||||
else if (action.equals("getDirectory")) {
|
||||
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true);
|
||||
return new PluginResult(status, obj);
|
||||
callbackContext.success(obj);
|
||||
}
|
||||
else if (action.equals("getFile")) {
|
||||
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false);
|
||||
return new PluginResult(status, obj);
|
||||
callbackContext.success(obj);
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
boolean success;
|
||||
@@ -169,51 +166,52 @@ public class FileUtils extends Plugin {
|
||||
|
||||
if (success) {
|
||||
notifyDelete(args.getString(0));
|
||||
return new PluginResult(status);
|
||||
callbackContext.success();
|
||||
} else {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("removeRecursively")) {
|
||||
boolean success = removeRecursively(args.getString(0));
|
||||
if (success) {
|
||||
return new PluginResult(status);
|
||||
callbackContext.success();
|
||||
} else {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("moveTo")) {
|
||||
JSONObject entry = transferTo(args.getString(0), args.getString(1), args.getString(2), true);
|
||||
return new PluginResult(status, entry);
|
||||
callbackContext.success(entry);
|
||||
}
|
||||
else if (action.equals("copyTo")) {
|
||||
JSONObject entry = transferTo(args.getString(0), args.getString(1), args.getString(2), false);
|
||||
return new PluginResult(status, entry);
|
||||
callbackContext.success(entry);
|
||||
}
|
||||
else if (action.equals("readEntries")) {
|
||||
JSONArray entries = readEntries(args.getString(0));
|
||||
return new PluginResult(status, entries);
|
||||
callbackContext.success(entries);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (FileNotFoundException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
|
||||
callbackContext.error(FileUtils.NOT_FOUND_ERR);
|
||||
} catch (FileExistsException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.PATH_EXISTS_ERR);
|
||||
callbackContext.error(FileUtils.PATH_EXISTS_ERR);
|
||||
} catch (NoModificationAllowedException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
} catch (InvalidModificationException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR);
|
||||
callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR);
|
||||
} catch (MalformedURLException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR);
|
||||
callbackContext.error(FileUtils.ENCODING_ERR);
|
||||
} catch (IOException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR);
|
||||
callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR);
|
||||
} catch (EncodingException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR);
|
||||
callbackContext.error(FileUtils.ENCODING_ERR);
|
||||
} catch (TypeMismatchException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.TYPE_MISMATCH_ERR);
|
||||
callbackContext.error(FileUtils.TYPE_MISMATCH_ERR);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -223,7 +221,7 @@ public class FileUtils extends Plugin {
|
||||
*/
|
||||
private void notifyDelete(String filePath) {
|
||||
String newFilePath = stripFileProtocol(filePath);
|
||||
int result = this.cordova.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
this.cordova.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
MediaStore.Images.Media.DATA + " = ?",
|
||||
new String[] { newFilePath });
|
||||
}
|
||||
@@ -298,7 +296,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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,8 +318,9 @@ public class FileUtils extends Plugin {
|
||||
* @throws InvalidModificationException
|
||||
* @throws EncodingException
|
||||
* @throws JSONException
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
private JSONObject transferTo(String fileName, String newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException {
|
||||
private JSONObject transferTo(String fileName, String newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException {
|
||||
fileName = stripFileProtocol(fileName);
|
||||
newParent = stripFileProtocol(newParent);
|
||||
|
||||
@@ -407,23 +408,31 @@ public class FileUtils extends Plugin {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
}
|
||||
|
||||
FileChannel input = new FileInputStream(srcFile).getChannel();
|
||||
FileChannel output = new FileOutputStream(destFile).getChannel();
|
||||
|
||||
input.transferTo(0, input.size(), output);
|
||||
|
||||
input.close();
|
||||
output.close();
|
||||
|
||||
/*
|
||||
if (srcFile.length() != destFile.length()) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
copyAction(srcFile, destFile);
|
||||
|
||||
return getEntry(destFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moved this code into it's own method so moveTo could use it when the move is across file systems
|
||||
*/
|
||||
private void copyAction(File srcFile, File destFile)
|
||||
throws FileNotFoundException, IOException {
|
||||
FileInputStream istream = new FileInputStream(srcFile);
|
||||
FileOutputStream ostream = new FileOutputStream(destFile);
|
||||
FileChannel input = istream.getChannel();
|
||||
FileChannel output = ostream.getChannel();
|
||||
|
||||
try {
|
||||
input.transferTo(0, input.size(), output);
|
||||
} finally {
|
||||
istream.close();
|
||||
ostream.close();
|
||||
input.close();
|
||||
output.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a directory
|
||||
*
|
||||
@@ -478,7 +487,7 @@ public class FileUtils extends Plugin {
|
||||
|
||||
// This weird test is to determine if we are copying or moving a directory into itself.
|
||||
// Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
|
||||
// Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
|
||||
// Copy /sdcard/myDir to /sdcard/myDir/backup should throw an INVALID_MODIFICATION_ERR
|
||||
if (dest.startsWith(src) && dest.indexOf(File.separator, src.length() - 1) != -1) {
|
||||
return true;
|
||||
}
|
||||
@@ -496,7 +505,7 @@ public class FileUtils extends Plugin {
|
||||
* @throws InvalidModificationException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject moveFile(File srcFile, File destFile) throws JSONException, InvalidModificationException {
|
||||
private JSONObject moveFile(File srcFile, File destFile) throws IOException, JSONException, InvalidModificationException {
|
||||
// Renaming a file to an existing directory should fail
|
||||
if (destFile.exists() && destFile.isDirectory()) {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
@@ -508,6 +517,12 @@ public class FileUtils extends Plugin {
|
||||
// Now we have to do things the hard way
|
||||
// 1) Copy all the old file
|
||||
// 2) delete the src file
|
||||
copyAction(srcFile, destFile);
|
||||
if (destFile.exists()) {
|
||||
srcFile.delete();
|
||||
} else {
|
||||
throw new IOException("moved failed");
|
||||
}
|
||||
}
|
||||
|
||||
return getEntry(destFile);
|
||||
@@ -522,8 +537,10 @@ public class FileUtils extends Plugin {
|
||||
* @throws JSONException
|
||||
* @throws IOException
|
||||
* @throws InvalidModificationException
|
||||
* @throws NoModificationAllowedException
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, InvalidModificationException {
|
||||
private JSONObject moveDirectory(File srcDir, File destinationDir) throws IOException, JSONException, InvalidModificationException, NoModificationAllowedException, FileExistsException {
|
||||
// Renaming a file to an existing directory should fail
|
||||
if (destinationDir.exists() && destinationDir.isFile()) {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
@@ -547,6 +564,12 @@ public class FileUtils extends Plugin {
|
||||
// Now we have to do things the hard way
|
||||
// 1) Copy all the old files
|
||||
// 2) delete the src directory
|
||||
copyDirectory(srcDir, destinationDir);
|
||||
if (destinationDir.exists()) {
|
||||
removeDirRecursively(srcDir);
|
||||
} else {
|
||||
throw new IOException("moved failed");
|
||||
}
|
||||
}
|
||||
|
||||
return getEntry(destinationDir);
|
||||
@@ -961,8 +984,16 @@ public class FileUtils extends Plugin {
|
||||
* @return a mime type
|
||||
*/
|
||||
public static String getMimeType(String filename) {
|
||||
// Stupid bug in getFileExtensionFromUrl when the file name has a space
|
||||
// So we need to replace the space with a url encoded %20
|
||||
String url = filename.replace(" ", "%20");
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
return map.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(filename));
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
|
||||
if (extension.toLowerCase().equals("3ga")) {
|
||||
return "audio/3gpp";
|
||||
} else {
|
||||
return map.getMimeTypeFromExtension(extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1006,14 +1037,17 @@ public class FileUtils extends Plugin {
|
||||
filename = stripFileProtocol(filename);
|
||||
|
||||
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
|
||||
|
||||
if (raf.length() >= size) {
|
||||
FileChannel channel = raf.getChannel();
|
||||
channel.truncate(size);
|
||||
return size;
|
||||
try {
|
||||
if (raf.length() >= size) {
|
||||
FileChannel channel = raf.getChannel();
|
||||
channel.truncate(size);
|
||||
return size;
|
||||
}
|
||||
|
||||
return raf.length();
|
||||
} finally {
|
||||
raf.close();
|
||||
}
|
||||
|
||||
return raf.length();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1038,15 +1072,23 @@ public class FileUtils extends Plugin {
|
||||
* Queries the media store to find out what the file path is for the Uri we supply
|
||||
*
|
||||
* @param contentUri the Uri of the audio/image/video
|
||||
* @param cordova) the current applicaiton context
|
||||
* @param cordova the current application context
|
||||
* @return the full path to the file
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
final String scheme = contentUri.getScheme();
|
||||
|
||||
if (scheme.compareTo("content") == 0) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
} else if (scheme.compareTo("file") == 0) {
|
||||
return contentUri.getPath();
|
||||
} else {
|
||||
return contentUri.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -34,7 +35,7 @@ import android.location.LocationManager;
|
||||
* This class only starts and stops various GeoListeners, which consist of a GPS and a Network Listener
|
||||
*/
|
||||
|
||||
public class GeoBroker extends Plugin {
|
||||
public class GeoBroker extends CordovaPlugin {
|
||||
private GPSListener gpsListener;
|
||||
private NetworkListener networkListener;
|
||||
private LocationManager locationManager;
|
||||
@@ -49,46 +50,51 @@ public class GeoBroker 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.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, or false if not.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (this.locationManager == null) {
|
||||
this.locationManager = (LocationManager) this.cordova.getActivity().getSystemService(Context.LOCATION_SERVICE);
|
||||
this.networkListener = new NetworkListener(this.locationManager, this);
|
||||
this.gpsListener = new GPSListener(this.locationManager, this);
|
||||
}
|
||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
||||
String message = "";
|
||||
PluginResult result = new PluginResult(status, message);
|
||||
result.setKeepCallback(true);
|
||||
|
||||
try {
|
||||
if ( locationManager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ||
|
||||
locationManager.isProviderEnabled( LocationManager.NETWORK_PROVIDER )) {
|
||||
|
||||
if (action.equals("getLocation")) {
|
||||
boolean enableHighAccuracy = args.getBoolean(0);
|
||||
int maximumAge = args.getInt(1);
|
||||
Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER));
|
||||
// Check if we can use lastKnownLocation to get a quick reading and use less battery
|
||||
if ((System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
||||
result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||
if (last != null && (System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||
callbackContext.sendPluginResult(result);
|
||||
} else {
|
||||
this.getCurrentLocation(callbackId, enableHighAccuracy);
|
||||
this.getCurrentLocation(callbackContext, enableHighAccuracy);
|
||||
}
|
||||
}
|
||||
else if (action.equals("addWatch")) {
|
||||
String id = args.getString(0);
|
||||
boolean enableHighAccuracy = args.getBoolean(1);
|
||||
this.addWatch(id, callbackId, enableHighAccuracy);
|
||||
this.addWatch(id, callbackContext, 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());
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
||||
String message = "Location API is not available for this device.";
|
||||
PluginResult result = new PluginResult(status, message);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void clearWatch(String id) {
|
||||
@@ -96,42 +102,43 @@ public class GeoBroker extends Plugin {
|
||||
this.networkListener.clearWatch(id);
|
||||
}
|
||||
|
||||
private void getCurrentLocation(String callbackId, boolean enableHighAccuracy) {
|
||||
private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy) {
|
||||
if (enableHighAccuracy) {
|
||||
this.gpsListener.addCallback(callbackId);
|
||||
this.gpsListener.addCallback(callbackContext);
|
||||
} else {
|
||||
this.networkListener.addCallback(callbackId);
|
||||
this.networkListener.addCallback(callbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
private void addWatch(String timerId, String callbackId, boolean enableHighAccuracy) {
|
||||
private void addWatch(String timerId, CallbackContext callbackContext, boolean enableHighAccuracy) {
|
||||
if (enableHighAccuracy) {
|
||||
this.gpsListener.addWatch(timerId, callbackId);
|
||||
this.gpsListener.addWatch(timerId, callbackContext);
|
||||
} else {
|
||||
this.networkListener.addWatch(timerId, callbackId);
|
||||
this.networkListener.addWatch(timerId, callbackContext);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// Starting listeners is easier to run on main thread, so don't run async.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.networkListener.destroy();
|
||||
this.gpsListener.destroy();
|
||||
this.networkListener = null;
|
||||
this.gpsListener = null;
|
||||
if (this.networkListener != null) {
|
||||
this.networkListener.destroy();
|
||||
this.networkListener = null;
|
||||
}
|
||||
if (this.gpsListener != null) {
|
||||
this.gpsListener.destroy();
|
||||
this.gpsListener = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the view navigates.
|
||||
* Stop the listeners.
|
||||
*/
|
||||
public void onReset() {
|
||||
this.onDestroy();
|
||||
}
|
||||
|
||||
public JSONObject returnLocationJSON(Location loc) {
|
||||
@@ -153,9 +160,9 @@ public class GeoBroker extends Plugin {
|
||||
return o;
|
||||
}
|
||||
|
||||
public void win(Location loc, String callbackId) {
|
||||
public void win(Location loc, CallbackContext callbackContext) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(loc));
|
||||
this.success(result, callbackId);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,7 +172,7 @@ public class GeoBroker extends Plugin {
|
||||
* @param msg The error message
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void fail(int code, String msg, String callbackId) {
|
||||
public void fail(int code, String msg, CallbackContext callbackContext) {
|
||||
JSONObject obj = new JSONObject();
|
||||
String backup = null;
|
||||
try {
|
||||
@@ -182,6 +189,16 @@ public class GeoBroker extends Plugin {
|
||||
result = new PluginResult(PluginResult.Status.ERROR, backup);
|
||||
}
|
||||
|
||||
this.error(result, callbackId);
|
||||
callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
public boolean isGlobalListener(CordovaLocationListener listener)
|
||||
{
|
||||
if (gpsListener != null && networkListener != null)
|
||||
{
|
||||
return gpsListener.equals(listener) || networkListener.equals(listener);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
577
framework/src/org/apache/cordova/Globalization.java
Normal file
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.text.DateFormat;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Currency;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.text.format.Time;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class Globalization extends CordovaPlugin {
|
||||
//GlobalizationCommand Plugin Actions
|
||||
public static final String GETLOCALENAME = "getLocaleName";
|
||||
public static final String DATETOSTRING = "dateToString";
|
||||
public static final String STRINGTODATE = "stringToDate";
|
||||
public static final String GETDATEPATTERN = "getDatePattern";
|
||||
public static final String GETDATENAMES = "getDateNames";
|
||||
public static final String ISDAYLIGHTSAVINGSTIME = "isDayLightSavingsTime";
|
||||
public static final String GETFIRSTDAYOFWEEK = "getFirstDayOfWeek";
|
||||
public static final String NUMBERTOSTRING = "numberToString";
|
||||
public static final String STRINGTONUMBER = "stringToNumber";
|
||||
public static final String GETNUMBERPATTERN = "getNumberPattern";
|
||||
public static final String GETCURRENCYPATTERN = "getCurrencyPattern";
|
||||
public static final String GETPREFERREDLANGUAGE = "getPreferredLanguage";
|
||||
|
||||
//GlobalizationCommand Option Parameters
|
||||
public static final String OPTIONS = "options";
|
||||
public static final String FORMATLENGTH = "formatLength";
|
||||
//public static final String SHORT = "short"; //default for dateToString format
|
||||
public static final String MEDIUM = "medium";
|
||||
public static final String LONG = "long";
|
||||
public static final String FULL = "full";
|
||||
public static final String SELECTOR = "selector";
|
||||
//public static final String DATEANDTIME = "date and time"; //default for dateToString
|
||||
public static final String DATE = "date";
|
||||
public static final String TIME = "time";
|
||||
public static final String DATESTRING = "dateString";
|
||||
public static final String TYPE = "type";
|
||||
public static final String ITEM = "item";
|
||||
public static final String NARROW = "narrow";
|
||||
public static final String WIDE = "wide";
|
||||
public static final String MONTHS = "months";
|
||||
public static final String DAYS = "days";
|
||||
//public static final String DECMIAL = "wide"; //default for numberToString
|
||||
public static final String NUMBER = "number";
|
||||
public static final String NUMBERSTRING = "numberString";
|
||||
public static final String PERCENT = "percent";
|
||||
public static final String CURRENCY = "currency";
|
||||
public static final String CURRENCYCODE = "currencyCode";
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray data, CallbackContext callbackContext) {
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try{
|
||||
if (action.equals(GETLOCALENAME)){
|
||||
obj = getLocaleName();
|
||||
}else if (action.equals(GETPREFERREDLANGUAGE)){
|
||||
obj = getPreferredLanguage();
|
||||
} else if (action.equalsIgnoreCase(DATETOSTRING)) {
|
||||
obj = getDateToString(data);
|
||||
}else if(action.equalsIgnoreCase(STRINGTODATE)){
|
||||
obj = getStringtoDate(data);
|
||||
}else if(action.equalsIgnoreCase(GETDATEPATTERN)){
|
||||
obj = getDatePattern(data);
|
||||
}else if(action.equalsIgnoreCase(GETDATENAMES)){
|
||||
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.GINGERBREAD) {
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
} else {
|
||||
obj = getDateNames(data);
|
||||
}
|
||||
}else if(action.equalsIgnoreCase(ISDAYLIGHTSAVINGSTIME)){
|
||||
obj = getIsDayLightSavingsTime(data);
|
||||
}else if(action.equalsIgnoreCase(GETFIRSTDAYOFWEEK)){
|
||||
obj = getFirstDayOfWeek(data);
|
||||
}else if(action.equalsIgnoreCase(NUMBERTOSTRING)){
|
||||
obj = getNumberToString(data);
|
||||
}else if(action.equalsIgnoreCase(STRINGTONUMBER)){
|
||||
obj = getStringToNumber(data);
|
||||
}else if(action.equalsIgnoreCase(GETNUMBERPATTERN)){
|
||||
obj = getNumberPattern(data);
|
||||
}else if(action.equalsIgnoreCase(GETCURRENCYPATTERN)){
|
||||
obj = getCurrencyPattern(data);
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackContext.success(obj);
|
||||
}catch (GlobalizationError ge){
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, ge.toJson()));
|
||||
}catch (Exception e){
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
* @Description: Returns the string identifier for the client's current locale setting
|
||||
*
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The locale identifier
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getLocaleName() throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
obj.put("value",Locale.getDefault().toString());//get the locale from the Android Device
|
||||
return obj;
|
||||
}catch(Exception e){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* @Description: Returns the string identifier for the client's current language
|
||||
*
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The language identifier
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getPreferredLanguage() throws GlobalizationError {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("value", Locale.getDefault().getDisplayLanguage().toString());
|
||||
return obj;
|
||||
} catch (Exception e) {
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* @Description: Returns a date formatted as a string according to the client's user preferences and
|
||||
* calendar using the time zone of the client.
|
||||
*
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The localized date string
|
||||
*
|
||||
* @throws: GlobalizationError.FORMATTING_ERROR
|
||||
*/
|
||||
private JSONObject getDateToString(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
Date date = new Date((Long)options.getJSONObject(0).get(DATE));
|
||||
|
||||
//get formatting pattern from android device (Will only have device specific formatting for short form of date) or options supplied
|
||||
JSONObject datePattern = getDatePattern(options);
|
||||
SimpleDateFormat fmt = new SimpleDateFormat(datePattern.getString("pattern"));
|
||||
|
||||
//return formatted date
|
||||
return obj.put("value",fmt.format(date));
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Parses a date formatted as a string according to the client's user
|
||||
* preferences and calendar using the time zone of the client and returns
|
||||
* the corresponding date object
|
||||
* @Return: JSONObject
|
||||
* Object.year {Number}: The four digit year
|
||||
* Object.month {Number}: The month from (0 - 11)
|
||||
* Object.day {Number}: The day from (1 - 31)
|
||||
* Object.hour {Number}: The hour from (0 - 23)
|
||||
* Object.minute {Number}: The minute from (0 - 59)
|
||||
* Object.second {Number}: The second from (0 - 59)
|
||||
* Object.millisecond {Number}: The milliseconds (from 0 - 999), not available on all platforms
|
||||
*
|
||||
* @throws: GlobalizationError.PARSING_ERROR
|
||||
*/
|
||||
private JSONObject getStringtoDate(JSONArray options)throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
Date date;
|
||||
try{
|
||||
//get format pattern from android device (Will only have device specific formatting for short form of date) or options supplied
|
||||
DateFormat fmt = new SimpleDateFormat(getDatePattern(options).getString("pattern"));
|
||||
|
||||
//attempt parsing string based on user preferences
|
||||
date = fmt.parse(options.getJSONObject(0).get(DATESTRING).toString());
|
||||
|
||||
//set Android Time object
|
||||
Time time = new Time();
|
||||
time.set(date.getTime());
|
||||
|
||||
//return properties;
|
||||
obj.put("year", time.year);
|
||||
obj.put("month", time.month);
|
||||
obj.put("day", time.monthDay);
|
||||
obj.put("hour", time.hour);
|
||||
obj.put("minute", time.minute);
|
||||
obj.put("second", time.second);
|
||||
obj.put("millisecond", new Long(0));
|
||||
return obj;
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a pattern string for formatting and parsing dates according to the client's
|
||||
* user preferences.
|
||||
* @Return: JSONObject
|
||||
*
|
||||
* Object.pattern {String}: The date and time pattern for formatting and parsing dates.
|
||||
* The patterns follow Unicode Technical Standard #35
|
||||
* http://unicode.org/reports/tr35/tr35-4.html
|
||||
* Object.timezone {String}: The abbreviated name of the time zone on the client
|
||||
* Object.utc_offset {Number}: The current difference in seconds between the client's
|
||||
* time zone and coordinated universal time.
|
||||
* Object.dst_offset {Number}: The current daylight saving time offset in seconds
|
||||
* between the client's non-daylight saving's time zone
|
||||
* and the client's daylight saving's time zone.
|
||||
*
|
||||
* @throws: GlobalizationError.PATTERN_ERROR
|
||||
*/
|
||||
private JSONObject getDatePattern(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try{
|
||||
SimpleDateFormat fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getDateFormat(this.cordova.getActivity()); //default user preference for date
|
||||
SimpleDateFormat fmtTime = (SimpleDateFormat)android.text.format.DateFormat.getTimeFormat(this.cordova.getActivity()); //default user preference for time
|
||||
|
||||
String fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern(); //default SHORT date/time format. ex. dd/MM/yyyy h:mm a
|
||||
|
||||
//get Date value + options (if available)
|
||||
if (options.getJSONObject(0).length() > 1){
|
||||
//options were included
|
||||
|
||||
//get formatLength option
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(FORMATLENGTH)){
|
||||
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(FORMATLENGTH);
|
||||
if (fmtOpt.equalsIgnoreCase(MEDIUM)){//medium
|
||||
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getMediumDateFormat(this.cordova.getActivity());
|
||||
}else if (fmtOpt.equalsIgnoreCase(LONG) || fmtOpt.equalsIgnoreCase(FULL)){ //long/full
|
||||
fmtDate = (SimpleDateFormat)android.text.format.DateFormat.getLongDateFormat(this.cordova.getActivity());
|
||||
}
|
||||
}
|
||||
|
||||
//return pattern type
|
||||
fmt = fmtDate.toLocalizedPattern() + " " + fmtTime.toLocalizedPattern();
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(SELECTOR)){
|
||||
String selOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(SELECTOR);
|
||||
if (selOpt.equalsIgnoreCase(DATE)){
|
||||
fmt = fmtDate.toLocalizedPattern();
|
||||
}else if (selOpt.equalsIgnoreCase(TIME)){
|
||||
fmt = fmtTime.toLocalizedPattern();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TimeZone from users device
|
||||
//TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone(); //substitute method
|
||||
TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
|
||||
|
||||
obj.put("pattern", fmt);
|
||||
obj.put("timezone", tz.getDisplayName(tz.inDaylightTime(Calendar.getInstance().getTime()),TimeZone.SHORT));
|
||||
obj.put("utc_offset", tz.getRawOffset()/1000);
|
||||
obj.put("dst_offset", tz.getDSTSavings()/1000);
|
||||
return obj;
|
||||
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns an array of either the names of the months or days of the week
|
||||
* according to the client's user preferences and calendar
|
||||
* @Return: JSONObject
|
||||
* Object.value {Array{String}}: The array of names starting from either
|
||||
* the first month in the year or the
|
||||
* first day of the week.
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
@TargetApi(9)
|
||||
private JSONObject getDateNames(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
//String[] value;
|
||||
JSONArray value = new JSONArray();
|
||||
List<String> namesList = new ArrayList<String>();
|
||||
final Map<String,Integer> namesMap; // final needed for sorting with anonymous comparator
|
||||
try{
|
||||
int type = 0; //default wide
|
||||
int item = 0; //default months
|
||||
|
||||
//get options if available
|
||||
if (options.getJSONObject(0).length() > 0){
|
||||
//get type if available
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
|
||||
String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
|
||||
if (t.equalsIgnoreCase(NARROW)){type++;} //DateUtils.LENGTH_MEDIUM
|
||||
}
|
||||
//get item if available
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(ITEM)){
|
||||
String t = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(ITEM);
|
||||
if (t.equalsIgnoreCase(DAYS)){item += 10;} //Days of week start at 1
|
||||
}
|
||||
}
|
||||
//determine return value
|
||||
int method = item + type;
|
||||
if (method == 1) { //months and narrow
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.SHORT, Locale.getDefault());
|
||||
} else if (method == 10) { //days and wide
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault());
|
||||
} else if (method == 11) { //days and narrow
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault());
|
||||
} else { //default: months and wide
|
||||
namesMap = Calendar.getInstance().getDisplayNames(Calendar.MONTH, Calendar.LONG, Locale.getDefault());
|
||||
}
|
||||
|
||||
// save names as a list
|
||||
for(String name : namesMap.keySet()) {
|
||||
namesList.add(name);
|
||||
}
|
||||
|
||||
// sort the list according to values in namesMap
|
||||
Collections.sort(namesList, new Comparator<String>() {
|
||||
public int compare(String arg0, String arg1) {
|
||||
return namesMap.get(arg0).compareTo(namesMap.get(arg1));
|
||||
}
|
||||
});
|
||||
|
||||
// convert nameList into JSONArray of String objects
|
||||
for (int i = 0; i < namesList.size(); i ++){
|
||||
value.put(namesList.get(i));
|
||||
}
|
||||
|
||||
//return array of names
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns whether daylight savings time is in effect for a given date using the client's
|
||||
* time zone and calendar.
|
||||
* @Return: JSONObject
|
||||
* Object.dst {Boolean}: The value "true" indicates that daylight savings time is
|
||||
* in effect for the given date and "false" indicate that it is not. *
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getIsDayLightSavingsTime(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
boolean dst = false;
|
||||
try{
|
||||
Date date = new Date((Long)options.getJSONObject(0).get(DATE));
|
||||
//TimeZone tz = Calendar.getInstance(Locale.getDefault()).getTimeZone();
|
||||
TimeZone tz = TimeZone.getTimeZone(Time.getCurrentTimezone());
|
||||
dst = tz.inDaylightTime(date); //get daylight savings data from date object and user timezone settings
|
||||
|
||||
return obj.put("dst",dst);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns the first day of the week according to the client's user preferences and calendar.
|
||||
* The days of the week are numbered starting from 1 where 1 is considered to be Sunday.
|
||||
* @Return: JSONObject
|
||||
* Object.value {Number}: The number of the first day of the week.
|
||||
*
|
||||
* @throws: GlobalizationError.UNKNOWN_ERROR
|
||||
*/
|
||||
private JSONObject getFirstDayOfWeek(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
int value = Calendar.getInstance(Locale.getDefault()).getFirstDayOfWeek(); //get first day of week based on user locale settings
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a number formatted as a string according to the client's user preferences.
|
||||
* @Return: JSONObject
|
||||
* Object.value {String}: The formatted number string.
|
||||
*
|
||||
* @throws: GlobalizationError.FORMATTING_ERROR
|
||||
*/
|
||||
private JSONObject getNumberToString(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
String value = "";
|
||||
try{
|
||||
DecimalFormat fmt = getNumberFormatInstance(options);//returns Decimal/Currency/Percent instance
|
||||
value = fmt.format(options.getJSONObject(0).get(NUMBER));
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Parses a number formatted as a string according to the client's user preferences and
|
||||
* returns the corresponding number.
|
||||
* @Return: JSONObject
|
||||
* Object.value {Number}: The parsed number.
|
||||
*
|
||||
* @throws: GlobalizationError.PARSING_ERROR
|
||||
*/
|
||||
private JSONObject getStringToNumber(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
Number value;
|
||||
try{
|
||||
DecimalFormat fmt = getNumberFormatInstance(options); //returns Decimal/Currency/Percent instance
|
||||
value = fmt.parse((String)options.getJSONObject(0).get(NUMBERSTRING));
|
||||
return obj.put("value", value);
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PARSING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a pattern string for formatting and parsing numbers according to the client's user
|
||||
* preferences.
|
||||
* @Return: JSONObject
|
||||
* Object.pattern {String}: The number pattern for formatting and parsing numbers.
|
||||
* The patterns follow Unicode Technical Standard #35.
|
||||
* http://unicode.org/reports/tr35/tr35-4.html
|
||||
* Object.symbol {String}: The symbol to be used when formatting and parsing
|
||||
* e.g., percent or currency symbol.
|
||||
* Object.fraction {Number}: The number of fractional digits to use when parsing and
|
||||
* formatting numbers.
|
||||
* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
|
||||
* Object.positive {String}: The symbol to use for positive numbers when parsing and formatting.
|
||||
* Object.negative: {String}: The symbol to use for negative numbers when parsing and formatting.
|
||||
* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
|
||||
* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
|
||||
*
|
||||
* @throws: GlobalizationError.PATTERN_ERROR
|
||||
*/
|
||||
private JSONObject getNumberPattern(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
//uses java.text.DecimalFormat to format value
|
||||
DecimalFormat fmt = (DecimalFormat) DecimalFormat.getInstance(Locale.getDefault()); //default format
|
||||
String symbol = String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator());
|
||||
//get Date value + options (if available)
|
||||
if (options.getJSONObject(0).length() > 0){
|
||||
//options were included
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
|
||||
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
|
||||
if (fmtOpt.equalsIgnoreCase(CURRENCY)){
|
||||
fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
|
||||
symbol = fmt.getDecimalFormatSymbols().getCurrencySymbol();
|
||||
}else if(fmtOpt.equalsIgnoreCase(PERCENT)){
|
||||
fmt = (DecimalFormat) DecimalFormat.getPercentInstance(Locale.getDefault());
|
||||
symbol = String.valueOf(fmt.getDecimalFormatSymbols().getPercent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//return properties
|
||||
obj.put("pattern", fmt.toPattern());
|
||||
obj.put("symbol", symbol);
|
||||
obj.put("fraction", fmt.getMinimumFractionDigits());
|
||||
obj.put("rounding", new Integer(0));
|
||||
obj.put("positive", fmt.getPositivePrefix());
|
||||
obj.put("negative", fmt.getNegativePrefix());
|
||||
obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
|
||||
obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
|
||||
|
||||
return obj;
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.PATTERN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Returns a pattern string for formatting and parsing currency values according to the client's
|
||||
* user preferences and ISO 4217 currency code.
|
||||
* @Return: JSONObject
|
||||
* Object.pattern {String}: The currency pattern for formatting and parsing currency values.
|
||||
* The patterns follow Unicode Technical Standard #35
|
||||
* http://unicode.org/reports/tr35/tr35-4.html
|
||||
* Object.code {String}: The ISO 4217 currency code for the pattern.
|
||||
* Object.fraction {Number}: The number of fractional digits to use when parsing and
|
||||
* formatting currency.
|
||||
* Object.rounding {Number}: The rounding increment to use when parsing and formatting.
|
||||
* Object.decimal: {String}: The decimal symbol to use for parsing and formatting.
|
||||
* Object.grouping: {String}: The grouping symbol to use for parsing and formatting.
|
||||
*
|
||||
* @throws: GlobalizationError.FORMATTING_ERROR
|
||||
*/
|
||||
private JSONObject getCurrencyPattern(JSONArray options) throws GlobalizationError{
|
||||
JSONObject obj = new JSONObject();
|
||||
try{
|
||||
//get ISO 4217 currency code
|
||||
String code = options.getJSONObject(0).getString(CURRENCYCODE);
|
||||
|
||||
//uses java.text.DecimalFormat to format value
|
||||
DecimalFormat fmt = (DecimalFormat) DecimalFormat.getCurrencyInstance(Locale.getDefault());
|
||||
|
||||
//set currency format
|
||||
Currency currency = Currency.getInstance(code);
|
||||
fmt.setCurrency(currency);
|
||||
|
||||
//return properties
|
||||
obj.put("pattern", fmt.toPattern());
|
||||
obj.put("code", currency.getCurrencyCode());
|
||||
obj.put("fraction", fmt.getMinimumFractionDigits());
|
||||
obj.put("rounding", new Integer(0));
|
||||
obj.put("decimal", String.valueOf(fmt.getDecimalFormatSymbols().getDecimalSeparator()));
|
||||
obj.put("grouping", String.valueOf(fmt.getDecimalFormatSymbols().getGroupingSeparator()));
|
||||
|
||||
return obj;
|
||||
}catch(Exception ge){
|
||||
throw new GlobalizationError(GlobalizationError.FORMATTING_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @Description: Parses a JSONArray from user options and returns the correct Instance of Decimal/Percent/Currency.
|
||||
* @Return: DecimalFormat : The Instance to use.
|
||||
*
|
||||
* @throws: JSONException
|
||||
*/
|
||||
private DecimalFormat getNumberFormatInstance(JSONArray options) throws JSONException{
|
||||
DecimalFormat fmt = (DecimalFormat)DecimalFormat.getInstance(Locale.getDefault()); //default format
|
||||
try{
|
||||
if (options.getJSONObject(0).length() > 1){
|
||||
//options were included
|
||||
if (!((JSONObject)options.getJSONObject(0).get(OPTIONS)).isNull(TYPE)){
|
||||
String fmtOpt = (String)((JSONObject)options.getJSONObject(0).get(OPTIONS)).get(TYPE);
|
||||
if (fmtOpt.equalsIgnoreCase(CURRENCY)){
|
||||
fmt = (DecimalFormat)DecimalFormat.getCurrencyInstance(Locale.getDefault());
|
||||
}else if(fmtOpt.equalsIgnoreCase(PERCENT)){
|
||||
fmt = (DecimalFormat)DecimalFormat.getPercentInstance(Locale.getDefault());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}catch (JSONException je){}
|
||||
return fmt;
|
||||
}
|
||||
}
|
||||
108
framework/src/org/apache/cordova/GlobalizationError.java
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* @description Exception class representing defined Globalization error codes
|
||||
* @Globalization error codes:
|
||||
* GlobalizationError.UNKNOWN_ERROR = 0;
|
||||
* GlobalizationError.FORMATTING_ERROR = 1;
|
||||
* GlobalizationError.PARSING_ERROR = 2;
|
||||
* GlobalizationError.PATTERN_ERROR = 3;
|
||||
*/
|
||||
public class GlobalizationError extends Exception{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static final String UNKNOWN_ERROR = "UNKNOWN_ERROR";
|
||||
public static final String FORMATTING_ERROR = "FORMATTING_ERROR";
|
||||
public static final String PARSING_ERROR = "PARSING_ERROR";
|
||||
public static final String PATTERN_ERROR = "PATTERN_ERROR";
|
||||
|
||||
int error = 0; //default unknown error thrown
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public GlobalizationError() {}
|
||||
/**
|
||||
* Create an exception returning an error code
|
||||
*
|
||||
* @param s
|
||||
*/
|
||||
public GlobalizationError(String s) {
|
||||
if (s.equalsIgnoreCase(FORMATTING_ERROR)){
|
||||
error = 1;
|
||||
}else if (s.equalsIgnoreCase(PARSING_ERROR)){
|
||||
error = 2;
|
||||
}else if (s.equalsIgnoreCase(PATTERN_ERROR)){
|
||||
error = 3;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* get error string based on error code
|
||||
*
|
||||
* @param String msg
|
||||
*/
|
||||
public String getErrorString(){
|
||||
String msg = "";
|
||||
switch (error){
|
||||
case 0:
|
||||
msg = UNKNOWN_ERROR;
|
||||
break;
|
||||
case 1:
|
||||
msg = FORMATTING_ERROR;
|
||||
break;
|
||||
case 2:
|
||||
msg = PARSING_ERROR;
|
||||
break;
|
||||
case 3:
|
||||
msg = PATTERN_ERROR;
|
||||
break;
|
||||
}
|
||||
return msg;
|
||||
}
|
||||
/**
|
||||
* get error code
|
||||
*
|
||||
* @param String msg
|
||||
*/
|
||||
public int getErrorCode(){
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the json version of this object to return to javascript
|
||||
* @return
|
||||
*/
|
||||
public JSONObject toJson() {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("code", getErrorCode());
|
||||
obj.put("message", getErrorString());
|
||||
} catch (JSONException e) {
|
||||
// never happens
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
531
framework/src/org/apache/cordova/InAppBrowser.java
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.text.InputType;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
public class InAppBrowser extends CordovaPlugin {
|
||||
|
||||
private static final String NULL = "null";
|
||||
protected static final String LOG_TAG = "InAppBrowser";
|
||||
private static final String SELF = "_self";
|
||||
private static final String SYSTEM = "_system";
|
||||
// private static final String BLANK = "_blank";
|
||||
private static final String LOCATION = "location";
|
||||
private static final String EXIT_EVENT = "exit";
|
||||
private static final String LOAD_START_EVENT = "loadstart";
|
||||
private static final String LOAD_STOP_EVENT = "loadstop";
|
||||
|
||||
private Dialog dialog;
|
||||
private WebView inAppWebView;
|
||||
private EditText edittext;
|
||||
private boolean showLocationBar = true;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
this.callbackContext = callbackContext;
|
||||
|
||||
try {
|
||||
if (action.equals("open")) {
|
||||
String url = args.getString(0);
|
||||
String target = args.optString(1);
|
||||
if (target == null || target.equals("") || target.equals(NULL)) {
|
||||
target = SELF;
|
||||
}
|
||||
HashMap<String, Boolean> features = parseFeature(args.optString(2));
|
||||
|
||||
Log.d(LOG_TAG, "target = " + target);
|
||||
|
||||
url = updateUrl(url);
|
||||
|
||||
// SELF
|
||||
if (SELF.equals(target)) {
|
||||
Log.d(LOG_TAG, "in self");
|
||||
// load in webview
|
||||
if (url.startsWith("file://") || url.startsWith("javascript:")
|
||||
|| this.webView.isUrlWhiteListed(url)) {
|
||||
this.webView.loadUrl(url);
|
||||
}
|
||||
// load in InAppBrowser
|
||||
else {
|
||||
result = this.showWebPage(url, features);
|
||||
}
|
||||
}
|
||||
// SYSTEM
|
||||
else if (SYSTEM.equals(target)) {
|
||||
Log.d(LOG_TAG, "in system");
|
||||
result = this.openExternal(url);
|
||||
}
|
||||
// BLANK - or anything else
|
||||
else {
|
||||
Log.d(LOG_TAG, "in blank");
|
||||
result = this.showWebPage(url, features);
|
||||
}
|
||||
}
|
||||
else if (action.equals("close")) {
|
||||
closeDialog();
|
||||
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||
pluginResult.setKeepCallback(false);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
}
|
||||
else {
|
||||
status = PluginResult.Status.INVALID_ACTION;
|
||||
}
|
||||
PluginResult pluginResult = new PluginResult(status, result);
|
||||
pluginResult.setKeepCallback(true);
|
||||
this.callbackContext.sendPluginResult(pluginResult);
|
||||
} catch (JSONException e) {
|
||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put the list of features into a hash map
|
||||
*
|
||||
* @param optString
|
||||
* @return
|
||||
*/
|
||||
private HashMap<String, Boolean> parseFeature(String optString) {
|
||||
if (optString.equals(NULL)) {
|
||||
return null;
|
||||
} else {
|
||||
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
|
||||
StringTokenizer features = new StringTokenizer(optString, ",");
|
||||
StringTokenizer option;
|
||||
while(features.hasMoreElements()) {
|
||||
option = new StringTokenizer(features.nextToken(), "=");
|
||||
if (option.hasMoreElements()) {
|
||||
String key = option.nextToken();
|
||||
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
|
||||
map.put(key, value);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert relative URL to full path
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
private String updateUrl(String url) {
|
||||
Uri newUrl = Uri.parse(url);
|
||||
if (newUrl.isRelative()) {
|
||||
url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a new browser with the specified URL.
|
||||
*
|
||||
* @param url The url to load.
|
||||
* @param usePhoneGap Load url in PhoneGap webview
|
||||
* @return "" if ok, or error message.
|
||||
*/
|
||||
public String openExternal(String url) {
|
||||
try {
|
||||
Intent intent = null;
|
||||
intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
return "";
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
|
||||
return e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the dialog
|
||||
*/
|
||||
private void closeDialog() {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", EXIT_EVENT);
|
||||
|
||||
sendUpdate(obj, false);
|
||||
} catch (JSONException ex) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if it is possible to go back one page in history, then does so.
|
||||
*/
|
||||
private void goBack() {
|
||||
if (this.inAppWebView.canGoBack()) {
|
||||
this.inAppWebView.goBack();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if it is possible to go forward one page in history, then does so.
|
||||
*/
|
||||
private void goForward() {
|
||||
if (this.inAppWebView.canGoForward()) {
|
||||
this.inAppWebView.goForward();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to the new page
|
||||
*
|
||||
* @param url to load
|
||||
*/
|
||||
private void navigate(String url) {
|
||||
InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
|
||||
|
||||
if (!url.startsWith("http") && !url.startsWith("file:")) {
|
||||
this.inAppWebView.loadUrl("http://" + url);
|
||||
} else {
|
||||
this.inAppWebView.loadUrl(url);
|
||||
}
|
||||
this.inAppWebView.requestFocus();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should we show the location bar?
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
private boolean getShowLocationBar() {
|
||||
return this.showLocationBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a new browser with the specified URL.
|
||||
*
|
||||
* @param url The url to load.
|
||||
* @param jsonObject
|
||||
*/
|
||||
public String showWebPage(final String url, HashMap<String, Boolean> features) {
|
||||
// Determine if we should hide the location bar.
|
||||
showLocationBar = true;
|
||||
if (features != null) {
|
||||
showLocationBar = features.get(LOCATION).booleanValue();
|
||||
}
|
||||
|
||||
final CordovaWebView thatWebView = this.webView;
|
||||
|
||||
// Create dialog in new thread
|
||||
Runnable runnable = new Runnable() {
|
||||
/**
|
||||
* Convert our DIP units to Pixels
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
private int dpToPixels(int dipValue) {
|
||||
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
|
||||
(float) dipValue,
|
||||
cordova.getActivity().getResources().getDisplayMetrics()
|
||||
);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
// Let's create the main dialog
|
||||
dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
|
||||
dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
|
||||
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
dialog.setCancelable(true);
|
||||
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", EXIT_EVENT);
|
||||
|
||||
sendUpdate(obj, false);
|
||||
} catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Main container layout
|
||||
LinearLayout main = new LinearLayout(cordova.getActivity());
|
||||
main.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
// Toolbar layout
|
||||
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
|
||||
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
|
||||
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
|
||||
toolbar.setHorizontalGravity(Gravity.LEFT);
|
||||
toolbar.setVerticalGravity(Gravity.TOP);
|
||||
|
||||
// Action Button Container layout
|
||||
RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());
|
||||
actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||
actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
|
||||
actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
|
||||
actionButtonContainer.setId(1);
|
||||
|
||||
// Back button
|
||||
Button back = new Button(cordova.getActivity());
|
||||
RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||
backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
|
||||
back.setLayoutParams(backLayoutParams);
|
||||
back.setContentDescription("Back Button");
|
||||
back.setId(2);
|
||||
back.setText("<");
|
||||
back.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
goBack();
|
||||
}
|
||||
});
|
||||
|
||||
// Forward button
|
||||
Button forward = new Button(cordova.getActivity());
|
||||
RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||
forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
|
||||
forward.setLayoutParams(forwardLayoutParams);
|
||||
forward.setContentDescription("Forward Button");
|
||||
forward.setId(3);
|
||||
forward.setText(">");
|
||||
forward.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
goForward();
|
||||
}
|
||||
});
|
||||
|
||||
// Edit Text Box
|
||||
edittext = new EditText(cordova.getActivity());
|
||||
RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||
textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
|
||||
textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
|
||||
edittext.setLayoutParams(textLayoutParams);
|
||||
edittext.setId(4);
|
||||
edittext.setSingleLine(true);
|
||||
edittext.setText(url);
|
||||
edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
|
||||
edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
|
||||
edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
|
||||
edittext.setOnKeyListener(new View.OnKeyListener() {
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
// If the event is a key-down event on the "enter" button
|
||||
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
|
||||
navigate(edittext.getText().toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
// Close button
|
||||
Button close = new Button(cordova.getActivity());
|
||||
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
close.setLayoutParams(closeLayoutParams);
|
||||
forward.setContentDescription("Close Button");
|
||||
close.setId(5);
|
||||
close.setText("Done");
|
||||
close.setOnClickListener(new View.OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
closeDialog();
|
||||
}
|
||||
});
|
||||
|
||||
// WebView
|
||||
inAppWebView = new WebView(cordova.getActivity());
|
||||
inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||
inAppWebView.setWebChromeClient(new WebChromeClient());
|
||||
WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
|
||||
inAppWebView.setWebViewClient(client);
|
||||
WebSettings settings = inAppWebView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setBuiltInZoomControls(true);
|
||||
/**
|
||||
* We need to be careful of this line as a future Android release may deprecate it out of existence.
|
||||
* Can't replace it with the API 8 level call right now as our minimum SDK is 7 until May 2013
|
||||
*/
|
||||
// @TODO: replace with settings.setPluginState(android.webkit.WebSettings.PluginState.ON)
|
||||
settings.setPluginsEnabled(true);
|
||||
settings.setDomStorageEnabled(true);
|
||||
inAppWebView.loadUrl(url);
|
||||
inAppWebView.setId(6);
|
||||
inAppWebView.getSettings().setLoadWithOverviewMode(true);
|
||||
inAppWebView.getSettings().setUseWideViewPort(true);
|
||||
inAppWebView.requestFocus();
|
||||
inAppWebView.requestFocusFromTouch();
|
||||
|
||||
// Add the back and forward buttons to our action button container layout
|
||||
actionButtonContainer.addView(back);
|
||||
actionButtonContainer.addView(forward);
|
||||
|
||||
// Add the views to our toolbar
|
||||
toolbar.addView(actionButtonContainer);
|
||||
toolbar.addView(edittext);
|
||||
toolbar.addView(close);
|
||||
|
||||
// Don't add the toolbar if its been disabled
|
||||
if (getShowLocationBar()) {
|
||||
// Add our toolbar to our main view/layout
|
||||
main.addView(toolbar);
|
||||
}
|
||||
|
||||
// Add our webview to our main view/layout
|
||||
main.addView(inAppWebView);
|
||||
|
||||
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
|
||||
lp.copyFrom(dialog.getWindow().getAttributes());
|
||||
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
|
||||
dialog.setContentView(main);
|
||||
dialog.show();
|
||||
dialog.getWindow().setAttributes(lp);
|
||||
}
|
||||
};
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
*
|
||||
* @param obj a JSONObject contain event payload information
|
||||
*/
|
||||
private void sendUpdate(JSONObject obj, boolean keepCallback) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
|
||||
result.setKeepCallback(keepCallback);
|
||||
this.callbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The webview client receives notifications about appView
|
||||
*/
|
||||
public class InAppBrowserClient extends WebViewClient {
|
||||
EditText edittext;
|
||||
CordovaWebView webView;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param mContext
|
||||
* @param edittext
|
||||
*/
|
||||
public InAppBrowserClient(CordovaWebView webView, EditText mEditText) {
|
||||
this.webView = webView;
|
||||
this.edittext = mEditText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has started loading.
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
super.onPageStarted(view, url, favicon);
|
||||
String newloc;
|
||||
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
|
||||
newloc = url;
|
||||
} else {
|
||||
newloc = "http://" + url;
|
||||
}
|
||||
|
||||
if (!newloc.equals(edittext.getText().toString())) {
|
||||
edittext.setText(newloc);
|
||||
}
|
||||
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", LOAD_START_EVENT);
|
||||
obj.put("url", newloc);
|
||||
|
||||
sendUpdate(obj, true);
|
||||
} catch (JSONException ex) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
}
|
||||
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("type", LOAD_STOP_EVENT);
|
||||
obj.put("url", url);
|
||||
|
||||
sendUpdate(obj, true);
|
||||
} catch (JSONException ex) {
|
||||
Log.d(LOG_TAG, "Should never happen");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
474
framework/src/org/apache/cordova/NativeToJsMessageQueue.java
Executable file
@@ -0,0 +1,474 @@
|
||||
/*
|
||||
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.Method;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
|
||||
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 = 2;
|
||||
|
||||
// Set this to true to force plugin results to be encoding as
|
||||
// JS instead of the custom format (useful for benchmarking).
|
||||
private static final boolean FORCE_ENCODE_USING_EVAL = false;
|
||||
|
||||
// Disable URL-based exec() bridge by default since it's a bit of a
|
||||
// security concern.
|
||||
static final boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
|
||||
|
||||
// Disable sending back native->JS messages during an exec() when the active
|
||||
// exec() is asynchronous. Set this to true when running bridge benchmarks.
|
||||
static final boolean DISABLE_EXEC_CHAINING = false;
|
||||
|
||||
// Upper limit for how much data to send to JS in one shot.
|
||||
// TODO(agrieve): This is currently disable. It should be re-enabled once we
|
||||
// remove support for returning values from exec() calls. This was
|
||||
// deprecated in 2.2.0.
|
||||
// Also, this currently only chops up on message boundaries. It may be useful
|
||||
// to allow it to break up messages.
|
||||
private static int MAX_PAYLOAD_SIZE = -1; //50 * 1024 * 10240;
|
||||
|
||||
/**
|
||||
* The index into registeredListeners to treat as active.
|
||||
*/
|
||||
private int activeListenerIndex;
|
||||
|
||||
/**
|
||||
* When true, the active listener is not fired upon enqueue. When set to false,
|
||||
* the active listener will be fired if the queue is non-empty.
|
||||
*/
|
||||
private boolean paused;
|
||||
|
||||
/**
|
||||
* The list of JavaScript statements to be sent to JavaScript.
|
||||
*/
|
||||
private final LinkedList<JsMessage> queue = new LinkedList<JsMessage>();
|
||||
|
||||
/**
|
||||
* 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[4];
|
||||
registeredListeners[0] = null; // Polling. Requires no logic.
|
||||
registeredListeners[1] = new LoadUrlBridgeMode();
|
||||
registeredListeners[2] = new OnlineEventsBridgeMode();
|
||||
registeredListeners[3] = 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 (!paused && !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);
|
||||
}
|
||||
}
|
||||
|
||||
private int calculatePackedMessageLength(JsMessage message) {
|
||||
int messageLen = message.calculateEncodedLength();
|
||||
String messageLenStr = String.valueOf(messageLen);
|
||||
return messageLenStr.length() + messageLen + 1;
|
||||
}
|
||||
|
||||
private void packMessage(JsMessage message, StringBuilder sb) {
|
||||
sb.append(message.calculateEncodedLength())
|
||||
.append(' ');
|
||||
message.encodeAsMessage(sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines and returns queued messages combined into a single string.
|
||||
* Combines as many messages as possible, while staying under MAX_PAYLOAD_SIZE.
|
||||
* Returns null if the queue is empty.
|
||||
*/
|
||||
public String popAndEncode() {
|
||||
synchronized (this) {
|
||||
if (queue.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
int totalPayloadLen = 0;
|
||||
int numMessagesToSend = 0;
|
||||
for (JsMessage message : queue) {
|
||||
int messageSize = calculatePackedMessageLength(message);
|
||||
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
|
||||
break;
|
||||
}
|
||||
totalPayloadLen += messageSize;
|
||||
numMessagesToSend += 1;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder(totalPayloadLen);
|
||||
for (int i = 0; i < numMessagesToSend; ++i) {
|
||||
JsMessage message = queue.removeFirst();
|
||||
packMessage(message, sb);
|
||||
}
|
||||
|
||||
if (!queue.isEmpty()) {
|
||||
// Attach a char to indicate that there are more messages pending.
|
||||
sb.append('*');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as popAndEncode(), except encodes in a form that can be executed as JS.
|
||||
*/
|
||||
private String popAndEncodeAsJs() {
|
||||
synchronized (this) {
|
||||
int length = queue.size();
|
||||
if (length == 0) {
|
||||
return null;
|
||||
}
|
||||
int totalPayloadLen = 0;
|
||||
int numMessagesToSend = 0;
|
||||
for (JsMessage message : queue) {
|
||||
int messageSize = message.calculateEncodedLength() + 50; // overestimate.
|
||||
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
|
||||
break;
|
||||
}
|
||||
totalPayloadLen += messageSize;
|
||||
numMessagesToSend += 1;
|
||||
}
|
||||
boolean willSendAllMessages = numMessagesToSend == queue.size();
|
||||
StringBuilder sb = new StringBuilder(totalPayloadLen + (willSendAllMessages ? 0 : 100));
|
||||
// Wrap each statement in a try/finally so that if one throws it does
|
||||
// not affect the next.
|
||||
for (int i = 0; i < numMessagesToSend; ++i) {
|
||||
JsMessage message = queue.removeFirst();
|
||||
if (willSendAllMessages && (i + 1 == numMessagesToSend)) {
|
||||
message.encodeAsJsMessage(sb);
|
||||
} else {
|
||||
sb.append("try{");
|
||||
message.encodeAsJsMessage(sb);
|
||||
sb.append("}finally{");
|
||||
}
|
||||
}
|
||||
if (!willSendAllMessages) {
|
||||
sb.append("window.setTimeout(function(){cordova.require('cordova/plugin/android/polling').pollOnce();},0);");
|
||||
}
|
||||
for (int i = willSendAllMessages ? 1 : 0; i < numMessagesToSend; ++i) {
|
||||
sb.append('}');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript statement to the list.
|
||||
*/
|
||||
public void addJavaScript(String statement) {
|
||||
enqueueMessage(new JsMessage(statement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript statement to the list.
|
||||
*/
|
||||
public void addPluginResult(PluginResult result, String callbackId) {
|
||||
if (callbackId == null) {
|
||||
Log.e(LOG_TAG, "Got plugin result with no callbackId", new Throwable());
|
||||
return;
|
||||
}
|
||||
// Don't send anything if there is no result and there is no need to
|
||||
// clear the callbacks.
|
||||
boolean noResult = result.getStatus() == PluginResult.Status.NO_RESULT.ordinal();
|
||||
boolean keepCallback = result.getKeepCallback();
|
||||
if (noResult && keepCallback) {
|
||||
return;
|
||||
}
|
||||
JsMessage message = new JsMessage(result, callbackId);
|
||||
if (FORCE_ENCODE_USING_EVAL) {
|
||||
StringBuilder sb = new StringBuilder(message.calculateEncodedLength() + 50);
|
||||
message.encodeAsJsMessage(sb);
|
||||
message = new JsMessage(sb.toString());
|
||||
}
|
||||
|
||||
enqueueMessage(message);
|
||||
}
|
||||
|
||||
private void enqueueMessage(JsMessage message) {
|
||||
synchronized (this) {
|
||||
queue.add(message);
|
||||
if (!paused && registeredListeners[activeListenerIndex] != null) {
|
||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setPaused(boolean value) {
|
||||
if (paused && value) {
|
||||
// This should never happen. If a use-case for it comes up, we should
|
||||
// change pause to be a counter.
|
||||
Log.e(LOG_TAG, "nested call to setPaused detected.", new Throwable());
|
||||
}
|
||||
paused = value;
|
||||
if (!value) {
|
||||
synchronized (this) {
|
||||
if (!queue.isEmpty() && registeredListeners[activeListenerIndex] != null) {
|
||||
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getPaused() {
|
||||
return paused;
|
||||
}
|
||||
|
||||
private interface BridgeMode {
|
||||
void onNativeToJsMessageAvailable();
|
||||
}
|
||||
|
||||
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||
private class LoadUrlBridgeMode implements BridgeMode {
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
String js = popAndEncodeAsJs();
|
||||
if (js != null) {
|
||||
webView.loadUrlNow("javascript:" + js);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void onNativeToJsMessageAvailable() {
|
||||
cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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() {
|
||||
public void run() {
|
||||
if (!queue.isEmpty()) {
|
||||
online = !online;
|
||||
webView.setNetworkAvailable(online);
|
||||
}
|
||||
}
|
||||
};
|
||||
OnlineEventsBridgeMode() {
|
||||
webView.setNetworkAvailable(true);
|
||||
}
|
||||
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 = popAndEncodeAsJs();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private static class JsMessage {
|
||||
final String jsPayloadOrCallbackId;
|
||||
final PluginResult pluginResult;
|
||||
JsMessage(String js) {
|
||||
if (js == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
jsPayloadOrCallbackId = js;
|
||||
pluginResult = null;
|
||||
}
|
||||
JsMessage(PluginResult pluginResult, String callbackId) {
|
||||
if (callbackId == null || pluginResult == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
jsPayloadOrCallbackId = callbackId;
|
||||
this.pluginResult = pluginResult;
|
||||
}
|
||||
|
||||
int calculateEncodedLength() {
|
||||
if (pluginResult == null) {
|
||||
return jsPayloadOrCallbackId.length() + 1;
|
||||
}
|
||||
int statusLen = String.valueOf(pluginResult.getStatus()).length();
|
||||
int ret = 2 + statusLen + 1 + jsPayloadOrCallbackId.length() + 1;
|
||||
switch (pluginResult.getMessageType()) {
|
||||
case PluginResult.MESSAGE_TYPE_BOOLEAN: // f or t
|
||||
case PluginResult.MESSAGE_TYPE_NULL: // N
|
||||
ret += 1;
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_NUMBER: // n
|
||||
ret += 1 + pluginResult.getMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||
ret += 1 + pluginResult.getStrMessage().length();
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_JSON:
|
||||
default:
|
||||
ret += pluginResult.getMessage().length();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void encodeAsMessage(StringBuilder sb) {
|
||||
if (pluginResult == null) {
|
||||
sb.append('J')
|
||||
.append(jsPayloadOrCallbackId);
|
||||
return;
|
||||
}
|
||||
int status = pluginResult.getStatus();
|
||||
boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
|
||||
boolean resultOk = status == PluginResult.Status.OK.ordinal();
|
||||
boolean keepCallback = pluginResult.getKeepCallback();
|
||||
|
||||
sb.append((noResult || resultOk) ? 'S' : 'F')
|
||||
.append(keepCallback ? '1' : '0')
|
||||
.append(status)
|
||||
.append(' ')
|
||||
.append(jsPayloadOrCallbackId)
|
||||
.append(' ');
|
||||
switch (pluginResult.getMessageType()) {
|
||||
case PluginResult.MESSAGE_TYPE_BOOLEAN:
|
||||
sb.append(pluginResult.getMessage().charAt(0)); // t or f.
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_NULL: // N
|
||||
sb.append('N');
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_NUMBER: // n
|
||||
sb.append('n')
|
||||
.append(pluginResult.getMessage());
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||
sb.append('s');
|
||||
sb.append(pluginResult.getStrMessage());
|
||||
break;
|
||||
case PluginResult.MESSAGE_TYPE_JSON:
|
||||
default:
|
||||
sb.append(pluginResult.getMessage()); // [ or {
|
||||
}
|
||||
}
|
||||
|
||||
void encodeAsJsMessage(StringBuilder sb) {
|
||||
if (pluginResult == null) {
|
||||
sb.append(jsPayloadOrCallbackId);
|
||||
} else {
|
||||
int status = pluginResult.getStatus();
|
||||
boolean success = (status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal());
|
||||
sb.append("cordova.callbackFromNative('")
|
||||
.append(jsPayloadOrCallbackId)
|
||||
.append("',")
|
||||
.append(success)
|
||||
.append(",")
|
||||
.append(status)
|
||||
.append(",")
|
||||
.append(pluginResult.getMessage())
|
||||
.append(",")
|
||||
.append(pluginResult.getKeepCallback())
|
||||
.append(");");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,9 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
|
||||
@@ -31,7 +32,7 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class NetworkManager extends Plugin {
|
||||
public class NetworkManager extends CordovaPlugin {
|
||||
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
@@ -68,10 +69,12 @@ public class NetworkManager extends Plugin {
|
||||
|
||||
private static final String LOG_TAG = "NetworkManager";
|
||||
|
||||
private String connectionCallbackId;
|
||||
private CallbackContext connectionCallbackContext;
|
||||
private boolean registered = false;
|
||||
|
||||
ConnectivityManager sockMan;
|
||||
BroadcastReceiver receiver;
|
||||
private String lastStatus = "";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -85,24 +88,27 @@ public class NetworkManager extends Plugin {
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void setContext(CordovaInterface cordova) {
|
||||
super.setContext(cordova);
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.sockMan = (ConnectivityManager) cordova.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.connectionCallbackId = null;
|
||||
this.connectionCallbackContext = null;
|
||||
|
||||
// We need to listen to connectivity events to update navigator.connection
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
if (this.receiver == null) {
|
||||
this.receiver = new BroadcastReceiver() {
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
// (The null check is for the ARM Emulator, please use Intel Emulator for better results)
|
||||
if(NetworkManager.this.webView != null)
|
||||
updateConnectionInfo(sockMan.getActiveNetworkInfo());
|
||||
}
|
||||
};
|
||||
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
this.registered = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,43 +116,31 @@ public class NetworkManager 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.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||
* @return True if the action was valid, false otherwise.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
|
||||
String result = "Unsupported Operation: " + action;
|
||||
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("getConnectionInfo")) {
|
||||
this.connectionCallbackId = callbackId;
|
||||
this.connectionCallbackContext = callbackContext;
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
|
||||
pluginResult.setKeepCallback(true);
|
||||
return pluginResult;
|
||||
callbackContext.sendPluginResult(pluginResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop network receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
if (this.receiver != null) {
|
||||
if (this.receiver != null && this.registered) {
|
||||
try {
|
||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||
this.registered = false;
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||
}
|
||||
@@ -165,7 +159,14 @@ public class NetworkManager extends Plugin {
|
||||
*/
|
||||
private void updateConnectionInfo(NetworkInfo info) {
|
||||
// send update to javascript "navigator.network.connection"
|
||||
sendUpdate(this.getConnectionInfo(info));
|
||||
// Jellybean sends its own info
|
||||
String thisStatus = this.getConnectionInfo(info);
|
||||
if(!thisStatus.equals(lastStatus))
|
||||
{
|
||||
sendUpdate(thisStatus);
|
||||
lastStatus = thisStatus;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,6 +186,7 @@ public class NetworkManager extends Plugin {
|
||||
type = getType(info);
|
||||
}
|
||||
}
|
||||
Log.d("CordovaNetworkManager", "Connection Type: " + type);
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -194,11 +196,11 @@ public class NetworkManager extends Plugin {
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(String type) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||
result.setKeepCallback(true);
|
||||
this.success(result, this.connectionCallbackId);
|
||||
|
||||
// Send to all plugins
|
||||
if (connectionCallbackContext != null) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||
result.setKeepCallback(true);
|
||||
connectionCallbackContext.sendPluginResult(result);
|
||||
}
|
||||
webView.postMessage("networkconnection", type);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@@ -35,7 +36,7 @@ import android.os.Vibrator;
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
*/
|
||||
public class Notification extends Plugin {
|
||||
public class Notification extends CordovaPlugin {
|
||||
|
||||
public int confirmResult = -1;
|
||||
public ProgressDialog spinnerDialog = null;
|
||||
@@ -49,87 +50,49 @@ 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.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArray of arguments for the plugin.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return True when the action was valid, false otherwise.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (action.equals("beep")) {
|
||||
this.beep(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("vibrate")) {
|
||||
this.vibrate(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("alert")) {
|
||||
this.alert(args.getString(0), args.getString(1), args.getString(2), callbackId);
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
}
|
||||
else if (action.equals("confirm")) {
|
||||
this.confirm(args.getString(0), args.getString(1), args.getString(2), callbackId);
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
}
|
||||
else if (action.equals("activityStart")) {
|
||||
this.activityStart(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("activityStop")) {
|
||||
this.activityStop();
|
||||
}
|
||||
else if (action.equals("progressStart")) {
|
||||
this.progressStart(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("progressValue")) {
|
||||
this.progressValue(args.getInt(0));
|
||||
}
|
||||
else if (action.equals("progressStop")) {
|
||||
this.progressStop();
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("beep")) {
|
||||
this.beep(args.getLong(0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (action.equals("alert")) {
|
||||
else if (action.equals("vibrate")) {
|
||||
this.vibrate(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("alert")) {
|
||||
this.alert(args.getString(0), args.getString(1), args.getString(2), callbackContext);
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("confirm")) {
|
||||
this.confirm(args.getString(0), args.getString(1), args.getString(2), callbackContext);
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("activityStart")) {
|
||||
return true;
|
||||
this.activityStart(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("activityStop")) {
|
||||
return true;
|
||||
this.activityStop();
|
||||
}
|
||||
else if (action.equals("progressStart")) {
|
||||
return true;
|
||||
this.progressStart(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("progressValue")) {
|
||||
return true;
|
||||
this.progressValue(args.getInt(0));
|
||||
}
|
||||
else if (action.equals("progressStop")) {
|
||||
return true;
|
||||
this.progressStop();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only alert and confirm are async.
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -138,7 +101,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 +126,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) {
|
||||
@@ -177,15 +140,14 @@ 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 callbackId The callback id
|
||||
* @param message The message the alert should display
|
||||
* @param title The title of the alert
|
||||
* @param buttonLabel The label of the button
|
||||
* @param callbackContext The callback context
|
||||
*/
|
||||
public synchronized void alert(final String message, final String title, final String buttonLabel, final String callbackId) {
|
||||
public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) {
|
||||
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
final Notification notification = this;
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
@@ -193,14 +155,22 @@ 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) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 0), callbackId);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
|
||||
}
|
||||
});
|
||||
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog)
|
||||
{
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
|
||||
}
|
||||
});
|
||||
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
};
|
||||
@@ -212,16 +182,15 @@ 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)
|
||||
* @param callbackId The callback id
|
||||
*
|
||||
* @param message The message the dialog should display
|
||||
* @param title The title of the dialog
|
||||
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
||||
* @param callbackContext The callback context.
|
||||
*/
|
||||
public synchronized void confirm(final String message, final String title, String buttonLabels, final String callbackId) {
|
||||
public synchronized void confirm(final String message, final String title, String buttonLabels, final CallbackContext callbackContext) {
|
||||
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
final Notification notification = this;
|
||||
final String[] fButtons = buttonLabels.split(",");
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@@ -229,7 +198,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) {
|
||||
@@ -237,7 +206,7 @@ public class Notification extends Plugin {
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 1), callbackId);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 1));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -248,7 +217,7 @@ public class Notification extends Plugin {
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 2), callbackId);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 2));
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -259,11 +228,18 @@ public class Notification extends Plugin {
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 3), callbackId);
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 3));
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog)
|
||||
{
|
||||
dialog.dismiss();
|
||||
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
|
||||
}
|
||||
});
|
||||
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
@@ -274,7 +250,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Show the spinner.
|
||||
*
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
@@ -283,14 +259,13 @@ public class Notification extends Plugin {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
final Notification notification = this;
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
notification.spinnerDialog = ProgressDialog.show(cordova.getActivity(), title, message, true, true,
|
||||
Notification.this.spinnerDialog = ProgressDialog.show(cordova.getActivity(), title, message, true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
notification.spinnerDialog = null;
|
||||
Notification.this.spinnerDialog = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -310,7 +285,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 +319,7 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Set value of progress bar.
|
||||
*
|
||||
*
|
||||
* @param value 0-100
|
||||
*/
|
||||
public synchronized void progressValue(int value) {
|
||||
|
||||
@@ -19,26 +19,25 @@
|
||||
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.apache.cordova.api.CallbackContext;
|
||||
import org.apache.cordova.api.CordovaPlugin;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class SplashScreen extends Plugin {
|
||||
public class SplashScreen extends CordovaPlugin {
|
||||
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||
if (action.equals("hide")) {
|
||||
this.webView.postMessage("splashscreen", "hide");
|
||||
} else if (action.equals("show")){
|
||||
this.webView.postMessage("splashscreen", "show");
|
||||
}
|
||||
else {
|
||||
status = PluginResult.Status.INVALID_ACTION;
|
||||
return false;
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||