Compare commits
380 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
9d5fb0b201 | ||
|
|
e0a5fe4002 | ||
|
|
f9d9a0a4bd | ||
|
|
78f0c7b119 | ||
|
|
c6d8343de2 | ||
|
|
0ccd11e587 | ||
|
|
b486711d68 | ||
|
|
2eb4c5e960 | ||
|
|
85aa740c98 | ||
|
|
6415848383 | ||
|
|
beb9460538 | ||
|
|
c030770be7 | ||
|
|
0180342dff | ||
|
|
b97748d3dc | ||
|
|
9d4977db00 | ||
|
|
f095284faa | ||
|
|
401c2f42f9 | ||
|
|
eb0348d47c | ||
|
|
1f46240ba9 | ||
|
|
14870726e0 | ||
|
|
c7d6a2eecb | ||
|
|
ce61eb2174 | ||
|
|
f3df21ef0a | ||
|
|
5eb554e008 | ||
|
|
e5e7c3fad3 | ||
|
|
2a8b9ab75e | ||
|
|
c8f0ffb42f | ||
|
|
b3e68b96cf | ||
|
|
ae7a550a09 | ||
|
|
e069bbb800 | ||
|
|
17ff6be6a9 | ||
|
|
d42489c67a | ||
|
|
3ea72e5d21 | ||
|
|
762854ad7a | ||
|
|
10465066ee | ||
|
|
0cf9f51816 | ||
|
|
3d5e2340ca | ||
|
|
e2047afa42 | ||
|
|
231b39d2dc | ||
|
|
dddce30368 | ||
|
|
e0e4ba2bd7 | ||
|
|
e0eadb6b76 | ||
|
|
483e5dfbea | ||
|
|
8aa9d8213d | ||
|
|
a74f71c935 | ||
|
|
87b81e53f0 | ||
|
|
a2816e31c3 | ||
|
|
f78af9f27b | ||
|
|
98138a0a60 | ||
|
|
e639b6303e | ||
|
|
99fb3ebe00 | ||
|
|
5829840409 | ||
|
|
4699ab5500 | ||
|
|
69fc7f39b7 | ||
|
|
510a962a52 | ||
|
|
570fc3cfb2 | ||
|
|
5d211f2fa6 | ||
|
|
dcb127c14d | ||
|
|
fba87de064 | ||
|
|
576f8cba44 | ||
|
|
b9f9429542 | ||
|
|
bf0df9f3c3 | ||
|
|
1d458f2782 | ||
|
|
5ca4b4a884 | ||
|
|
5935052ead | ||
|
|
f3f2ad9144 | ||
|
|
9d1edc4554 | ||
|
|
5143b8a492 | ||
|
|
7c67f40fc4 | ||
|
|
167b600135 | ||
|
|
6c465e25d3 | ||
|
|
c183d06ed1 | ||
|
|
574731b853 | ||
|
|
94568a4ec8 | ||
|
|
b22c0e5b6d | ||
|
|
ab3347d25d | ||
|
|
66872de8e5 | ||
|
|
f6d4402fdc | ||
|
|
9f66ccb5f3 | ||
|
|
b339330592 | ||
|
|
56acd2953b | ||
|
|
6f8e13297e | ||
|
|
e9a9144098 | ||
|
|
d1905dbee8 | ||
|
|
adc88f01b7 | ||
|
|
507554b8e8 | ||
|
|
4795133daf | ||
|
|
c1c9075962 | ||
|
|
a691e9f744 | ||
|
|
8969eed506 | ||
|
|
be83095edf | ||
|
|
5c7783305a | ||
|
|
d60806bfa6 | ||
|
|
32febcb892 | ||
|
|
6c594b6f5f | ||
|
|
67d46432ed | ||
|
|
adf4166caa | ||
|
|
d9e7984279 | ||
|
|
e5b9900d3b | ||
|
|
fc3f1431b2 | ||
|
|
c8bf2f4cb1 | ||
|
|
d16555ec4b | ||
|
|
3c9415b1c2 | ||
|
|
aa45670d87 | ||
|
|
e6d801a594 | ||
|
|
0aec2be4dd | ||
|
|
c86b618aaa | ||
|
|
dffd2deb53 | ||
|
|
8ff48b371e | ||
|
|
6de66b87cb | ||
|
|
45680a562e |
13
.gitignore
vendored
@@ -2,6 +2,7 @@
|
||||
default.properties
|
||||
gen
|
||||
assets/www/cordova.js
|
||||
framework/assets/www/.tmp*
|
||||
local.properties
|
||||
framework/proguard.cfg
|
||||
framework/cordova.jar
|
||||
@@ -12,9 +13,19 @@ framework/test/org/apache/cordova/*.class
|
||||
framework/assets/www/.DS_Store
|
||||
framework/assets/www/cordova-*.js
|
||||
framework/assets/www/phonegap-*.js
|
||||
framework/libs
|
||||
example
|
||||
./test
|
||||
tmp
|
||||
test/bin
|
||||
test/assets/www/.tmp*
|
||||
tmp/**
|
||||
*.tmp
|
||||
test/libs/*.jar
|
||||
bin/node_modules
|
||||
.metadata
|
||||
*.bak
|
||||
tmp/**/*
|
||||
*.swp
|
||||
Thumbs.db
|
||||
Desktop.ini
|
||||
|
||||
|
||||
19
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/)
|
||||
|
||||
@@ -33,7 +33,7 @@ To create your cordova.jar, copy the commons codec:
|
||||
|
||||
then run in the framework directory:
|
||||
|
||||
android update project -p . -t android-15
|
||||
android update project -p . -t android-16
|
||||
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/)
|
||||
|
||||
21
bin/BOOM
@@ -1,21 +0,0 @@
|
||||
#! /bin/sh
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
#
|
||||
./bin/create
|
||||
cd ./example && ./cordova/debug && ./cordova/log
|
||||
135
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
|
||||
@@ -30,15 +30,15 @@ then
|
||||
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,70 +46,111 @@ fi
|
||||
|
||||
# cleanup after exit and/or on error
|
||||
function on_exit {
|
||||
echo "Cleaning up ..."
|
||||
# [ -f $BUILD_PATH/framework/libs/commons-codec-1.6.jar ] && rm $BUILD_PATH/framework/libs/commons-codec-1.6.jar
|
||||
# [ -d $BUILD_PATH/framework/libs ] && rmdir $BUILD_PATH/framework/libs
|
||||
[ -f $BUILD_PATH/framework/assets/www/cordova-$VERSION.js ] && rm $BUILD_PATH/framework/assets/www/cordova-$VERSION.js
|
||||
[ -f $BUILD_PATH/framework/cordova-$VERSION.jar ] && rm $BUILD_PATH/framework/cordova-$VERSION.jar
|
||||
# [ -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
|
||||
fi
|
||||
if [ -f "$BUILD_PATH"/framework/cordova-$VERSION.jar ]
|
||||
then
|
||||
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 error occurred. Deleting project..."
|
||||
[ -d "$PROJECT_PATH" ] && rm -rf "$PROJECT_PATH"
|
||||
}
|
||||
|
||||
function replace {
|
||||
local pattern=$1
|
||||
local filename=$2
|
||||
# Mac OS X requires -i argument
|
||||
if [[ "$OSTYPE" =~ "darwin" ]]
|
||||
then
|
||||
/usr/bin/sed -i '' -e $pattern "$filename"
|
||||
elif [[ "$OSTYPE" =~ "linux" ]]
|
||||
then
|
||||
/bin/sed -i -e $pattern "$filename"
|
||||
fi
|
||||
}
|
||||
|
||||
# we do not want the script to silently fail
|
||||
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 ]
|
||||
then
|
||||
# update the cordova-android framework for the desired target
|
||||
$ANDROID_BIN update project --target $TARGET --path $BUILD_PATH/framework &> /dev/null
|
||||
"$ANDROID_BIN" update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
|
||||
|
||||
if [ ! -e $BUILD_PATH/framework/libs/commons-codec-1.6.jar ]; then
|
||||
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
||||
echo "Downloading common-codecs-1.6 ..."
|
||||
curl -OL http://mirror.symnds.com/software/Apache//commons/codec/binaries/commons-codec-1.6-bin.zip &> /dev/null
|
||||
unzip commons-codec-1.6-bin.zip &> /dev/null
|
||||
mkdir -p $BUILD_PATH/framework/libs
|
||||
cp commons-codec-1.6/commons-codec-1.6.jar $BUILD_PATH/framework/libs
|
||||
# cleanup yo
|
||||
rm commons-codec-1.6-bin.zip && rm -rf commons-codec-1.6
|
||||
fi
|
||||
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
|
||||
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
||||
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.7-bin.zip && rm -rf commons-codec-1.7
|
||||
fi
|
||||
|
||||
# compile cordova.js and cordova.jar
|
||||
echo "Building cordova-$VERSION.jar and cordova-$VERSION.js ..."
|
||||
(cd $BUILD_PATH/framework && ant jar &> /dev/null )
|
||||
(cd "$BUILD_PATH"/framework && ant jar &> /dev/null )
|
||||
fi
|
||||
|
||||
# create new android project
|
||||
echo "Creating a new cordova android project ..."
|
||||
$ANDROID_BIN create project --target $TARGET --path $PROJECT_PATH --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
||||
"$ANDROID_BIN" create project --target $TARGET --path "$PROJECT_PATH" --package $PACKAGE --activity $ACTIVITY &> /dev/null
|
||||
|
||||
# copy project template
|
||||
echo "Copying assets and resources ..."
|
||||
cp -r $BUILD_PATH/bin/templates/project/assets $PROJECT_PATH
|
||||
cp -r $BUILD_PATH/bin/templates/project/res $PROJECT_PATH
|
||||
cp -r "$BUILD_PATH"/bin/templates/project/assets "$PROJECT_PATH"
|
||||
cp -r "$BUILD_PATH"/bin/templates/project/res "$PROJECT_PATH"
|
||||
|
||||
# copy cordova.js, cordova.jar and res/xml
|
||||
echo "Setting up config and plugins list ..."
|
||||
cp -r $BUILD_PATH/framework/res/xml $PROJECT_PATH/res
|
||||
|
||||
echo "Adding cordova-$VERSION.js and cordova-$VERSION.jar ..."
|
||||
cp $BUILD_PATH/framework/assets/www/cordova-$VERSION.js $PROJECT_PATH/assets/www/cordova-$VERSION.js
|
||||
cp $BUILD_PATH/framework/cordova-$VERSION.jar $PROJECT_PATH/libs/cordova-$VERSION.jar
|
||||
if [ -d "$BUILD_PATH"/framework ]
|
||||
then
|
||||
cp -r "$BUILD_PATH"/framework/res/xml "$PROJECT_PATH"/res
|
||||
cp "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js "$PROJECT_PATH"/assets/www/cordova-$VERSION.js
|
||||
cp "$BUILD_PATH"/framework/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||
else
|
||||
cp -r "$BUILD_PATH"/xml "$PROJECT_PATH"/res/xml
|
||||
cp "$BUILD_PATH"/cordova-$VERSION.js "$PROJECT_PATH"/assets/www/cordova-$VERSION.js
|
||||
cp "$BUILD_PATH"/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||
fi
|
||||
|
||||
# interpolate the activity name and package
|
||||
echo "Updating Activity and AndroidManifest ..."
|
||||
cp $BUILD_PATH/bin/templates/project/Activity.java $ACTIVITY_PATH
|
||||
sed -i '' -e "s/__ACTIVITY__/${ACTIVITY}/g" $ACTIVITY_PATH
|
||||
sed -i '' -e "s/__ID__/${PACKAGE}/g" $ACTIVITY_PATH
|
||||
cp "$BUILD_PATH"/bin/templates/project/Activity.java "$ACTIVITY_PATH"
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$ACTIVITY_PATH"
|
||||
replace "s/__ID__/${PACKAGE}/g" "$ACTIVITY_PATH"
|
||||
|
||||
cp $BUILD_PATH/bin/templates/project/AndroidManifest.xml $MANIFEST_PATH
|
||||
sed -i '' -e "s/__ACTIVITY__/${ACTIVITY}/g" $MANIFEST_PATH
|
||||
sed -i '' -e "s/__PACKAGE__/${PACKAGE}/g" $MANIFEST_PATH
|
||||
cp "$BUILD_PATH"/bin/templates/project/AndroidManifest.xml "$MANIFEST_PATH"
|
||||
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$MANIFEST_PATH"
|
||||
replace "s/__PACKAGE__/${PACKAGE}/g" "$MANIFEST_PATH"
|
||||
replace "s/__APILEVEL__/${API_LEVEL}/g" "$MANIFEST_PATH"
|
||||
|
||||
# creating cordova folder and copying emulate/debug/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/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
|
||||
|
||||
@@ -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:
|
||||
|
||||
111
bin/create.js
@@ -33,6 +33,14 @@ function read(filename) {
|
||||
f.Close();
|
||||
return s;
|
||||
}
|
||||
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);
|
||||
@@ -42,12 +50,27 @@ function write(filename, contents) {
|
||||
function replaceInFile(filename, regexp, replacement) {
|
||||
write(filename, read(filename).replace(regexp, replacement));
|
||||
}
|
||||
function exec(s, output) {
|
||||
var o=shell.Exec(s);
|
||||
while (o.Status == 0) {
|
||||
WScript.Sleep(100);
|
||||
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;
|
||||
}
|
||||
//WScript.Echo("Command exited with code " + o.Status);
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
@@ -65,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);
|
||||
@@ -96,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,38 +148,68 @@ if(fso.FolderExists(PROJECT_PATH)) {
|
||||
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=shell.Exec('android.bat list targets').StdOut.ReadAll().match(/id:\s([0-9]).*/)[1];
|
||||
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);
|
||||
|
||||
// update the cordova framework project to a target that exists on this machine
|
||||
exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
|
||||
|
||||
// pull down commons codec if necessary
|
||||
downloadCommonsCodec();
|
||||
|
||||
exec('ant.bat -f '+ ROOT +'\\framework\\build.xml jar');
|
||||
// build from source. distro should have these files
|
||||
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||
!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
|
||||
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');
|
||||
}
|
||||
|
||||
// copy in the project template
|
||||
exec('cmd /c xcopy '+ ROOT + '\\bin\\templates\\project\\* '+PROJECT_PATH+' /S /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');
|
||||
|
||||
// copy in cordova.js
|
||||
exec('cmd /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /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');
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
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');
|
||||
// copy in cordova.jar
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||
// copy in xml
|
||||
fso.CreateFolder(PROJECT_PATH + '\\res\\xml');
|
||||
exec('%comspec% /c copy "'+ROOT+'"\\xml\\config.xml ' + PROJECT_PATH + '\\res\\xml\\config.xml /Y');
|
||||
}
|
||||
|
||||
// copy in cordova.jar
|
||||
exec('cmd /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
||||
|
||||
// copy in xml
|
||||
exec('cmd /c copy '+ROOT+'\\framework\\res\\xml\\cordova.xml ' + PROJECT_PATH + '\\res\\xml\\cordova.xml /Y');
|
||||
exec('cmd /c copy '+ROOT+'\\framework\\res\\xml\\plugins.xml ' + PROJECT_PATH + '\\res\\xml\\plugins.xml /Y');
|
||||
// copy cordova scripts
|
||||
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||
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\\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');
|
||||
|
||||
// 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();
|
||||
|
||||
61
bin/templates/cordova/ApplicationInfo/ApplicationInfo.java
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import org.xml.sax.SAXException;
|
||||
import java.io.IOException;
|
||||
|
||||
public class ApplicationInfo {
|
||||
private static void parseAndroidManifest(String path) {
|
||||
// System.out.println(path);
|
||||
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
||||
Document dom;
|
||||
try {
|
||||
DocumentBuilder db = dbf.newDocumentBuilder();
|
||||
dom = db.parse(path);
|
||||
|
||||
// getting package information
|
||||
Element manifest = dom.getDocumentElement();
|
||||
String pakkage = manifest.getAttribute("package");
|
||||
|
||||
// getting activity name
|
||||
String activity = ((Element)dom.getElementsByTagName("activity").item(0)).getAttribute("android:name");
|
||||
System.out.println(String.format("%s/.%s", pakkage, activity.replace(".", "")));
|
||||
} catch(ParserConfigurationException pce) {
|
||||
pce.printStackTrace();
|
||||
} catch(SAXException se) {
|
||||
se.printStackTrace();
|
||||
} catch(IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String path;
|
||||
if(args.length > 0) {
|
||||
path = args[0];
|
||||
} else {
|
||||
path = System.getProperty("user.dir") + "/../AndroidManifest.xml";
|
||||
}
|
||||
parseAndroidManifest(path);
|
||||
}
|
||||
}
|
||||
24
bin/templates/cordova/BOOM
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 BOOM
|
||||
18
bin/templates/cordova/BOOM.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 BOOM
|
||||
24
bin/templates/cordova/clean
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 clean
|
||||
18
bin/templates/cordova/clean.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 clean
|
||||
102
bin/templates/cordova/cordova
Executable file
@@ -0,0 +1,102 @@
|
||||
# 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 }}'`
|
||||
if [ -z "$devices" ] ; then
|
||||
echo "1"
|
||||
else
|
||||
echo "0"
|
||||
fi
|
||||
}
|
||||
|
||||
function emulate {
|
||||
declare -a avd_list=($(android list avd | grep "Name:" | cut -f 2 -d ":" | xargs))
|
||||
# we need to start adb-server
|
||||
adb start-server 1>/dev/null
|
||||
|
||||
# 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
|
||||
|
||||
local avd_id="1000" #FIXME: hopefully user does not have 1000 AVDs
|
||||
# User has no AVDs
|
||||
if [ ${#avd_list[@]} == 0 ]
|
||||
then
|
||||
echo "You don't have any Android Virtual Devices. Please create at least one AVD."
|
||||
echo "android"
|
||||
fi
|
||||
# User has only one AVD
|
||||
if [ ${#avd_list[@]} == 1 ]
|
||||
then
|
||||
emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[0]} 1> /dev/null 2>&1 &
|
||||
# User has more than 1 AVD
|
||||
elif [ ${#avd_list[@]} -gt 1 ]
|
||||
then
|
||||
while [ -z ${avd_list[$avd_id]} ]
|
||||
do
|
||||
echo "Choose from one of the following Android Virtual Devices [0 to $((${#avd_list[@]}-1))]:"
|
||||
for(( i = 0 ; i < ${#avd_list[@]} ; i++ ))
|
||||
do
|
||||
echo "$i) ${avd_list[$i]}"
|
||||
done
|
||||
echo -n "> "
|
||||
read avd_id
|
||||
done
|
||||
emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function clean {
|
||||
ant clean
|
||||
}
|
||||
# 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
|
||||
else
|
||||
ant debug
|
||||
echo "##################################################################"
|
||||
echo "# Plug in your device or launch an emulator with cordova/emulate #"
|
||||
echo "##################################################################"
|
||||
fi
|
||||
}
|
||||
|
||||
function launch {
|
||||
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)
|
||||
32
bin/templates/cordova/cordova.bat
Normal file
@@ -0,0 +1,32 @@
|
||||
:: 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 (
|
||||
SET FOUND=%%~$PATH:X
|
||||
IF NOT DEFINED FOUND GOTO MISSING
|
||||
)
|
||||
cscript %~dp0\cordova.js %*
|
||||
GOTO END
|
||||
:MISSING
|
||||
ECHO Missing one of the following:
|
||||
ECHO JDK: http://java.oracle.com
|
||||
ECHO Android SDK: http://developer.android.com
|
||||
ECHO Apache ant: http://ant.apache.org
|
||||
EXIT /B 1
|
||||
:END
|
||||
121
bin/templates/cordova/cordova.js
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
// 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");
|
||||
|
||||
function exec(command) {
|
||||
var oExec=shell.Exec(command);
|
||||
var output = new String();
|
||||
while(oExec.Status == 0) {
|
||||
if(!oExec.StdOut.AtEndOfStream) {
|
||||
var line = oExec.StdOut.ReadLine();
|
||||
// XXX: Change to verbose mode
|
||||
// WScript.StdOut.WriteLine(line);
|
||||
output += line;
|
||||
}
|
||||
WScript.sleep(100);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
function emulator_running() {
|
||||
var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
|
||||
if(local_devices.match(/emulator/)) {
|
||||
return true;
|
||||
}
|
||||
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!");
|
||||
return;
|
||||
}
|
||||
var oExec = shell.Exec("%comspec% /c android.bat list avd");
|
||||
var avd_list = [];
|
||||
var avd_id = -10;
|
||||
while(!oExec.StdOut.AtEndOfStream) {
|
||||
var output = oExec.StdOut.ReadLine();
|
||||
if(output.match(/Name: (.)*/)) {
|
||||
avd_list.push(output.replace(/ *Name:\s/, ""));
|
||||
}
|
||||
}
|
||||
// user has no AVDs
|
||||
if(avd_list.length == 0) {
|
||||
WScript.Echo("You don't have any Android Virtual Devices. Please create at least one AVD.");
|
||||
WScript.Echo("android");
|
||||
WScript.Quit(1);
|
||||
}
|
||||
// user has only one AVD so we launch that one
|
||||
if(avd_list.length == 1) {
|
||||
|
||||
shell.Run("emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\cache -avd "+avd_list[0]);
|
||||
}
|
||||
|
||||
// user has more than one avd so we ask them to choose
|
||||
if(avd_list.length > 1) {
|
||||
while(!avd_list[avd_id]) {
|
||||
WScript.Echo("Choose from one of the following Android Virtual Devices [0 to "+(avd_list.length - 1)+"]:")
|
||||
for(i = 0, j = avd_list.length ; i < j ; i++) {
|
||||
WScript.Echo((i)+") "+avd_list[i]);
|
||||
}
|
||||
WScript.StdOut.Write("> ");
|
||||
avd_id = new Number(WScript.StdIn.ReadLine());
|
||||
}
|
||||
|
||||
shell.Run("emulator -cpu-delay 0 -no-boot-anim -cache %Temp%\\cache -avd "+avd_list[avd_id], 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
function clean() {
|
||||
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 log() {
|
||||
shell.Run("%comspec% /c adb logcat");
|
||||
}
|
||||
|
||||
function launch() {
|
||||
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() {
|
||||
clean();
|
||||
debug();
|
||||
launch();
|
||||
}
|
||||
var args = WScript.Arguments;
|
||||
if(args.count() != 1) {
|
||||
WScript.StdErr.Write("An error has occured!\n");
|
||||
WScript.Quit(1);
|
||||
}
|
||||
eval(args(0)+"()");
|
||||
24
bin/templates/cordova/debug
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 debug
|
||||
18
bin/templates/cordova/debug.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 debug
|
||||
24
bin/templates/cordova/emulate
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 emulate
|
||||
1
bin/templates/cordova/emulate.bat
Normal file
@@ -0,0 +1 @@
|
||||
%~dp0\cordova.bat emulate
|
||||
24
bin/templates/cordova/log
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
|
||||
|
||||
PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||
|
||||
bash $PROJECT_PATH/cordova/cordova log
|
||||
18
bin/templates/cordova/log.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 log
|
||||
@@ -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-1.8.0.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
<h1>Welcome to Cordova!</h1>
|
||||
<h2>this file is located at assets/www/index.html</h2>
|
||||
<div id="info">
|
||||
<h4>Platform: <span id="platform"> </span>, Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span>, Name: <span id="name"> </span></h4>
|
||||
<h4>Width: <span id="width"> </span>, Height: <span id="height">
|
||||
</span>, Color Depth: <span id="colorDepth"></span></h4>
|
||||
</div>
|
||||
<dl id="accel-data">
|
||||
<dt>X:</dt><dd id="x"> </dd>
|
||||
<dt>Y:</dt><dd id="y"> </dd>
|
||||
<dt>Z:</dt><dd id="z"> </dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="toggleAccel();">Toggle Accelerometer</a>
|
||||
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
|
||||
<a href="tel:411" class="btn large">Call 411</a>
|
||||
<a href="#" class="btn large" onclick="beep();">Beep</a>
|
||||
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
|
||||
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
|
||||
<a href="#" class="btn large" onclick="get_contacts();return false;">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();return false;">Check Network</a>
|
||||
<dl>
|
||||
<dt>Compass Heading:</dt><dd id="h">Off</dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="toggleCompass();return false;">Toggle Compass</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</body>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, 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.2.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
|
||||
@@ -80,6 +97,41 @@ create_project.on('exit', function(code) {
|
||||
path.exists(util.format('%s/assets/www/cordova-%s.js', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.js did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova master script was added
|
||||
path.exists(util.format('%s/cordova/cordova', project_path), function(exists) {
|
||||
assert(exists, 'cordova script did not get added');
|
||||
});
|
||||
|
||||
// make sure debug script was added
|
||||
path.exists(util.format('%s/cordova/debug', project_path), function(exists) {
|
||||
assert(exists, 'debug script did not get added');
|
||||
});
|
||||
|
||||
// make sure BOOM script was added
|
||||
path.exists(util.format('%s/cordova/BOOM', project_path), function(exists) {
|
||||
assert(exists, 'BOOM script did not get added');
|
||||
});
|
||||
|
||||
// make sure log script was added
|
||||
path.exists(util.format('%s/cordova/log', project_path), function(exists) {
|
||||
assert(exists, 'log script did not get added');
|
||||
});
|
||||
|
||||
// make sure clean script was added
|
||||
path.exists(util.format('%s/cordova/clean', project_path), function(exists) {
|
||||
assert(exists, 'clean script did not get added');
|
||||
});
|
||||
|
||||
// make sure emulate script was added
|
||||
path.exists(util.format('%s/cordova/emulate', project_path), function(exists) {
|
||||
assert(exists, 'emulate script did not get added');
|
||||
});
|
||||
|
||||
// make sure appinfo.jar script was added
|
||||
path.exists(util.format('%s/cordova/appinfo.jar', project_path), function(exists) {
|
||||
assert(exists, 'appinfo.jar script did not get added');
|
||||
});
|
||||
|
||||
// check that project compiles && creates a cordovaExample-debug.apk
|
||||
var compile_project = spawn('ant', ['debug'], {cwd: project_path});
|
||||
|
||||
155
bin/tests/test_create_win.js
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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',
|
||||
package_as_path = 'org/apache/cordova/example',
|
||||
project_name = 'cordovaExample';
|
||||
|
||||
var path = require('path'),
|
||||
fs = require('fs'),
|
||||
util = require('util'),
|
||||
assert = require('assert'),
|
||||
exec = require('child_process').exec,
|
||||
spawn = require('child_process').spawn;
|
||||
|
||||
var version = fs.readFileSync(build_path + '/VERSION').toString().replace('\r\n', '');
|
||||
|
||||
assert(version !== undefined);
|
||||
assert(version !== '');
|
||||
|
||||
process.on('uncaughtException', function (err) {
|
||||
console.log('Caught exception: ' + err);
|
||||
exec('rd /s /q ' + project_path);
|
||||
});
|
||||
|
||||
var create_project = spawn('cscript',
|
||||
[build_path + '/bin/create.js',
|
||||
project_path,
|
||||
package_name,
|
||||
project_name]
|
||||
);
|
||||
|
||||
create_project.stderr.on('data', function (data) {
|
||||
console.log('ps stderr: ' + data);
|
||||
});
|
||||
|
||||
create_project.stderr.on('data', function(data) {
|
||||
console.log(data.toString());
|
||||
});
|
||||
|
||||
create_project.stdout.on('data', function(data) {
|
||||
console.log(data.toString());
|
||||
});
|
||||
|
||||
create_project.on('exit', function(code) {
|
||||
assert.equal(code, 0, 'Project did not get created');
|
||||
|
||||
// make sure the project was created
|
||||
path.exists(project_path, function(exists) {
|
||||
assert(exists, 'Project path does not exist');
|
||||
});
|
||||
|
||||
// make sure the build directory was cleaned up
|
||||
// path.exists(build_path + '/framework/libs', function(exists) {
|
||||
// assert(!exists, 'libs directory did not get cleaned up');
|
||||
// });
|
||||
path.exists(build_path + util.format('/framework/assets/cordova-%s.js', version), function(exists) {
|
||||
assert(!exists, 'javascript file did not get cleaned up');
|
||||
});
|
||||
path.exists(build_path + util.format('/framework/cordova-%s.jar', version), function(exists) {
|
||||
assert(!exists, 'jar file did not get cleaned up');
|
||||
});
|
||||
|
||||
// make sure AndroidManifest.xml was added
|
||||
path.exists(util.format('%s/AndroidManifest.xml', project_path), function(exists) {
|
||||
assert(exists, 'AndroidManifest.xml did not get created');
|
||||
// TODO check that the activity name was properly substituted
|
||||
});
|
||||
|
||||
// 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 substituted properly
|
||||
});
|
||||
|
||||
// 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
|
||||
path.exists(util.format('%s/libs/cordova-%s.jar', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.jar did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova.js was added
|
||||
path.exists(util.format('%s/assets/www/cordova-%s.js', project_path, version), function(exists) {
|
||||
assert(exists, 'cordova.js did not get added');
|
||||
});
|
||||
|
||||
// make sure cordova master script was added
|
||||
path.exists(util.format('%s/cordova/cordova.bat', project_path), function(exists) {
|
||||
assert(exists, 'cordova script did not get added');
|
||||
});
|
||||
|
||||
// make sure debug script was added
|
||||
path.exists(util.format('%s/cordova/debug.bat', project_path), function(exists) {
|
||||
assert(exists, 'debug script did not get added');
|
||||
});
|
||||
|
||||
// make sure BOOM script was added
|
||||
path.exists(util.format('%s/cordova/BOOM.bat', project_path), function(exists) {
|
||||
assert(exists, 'BOOM script did not get added');
|
||||
});
|
||||
|
||||
// make sure log script was added
|
||||
path.exists(util.format('%s/cordova/log.bat', project_path), function(exists) {
|
||||
assert(exists, 'log script did not get added');
|
||||
});
|
||||
|
||||
// make sure clean script was added
|
||||
path.exists(util.format('%s/cordova/clean.bat', project_path), function(exists) {
|
||||
assert(exists, 'clean script did not get added');
|
||||
});
|
||||
|
||||
// make sure emulate script was added
|
||||
path.exists(util.format('%s/cordova/emulate.bat', project_path), function(exists) {
|
||||
assert(exists, 'emulate script did not get added');
|
||||
});
|
||||
|
||||
// make sure appinfo.jar script was added
|
||||
path.exists(util.format('%s/cordova/appinfo.jar', project_path), function(exists) {
|
||||
assert(exists, 'appinfo.jar script did not get added');
|
||||
});
|
||||
|
||||
// check that project compiles && creates a cordovaExample-debug.apk
|
||||
// XXX: !@##!@# WINDOWS
|
||||
exec('ant debug -f ' + project_path + "\\build.xml", function(error, stdout, stderr) {
|
||||
assert(error == null, "Cordova Android Project does not compile");
|
||||
path.exists(util.format('%s/bin/%s-debug.apk', project_path, project_name),
|
||||
function(exists) {
|
||||
assert(exists, 'Package did not get created');
|
||||
// if project compiles properly just AXE it
|
||||
exec('rd /s /q ' + project_path);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -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-1.8.0.js"></script>
|
||||
<script src="cordova-2.2.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,5 +10,7 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=Google Inc.:Google APIs:15
|
||||
target=Google Inc.:Google APIs:16
|
||||
apk-configurations=
|
||||
renderscript.opt.level=O0
|
||||
android.library=true
|
||||
|
||||
20
framework/res/xml/plugins.xml → framework/res/xml/config.xml
Executable file → Normal file
@@ -17,6 +17,21 @@
|
||||
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=".*"/>
|
||||
|
||||
<log level="DEBUG"/>
|
||||
<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"/>
|
||||
@@ -30,9 +45,12 @@
|
||||
<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"/>
|
||||
</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.
|
||||
@@ -68,30 +73,26 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The associated CordovaWebView.
|
||||
*/
|
||||
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||
@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.
|
||||
*
|
||||
@@ -215,7 +226,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
||||
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
|
||||
|
||||
// Save time that event was received
|
||||
this.timestamp = System.nanoTime();
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.x = event.values[0];
|
||||
this.y = event.values[1];
|
||||
this.z = event.values[2];
|
||||
@@ -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) {
|
||||
|
||||
@@ -48,8 +48,11 @@ public class App extends Plugin {
|
||||
if (action.equals("clearCache")) {
|
||||
this.clearCache();
|
||||
}
|
||||
else if (action.equals("show")) { // TODO @bc - Not in master branch. When should this be called?
|
||||
ctx.getActivity().runOnUiThread(new Runnable() {
|
||||
else if (action.equals("show")) {
|
||||
// This gets called from JavaScript onCordovaReady to show the webview.
|
||||
// I recommend we change the name of the Message as spinner/stop is not
|
||||
// indicative of what this actually does (shows the webview).
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.postMessage("spinner", "stop");
|
||||
}
|
||||
@@ -73,10 +76,6 @@ public class App extends Plugin {
|
||||
else if (action.equals("overrideBackbutton")) {
|
||||
this.overrideBackbutton(args.getBoolean(0));
|
||||
}
|
||||
else if (action.equals("isBackbuttonOverridden")) {
|
||||
boolean b = this.isBackbuttonOverridden();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("exitApp")) {
|
||||
this.exitApp();
|
||||
}
|
||||
@@ -162,7 +161,7 @@ public class App extends Plugin {
|
||||
*/
|
||||
@Deprecated
|
||||
public void cancelLoadUrl() {
|
||||
this.ctx.cancelLoadUrl();
|
||||
this.cordova.cancelLoadUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +176,11 @@ public class App extends Plugin {
|
||||
* This is the same as pressing the backbutton on Android device.
|
||||
*/
|
||||
public void backHistory() {
|
||||
this.webView.backHistory();
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
webView.backHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -202,13 +205,14 @@ public class App extends Plugin {
|
||||
LOG.i("DroidGap", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!");
|
||||
webView.bindButton(button, override);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the Android back button is overridden by the user.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isBackbuttonOverridden() {
|
||||
return this.ctx.isBackButtonBound();
|
||||
return webView.isBackButtonBound();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
136
framework/src/org/apache/cordova/AudioHandler.java
Executable file → Normal file
@@ -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,69 +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("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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,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.
|
||||
*
|
||||
@@ -149,7 +155,7 @@ public class AudioHandler extends Plugin {
|
||||
|
||||
// Get all audio players and pause them
|
||||
for (AudioPlayer audio : this.players.values()) {
|
||||
if (audio.getState() == AudioPlayer.MEDIA_RUNNING) {
|
||||
if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) {
|
||||
this.pausedForPhone.add(audio);
|
||||
audio.pausePlaying();
|
||||
}
|
||||
@@ -192,12 +198,11 @@ public class AudioHandler extends Plugin {
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecordingAudio(String id, String file) {
|
||||
// If already recording, then just return;
|
||||
if (this.players.containsKey(id)) {
|
||||
return;
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if ( audio == null) {
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
AudioPlayer audio = new AudioPlayer(this, id);
|
||||
this.players.put(id, audio);
|
||||
audio.startRecording(file);
|
||||
}
|
||||
|
||||
@@ -209,7 +214,6 @@ public class AudioHandler extends Plugin {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopRecording();
|
||||
this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +225,7 @@ public class AudioHandler extends Plugin {
|
||||
public void startPlayingAudio(String id, String file) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio == null) {
|
||||
audio = new AudioPlayer(this, id);
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
audio.startPlaying(file);
|
||||
@@ -230,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);
|
||||
@@ -292,7 +296,7 @@ public class AudioHandler extends Plugin {
|
||||
|
||||
// If not already open, then open the file
|
||||
else {
|
||||
audio = new AudioPlayer(this, id);
|
||||
audio = new AudioPlayer(this, id, file);
|
||||
this.players.put(id, audio);
|
||||
return (audio.getDuration(file));
|
||||
}
|
||||
@@ -305,7 +309,7 @@ public class AudioHandler extends Plugin {
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setAudioOutputDevice(int output) {
|
||||
AudioManager audiMgr = (AudioManager) this.ctx.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
||||
AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
||||
if (output == 2) {
|
||||
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
|
||||
}
|
||||
@@ -324,7 +328,7 @@ public class AudioHandler extends Plugin {
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public int getAudioOutputDevice() {
|
||||
AudioManager audiMgr = (AudioManager) this.ctx.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
||||
AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
418
framework/src/org/apache/cordova/AudioPlayer.java
Executable file → Normal file
@@ -42,14 +42,19 @@ import java.io.IOException;
|
||||
*/
|
||||
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
|
||||
|
||||
private static final String LOG_TAG = "AudioPlayer";
|
||||
// AudioPlayer modes
|
||||
public enum MODE { NONE, PLAY, RECORD };
|
||||
|
||||
// AudioPlayer states
|
||||
public static int MEDIA_NONE = 0;
|
||||
public static int MEDIA_STARTING = 1;
|
||||
public static int MEDIA_RUNNING = 2;
|
||||
public static int MEDIA_PAUSED = 3;
|
||||
public static int MEDIA_STOPPED = 4;
|
||||
public enum STATE { MEDIA_NONE,
|
||||
MEDIA_STARTING,
|
||||
MEDIA_RUNNING,
|
||||
MEDIA_PAUSED,
|
||||
MEDIA_STOPPED,
|
||||
MEDIA_LOADING
|
||||
};
|
||||
|
||||
private static final String LOG_TAG = "AudioPlayer";
|
||||
|
||||
// AudioPlayer message ids
|
||||
private static int MEDIA_STATE = 1;
|
||||
@@ -63,33 +68,40 @@ 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 int state = MEDIA_NONE; // State of recording or playback
|
||||
private String audioFile = null; // File name to play or record to
|
||||
private float duration = -1; // Duration of audio
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
|
||||
private MediaPlayer mPlayer = null; // Audio player object
|
||||
private boolean prepareOnly = false;
|
||||
private AudioHandler handler; // The AudioHandler object
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private MODE mode = MODE.NONE; // Playback or Recording mode
|
||||
private STATE state = STATE.MEDIA_NONE; // State of recording or playback
|
||||
|
||||
private String audioFile = null; // File name to play or record to
|
||||
private float duration = -1; // Duration of audio
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
|
||||
private MediaPlayer 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) {
|
||||
public AudioPlayer(AudioHandler handler, String id, String file) {
|
||||
this.handler = handler;
|
||||
this.id = id;
|
||||
this.audioFile = file;
|
||||
this.recorder = new MediaRecorder();
|
||||
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.3gp";
|
||||
} else {
|
||||
this.tempFile = "/data/data/" + handler.ctx.getActivity().getPackageName() + "/cache/tmprecording.mp3";
|
||||
this.tempFile = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording.3gp";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,13 +109,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
*/
|
||||
public void destroy() {
|
||||
// Stop any play or record
|
||||
if (this.mPlayer != null) {
|
||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
||||
this.mPlayer.stop();
|
||||
this.setState(MEDIA_STOPPED);
|
||||
if (this.player != null) {
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
this.player.stop();
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
this.mPlayer.release();
|
||||
this.mPlayer = null;
|
||||
this.player.release();
|
||||
this.player = null;
|
||||
}
|
||||
if (this.recorder != null) {
|
||||
this.stopRecording();
|
||||
@@ -114,18 +126,17 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
/**
|
||||
* Start recording the specified file.
|
||||
*
|
||||
*
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecording(String file) {
|
||||
if (this.mPlayer != null) {
|
||||
switch (this.mode) {
|
||||
case PLAY:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
// Make sure we're not already recording
|
||||
else if (this.recorder == null) {
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
break;
|
||||
case NONE:
|
||||
this.audioFile = file;
|
||||
this.recorder = new MediaRecorder();
|
||||
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
||||
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
||||
@@ -133,18 +144,18 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
try {
|
||||
this.recorder.prepare();
|
||||
this.recorder.start();
|
||||
this.setState(MEDIA_RUNNING);
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||
}
|
||||
else {
|
||||
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+"});");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +171,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||
+ File.separator + file));
|
||||
} else {
|
||||
f.renameTo(new File("/data/data/" + handler.ctx.getActivity().getPackageName() + "/cache/" + file));
|
||||
f.renameTo(new File("/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/" + file));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -171,10 +182,11 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void stopRecording() {
|
||||
if (this.recorder != null) {
|
||||
try{
|
||||
if (this.state == MEDIA_RUNNING) {
|
||||
if (this.state == STATE.MEDIA_RUNNING) {
|
||||
this.recorder.stop();
|
||||
this.setState(MEDIA_STOPPED);
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
this.recorder.reset();
|
||||
this.moveFile(this.audioFile);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -183,81 +195,22 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
// Playback
|
||||
//==========================================================================
|
||||
|
||||
/**
|
||||
* Start or resume playing audio file.
|
||||
*
|
||||
*
|
||||
* @param file The name of the audio file.
|
||||
*/
|
||||
public void startPlaying(String file) {
|
||||
if (this.recorder != null) {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
|
||||
// If this is a new request to play audio, or stopped
|
||||
else if ((this.mPlayer == null) || (this.state == MEDIA_STOPPED)) {
|
||||
try {
|
||||
// If stopped, then reset player
|
||||
if (this.mPlayer != null) {
|
||||
this.mPlayer.reset();
|
||||
}
|
||||
// Otherwise, create a new one
|
||||
else {
|
||||
this.mPlayer = new MediaPlayer();
|
||||
}
|
||||
this.audioFile = file;
|
||||
|
||||
// If streaming file
|
||||
if (this.isStreaming(file)) {
|
||||
this.mPlayer.setDataSource(file);
|
||||
this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
this.setState(MEDIA_STARTING);
|
||||
this.mPlayer.setOnPreparedListener(this);
|
||||
this.mPlayer.prepareAsync();
|
||||
}
|
||||
|
||||
// If local file
|
||||
else {
|
||||
if (file.startsWith("/android_asset/")) {
|
||||
String f = file.substring(15);
|
||||
android.content.res.AssetFileDescriptor fd = this.handler.ctx.getActivity().getAssets().openFd(f);
|
||||
this.mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
|
||||
}
|
||||
else {
|
||||
File fp = new File(file);
|
||||
if (fp.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
this.mPlayer.setDataSource(fileInputStream.getFD());
|
||||
}
|
||||
else {
|
||||
this.mPlayer.setDataSource("/sdcard/" + file);
|
||||
}
|
||||
}
|
||||
this.setState(MEDIA_STARTING);
|
||||
this.mPlayer.setOnPreparedListener(this);
|
||||
this.mPlayer.prepare();
|
||||
|
||||
// Get duration
|
||||
this.duration = getDurationInSeconds();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
}
|
||||
|
||||
// If we have already have created an audio player
|
||||
else {
|
||||
|
||||
// If player has been paused, then resume playback
|
||||
if ((this.state == MEDIA_PAUSED) || (this.state == MEDIA_STARTING)) {
|
||||
this.mPlayer.start();
|
||||
this.setState(MEDIA_RUNNING);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: " + this.state);
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
||||
}
|
||||
if (this.readyPlayer(file) && this.player != null) {
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //insures this is always reset
|
||||
} else {
|
||||
this.prepareOnly = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,10 +218,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
* Seek or jump to a new time in the track.
|
||||
*/
|
||||
public void seekToPlaying(int milliseconds) {
|
||||
if (this.mPlayer != null) {
|
||||
this.mPlayer.seekTo(milliseconds);
|
||||
if (this.readyPlayer(this.audioFile)) {
|
||||
this.player.seekTo(milliseconds);
|
||||
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + milliseconds / 1000.0f + ");");
|
||||
}
|
||||
else {
|
||||
this.seekOnPrepared = milliseconds;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,13 +234,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public void pausePlaying() {
|
||||
|
||||
// If playing, then pause
|
||||
if (this.state == MEDIA_RUNNING) {
|
||||
this.mPlayer.pause();
|
||||
this.setState(MEDIA_PAUSED);
|
||||
if (this.state == STATE.MEDIA_RUNNING && this.player != null) {
|
||||
this.player.pause();
|
||||
this.setState(STATE.MEDIA_PAUSED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state);
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state.ordinal());
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,34 +248,37 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
* Stop playing the audio file.
|
||||
*/
|
||||
public void stopPlaying() {
|
||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
||||
this.mPlayer.stop();
|
||||
this.setState(MEDIA_STOPPED);
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
this.player.pause();
|
||||
this.player.seekTo(0);
|
||||
Log.d(LOG_TAG, "stopPlaying is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state);
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state.ordinal());
|
||||
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 mPlayer The MediaPlayer that reached the end of the file
|
||||
*
|
||||
* @param player The MediaPlayer that reached the end of the file
|
||||
*/
|
||||
public void onCompletion(MediaPlayer mPlayer) {
|
||||
this.setState(MEDIA_STOPPED);
|
||||
public void onCompletion(MediaPlayer player) {
|
||||
Log.d(LOG_TAG, "on completion is calling stopped");
|
||||
this.setState(STATE.MEDIA_STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
*
|
||||
*
|
||||
* @return position in msec or -1 if not playing
|
||||
*/
|
||||
public long getCurrentPosition() {
|
||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
||||
int curPos = this.mPlayer.getCurrentPosition();
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
||||
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||
int curPos = this.player.getCurrentPosition();
|
||||
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
||||
return curPos;
|
||||
}
|
||||
else {
|
||||
@@ -330,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
|
||||
*/
|
||||
@@ -345,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
|
||||
@@ -359,7 +318,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
|
||||
// If audio file already loaded and started, then return duration
|
||||
if (this.mPlayer != null) {
|
||||
if (this.player != null) {
|
||||
return this.duration;
|
||||
}
|
||||
|
||||
@@ -375,82 +334,93 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the media source is ready for playback.
|
||||
*
|
||||
* @param mPlayer The MediaPlayer that is ready for playback
|
||||
* 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 mPlayer) {
|
||||
public void onPrepared(MediaPlayer player) {
|
||||
// Listen for playback completion
|
||||
this.mPlayer.setOnCompletionListener(this);
|
||||
|
||||
this.player.setOnCompletionListener(this);
|
||||
// seek to any location received while not prepared
|
||||
this.seekToPlaying(this.seekOnPrepared);
|
||||
// If start playing after prepared
|
||||
if (!this.prepareOnly) {
|
||||
|
||||
// Start playing
|
||||
this.mPlayer.start();
|
||||
|
||||
// Set player init flag
|
||||
this.setState(MEDIA_RUNNING);
|
||||
this.player.start();
|
||||
this.setState(STATE.MEDIA_RUNNING);
|
||||
this.seekOnPrepared = 0; //reset only when played
|
||||
} else {
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
}
|
||||
|
||||
// Save off duration
|
||||
this.duration = getDurationInSeconds();
|
||||
this.prepareOnly = false;
|
||||
// reset prepare only flag
|
||||
this.prepareOnly = true;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_DURATION + "," + this.duration + ");");
|
||||
|
||||
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() {
|
||||
return (this.mPlayer.getDuration() / 1000.0f);
|
||||
return (this.player.getDuration() / 1000.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||
* (other errors will throw exceptions at method call time).
|
||||
*
|
||||
* @param mPlayer the MediaPlayer the error pertains to
|
||||
*
|
||||
* @param player the MediaPlayer the error pertains to
|
||||
* @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED)
|
||||
* @param arg2 an extra code, specific to the error.
|
||||
*/
|
||||
public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) {
|
||||
public boolean onError(MediaPlayer player, int arg1, int arg2) {
|
||||
Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2 + ")");
|
||||
|
||||
// TODO: Not sure if this needs to be sent?
|
||||
this.mPlayer.stop();
|
||||
this.mPlayer.release();
|
||||
this.player.stop();
|
||||
this.player.release();
|
||||
|
||||
// Send error notification to JavaScript
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', { \"code\":" + arg1 + "});");
|
||||
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(int state) {
|
||||
private void setState(STATE state) {
|
||||
if (this.state != state) {
|
||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_STATE + ", " + state + ");");
|
||||
this.handler.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 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() {
|
||||
return this.state;
|
||||
return this.state.ordinal();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -459,6 +429,120 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
* @param volume
|
||||
*/
|
||||
public void setVolume(float volume) {
|
||||
this.mPlayer.setVolume(volume, volume);
|
||||
this.player.setVolume(volume, volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* attempts to put the player in play mode
|
||||
* @return true if in playmode, false otherwise
|
||||
*/
|
||||
private boolean playMode() {
|
||||
switch(this.mode) {
|
||||
case NONE:
|
||||
this.setMode(MODE.PLAY);
|
||||
break;
|
||||
case PLAY:
|
||||
break;
|
||||
case RECORD:
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
||||
this.handler.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
|
||||
* @return false if player not ready, reports if in wrong mode or state
|
||||
*/
|
||||
private boolean readyPlayer(String file) {
|
||||
if (playMode()) {
|
||||
switch (this.state) {
|
||||
case MEDIA_NONE:
|
||||
if (this.player == null) {
|
||||
this.player = new MediaPlayer();
|
||||
}
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
this.handler.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 instead
|
||||
Log.d(LOG_TAG, "AudioPlayer Loading: startPlaying() called during media preparation: " + STATE.MEDIA_STARTING.ordinal());
|
||||
this.prepareOnly = false;
|
||||
return false;
|
||||
case MEDIA_STARTING:
|
||||
case MEDIA_RUNNING:
|
||||
case MEDIA_PAUSED:
|
||||
return true;
|
||||
case MEDIA_STOPPED:
|
||||
//if we are readying the same file
|
||||
if (this.audioFile.compareTo(file) == 0) {
|
||||
//reset the audio file
|
||||
player.seekTo(0);
|
||||
player.pause();
|
||||
return true;
|
||||
} else {
|
||||
//reset the player
|
||||
this.player.reset();
|
||||
try {
|
||||
this.loadAudioFile(file);
|
||||
} catch (Exception e) {
|
||||
this.handler.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.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
|
||||
*/
|
||||
private void loadAudioFile(String file) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException {
|
||||
if (this.isStreaming(file)) {
|
||||
this.player.setDataSource(file);
|
||||
this.player.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
//if it's a streaming file, play mode is implied
|
||||
this.setMode(MODE.PLAY);
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
this.player.setOnPreparedListener(this);
|
||||
this.player.prepareAsync();
|
||||
}
|
||||
else {
|
||||
if (file.startsWith("/android_asset/")) {
|
||||
String f = file.substring(15);
|
||||
android.content.res.AssetFileDescriptor fd = this.handler.cordova.getActivity().getAssets().openFd(f);
|
||||
this.player.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
|
||||
}
|
||||
else {
|
||||
File fp = new File(file);
|
||||
if (fp.exists()) {
|
||||
FileInputStream fileInputStream = new FileInputStream(file);
|
||||
this.player.setDataSource(fileInputStream.getFD());
|
||||
}
|
||||
else {
|
||||
this.player.setDataSource("/sdcard/" + file);
|
||||
}
|
||||
}
|
||||
this.setState(STATE.MEDIA_STARTING);
|
||||
this.player.setOnPreparedListener(this);
|
||||
this.player.prepare();
|
||||
|
||||
// Get duration
|
||||
this.duration = getDurationInSeconds();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
@@ -73,23 +72,25 @@ public class BatteryListener extends Plugin {
|
||||
updateBatteryInfo(intent);
|
||||
}
|
||||
};
|
||||
ctx.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
}
|
||||
|
||||
// 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,13 +100,20 @@ public class BatteryListener extends Plugin {
|
||||
removeBatteryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop battery receiver.
|
||||
*/
|
||||
public void onReset() {
|
||||
removeBatteryListener();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the battery receiver and set it to null.
|
||||
*/
|
||||
private void removeBatteryListener() {
|
||||
if (this.receiver != null) {
|
||||
try {
|
||||
this.ctx.getActivity().unregisterReceiver(this.receiver);
|
||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||
this.receiver = null;
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering battery receiver: " + e.getMessage(), e);
|
||||
@@ -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 */
|
||||
}
|
||||
@@ -20,36 +20,41 @@ package org.apache.cordova;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
//import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.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 android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
//import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.media.MediaScannerConnection;
|
||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
* 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 {
|
||||
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)
|
||||
@@ -76,10 +81,16 @@ public class CameraLauncher extends Plugin {
|
||||
private Uri imageUri; // Uri of captured image
|
||||
private int encodingType; // Type of encoding to use
|
||||
private int mediaType; // What type of media to retrieve
|
||||
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
|
||||
private boolean correctOrientation; // Should the pictures orientation be corrected
|
||||
//private boolean allowEdit; // Should we allow the user to crop the image. UNUSED.
|
||||
|
||||
public String callbackId;
|
||||
public CallbackContext callbackContext;
|
||||
private int numPics;
|
||||
|
||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||
private Uri scanMe; // Uri of image to be added to content store
|
||||
|
||||
//This should never be null!
|
||||
//private CordovaInterface cordova;
|
||||
|
||||
@@ -100,49 +111,56 @@ public class CameraLauncher 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 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.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.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 (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;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -165,19 +183,18 @@ public class CameraLauncher extends Plugin {
|
||||
*/
|
||||
public void takePicture(int returnType, int encodingType) {
|
||||
// Save the number of images currently on disk for later
|
||||
this.numPics = queryImgDB().getCount();
|
||||
this.numPics = queryImgDB(whichContentStore()).getCount();
|
||||
|
||||
// Display camera
|
||||
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
// TODO: What if there isn't any external storage?
|
||||
File photo = createCaptureFile(encodingType);
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
if (this.ctx != null) {
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||
if (this.cordova != null) {
|
||||
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");
|
||||
@@ -192,9 +209,9 @@ public class CameraLauncher extends Plugin {
|
||||
private File createCaptureFile(int encodingType) {
|
||||
File photo = null;
|
||||
if (encodingType == JPEG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()), "Pic.jpg");
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.jpg");
|
||||
} else if (encodingType == PNG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()), "Pic.png");
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), ".Pic.png");
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
|
||||
}
|
||||
@@ -228,27 +245,347 @@ public class CameraLauncher extends Plugin {
|
||||
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
if (this.ctx != null) {
|
||||
this.ctx.startActivityForResult((Plugin) this, Intent.createChooser(intent,
|
||||
if (this.cordova != null) {
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
||||
new String(title)), (srcType + 1) * 16 + returnType + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the bitmap according to the requested size.
|
||||
* Called when the camera view exits.
|
||||
*
|
||||
* @param bitmap The bitmap to scale.
|
||||
* @return Bitmap A new Bitmap object of the same bitmap after scaling.
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
public Bitmap scaleBitmap(Bitmap bitmap) {
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Get src and dest types from request code
|
||||
int srcType = (requestCode / 16) - 1;
|
||||
int destType = (requestCode % 16) - 1;
|
||||
int rotate = 0;
|
||||
|
||||
// If CAMERA
|
||||
if (srcType == CAMERA) {
|
||||
// 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 (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
}
|
||||
|
||||
this.processPicture(bitmap);
|
||||
checkForDuplicateImage(DATA_URL);
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
if (!this.saveToPhotoAlbum) {
|
||||
uri = Uri.fromFile(new File(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()), System.currentTimeMillis() + ".jpg"));
|
||||
} else {
|
||||
uri = getUriFromMediaStore();
|
||||
}
|
||||
|
||||
if (uri == null) {
|
||||
this.failPicture("Error capturing image - no media storage found.");
|
||||
}
|
||||
|
||||
// If all this is true we shouldn't compress the image.
|
||||
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 && rotate == 0) {
|
||||
writeUncompressedImage(uri);
|
||||
|
||||
this.callbackContext.success(uri.toString());
|
||||
} else {
|
||||
bitmap = getScaledBitmap(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
|
||||
if (rotate != 0 && this.correctOrientation) {
|
||||
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
String exifPath;
|
||||
if (this.saveToPhotoAlbum) {
|
||||
exifPath = FileUtils.getRealPathFromURI(uri, this.cordova);
|
||||
} else {
|
||||
exifPath = uri.getPath();
|
||||
}
|
||||
exif.createOutFile(exifPath);
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
}
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
|
||||
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
|
||||
bitmap = null;
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error capturing image.");
|
||||
}
|
||||
}
|
||||
|
||||
// If cancelled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Camera cancelled.");
|
||||
}
|
||||
|
||||
// If something else
|
||||
else {
|
||||
this.failPicture("Did not complete!");
|
||||
}
|
||||
}
|
||||
|
||||
// If retrieving photo from library
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Uri uri = intent.getData();
|
||||
|
||||
// If you ask for video or all media type you will automatically get back a file URI
|
||||
// and there will be no attempt to resize any returned data
|
||||
if (this.mediaType != PICTURE) {
|
||||
this.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.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 };
|
||||
Cursor cursor = this.cordova.getActivity().getContentResolver().query(intent.getData(),
|
||||
cols, null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
}
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
this.processPicture(bitmap);
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
// Do we need to scale the returned file
|
||||
if (this.targetHeight > 0 && this.targetWidth > 0) {
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
String resizePath = DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/resize.jpg";
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(resizePath);
|
||||
exif.readExifData();
|
||||
rotate = exif.getOrientation();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
OutputStream os = new FileOutputStream(resizePath);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
// The resized image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.callbackContext.success("file://" + resizePath + "?" + System.currentTimeMillis());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.callbackContext.success(uri.toString());
|
||||
}
|
||||
}
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
}
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Selection cancelled.");
|
||||
}
|
||||
else {
|
||||
this.failPicture("Selection did not complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Figure out if the bitmap should be rotated. For instance if the picture was taken in
|
||||
* portrait mode
|
||||
*
|
||||
* @param rotate
|
||||
* @param bitmap
|
||||
* @return rotated bitmap
|
||||
*/
|
||||
private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
|
||||
Matrix matrix = new Matrix();
|
||||
if (rotate == 180) {
|
||||
matrix.setRotate(rotate);
|
||||
} else {
|
||||
matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
|
||||
}
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
exif.resetOrientation();
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the special case where the default width, height and quality are unchanged
|
||||
* we just write the file out to disk saving the expensive Bitmap.compress function.
|
||||
*
|
||||
* @param uri
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
|
||||
IOException {
|
||||
FileInputStream fis = new FileInputStream(FileUtils.stripFileProtocol(imageUri.toString()));
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
fis.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create entry in media store for image
|
||||
*
|
||||
* @return uri
|
||||
*/
|
||||
private Uri getUriFromMediaStore() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri uri;
|
||||
try {
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a scaled bitmap based on the target width and height
|
||||
*
|
||||
* @param imagePath
|
||||
* @return
|
||||
*/
|
||||
private Bitmap getScaledBitmap(String imagePath) {
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
|
||||
return BitmapFactory.decodeFile(imagePath);
|
||||
}
|
||||
|
||||
// figure out the original width and height of the image
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
// determine the correct aspect ratio
|
||||
int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
|
||||
|
||||
// Load in the smallest bitmap possible that is closest to the size we want
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
|
||||
Bitmap unscaledBitmap = BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maintain the aspect ratio so the resulting image does not look smooshed
|
||||
*
|
||||
* @param origWidth
|
||||
* @param origHeight
|
||||
* @return
|
||||
*/
|
||||
public int[] calculateAspectRatio(int origWidth, int origHeight) {
|
||||
int newWidth = this.targetWidth;
|
||||
int newHeight = this.targetHeight;
|
||||
int origWidth = bitmap.getWidth();
|
||||
int origHeight = bitmap.getHeight();
|
||||
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (newWidth <= 0 && newHeight <= 0) {
|
||||
return bitmap;
|
||||
newWidth = origWidth;
|
||||
newHeight = origHeight;
|
||||
}
|
||||
// Only the width was specified
|
||||
else if (newWidth > 0 && newHeight <= 0) {
|
||||
@@ -275,216 +612,68 @@ public class CameraLauncher extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
Bitmap retval = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
|
||||
bitmap.recycle();
|
||||
int[] retval = new int[2];
|
||||
retval[0] = newWidth;
|
||||
retval[1] = newHeight;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the camera view exits.
|
||||
* Figure out what ratio we can load our image into memory at while still being bigger than
|
||||
* our desired width and height
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @param srcWidth
|
||||
* @param srcHeight
|
||||
* @param dstWidth
|
||||
* @param dstHeight
|
||||
* @return
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
|
||||
final float srcAspect = (float)srcWidth / (float)srcHeight;
|
||||
final float dstAspect = (float)dstWidth / (float)dstHeight;
|
||||
|
||||
// Get src and dest types from request code
|
||||
int srcType = (requestCode / 16) - 1;
|
||||
int destType = (requestCode % 16) - 1;
|
||||
int rotate = 0;
|
||||
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
try {
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()) + "/Pic.jpg");
|
||||
exif.readExifData();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
if (srcAspect > dstAspect) {
|
||||
return srcWidth / dstWidth;
|
||||
} else {
|
||||
return srcHeight / dstHeight;
|
||||
}
|
||||
// If CAMERA
|
||||
if (srcType == CAMERA) {
|
||||
// If image available
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getActivity().getContentResolver(), imageUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Uri uri = intent.getData();
|
||||
android.content.ContentResolver resolver = this.ctx.getActivity().getContentResolver();
|
||||
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
}
|
||||
|
||||
bitmap = scaleBitmap(bitmap);
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
this.processPicture(bitmap);
|
||||
checkForDuplicateImage(DATA_URL);
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
// Create entry in media store for image
|
||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri uri = null;
|
||||
try {
|
||||
uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
this.failPicture("Error capturing image - no media storage found.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.ctx.getActivity().getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx));
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
|
||||
checkForDuplicateImage(FILE_URI);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error capturing image.");
|
||||
}
|
||||
}
|
||||
|
||||
// If cancelled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Camera cancelled.");
|
||||
}
|
||||
|
||||
// If something else
|
||||
else {
|
||||
this.failPicture("Did not complete!");
|
||||
}
|
||||
}
|
||||
|
||||
// If retrieving photo from library
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
Uri uri = intent.getData();
|
||||
android.content.ContentResolver resolver = this.ctx.getActivity().getContentResolver();
|
||||
|
||||
// If you ask for video or all media type you will automatically get back a file URI
|
||||
// and there will be no attempt to resize any returned data
|
||||
if (this.mediaType != PICTURE) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
else {
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
try {
|
||||
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
String[] cols = { MediaStore.Images.Media.ORIENTATION };
|
||||
Cursor cursor = this.ctx.getActivity().getContentResolver().query(intent.getData(),
|
||||
cols,
|
||||
null, null, null);
|
||||
if (cursor != null) {
|
||||
cursor.moveToPosition(0);
|
||||
rotate = cursor.getInt(0);
|
||||
cursor.close();
|
||||
}
|
||||
if (rotate != 0) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(rotate);
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
}
|
||||
bitmap = scaleBitmap(bitmap);
|
||||
this.processPicture(bitmap);
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
}
|
||||
}
|
||||
|
||||
// If sending filename back
|
||||
else if (destType == FILE_URI) {
|
||||
// Do we need to scale the returned file
|
||||
if (this.targetHeight > 0 && this.targetWidth > 0) {
|
||||
try {
|
||||
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
bitmap = scaleBitmap(bitmap);
|
||||
|
||||
String fileName = DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()) + "/resize.jpg";
|
||||
OutputStream os = new FileOutputStream(fileName);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// Restore exif data to file
|
||||
if (this.encodingType == JPEG) {
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx));
|
||||
exif.writeExifData();
|
||||
}
|
||||
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
|
||||
// The resized image is cached by the app in order to get around this and not have to delete you
|
||||
// application cache I'm adding the current system time to the end of the file url.
|
||||
this.success(new PluginResult(PluginResult.Status.OK, ("file://" + fileName + "?" + System.currentTimeMillis())), this.callbackId);
|
||||
System.gc();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
this.failPicture("Selection cancelled.");
|
||||
}
|
||||
else {
|
||||
this.failPicture("Selection did not complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cursor that can be used to determine how many images we have.
|
||||
*
|
||||
* @return a cursor
|
||||
*/
|
||||
private Cursor queryImgDB() {
|
||||
return this.ctx.getActivity().getContentResolver().query(
|
||||
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||
private Cursor queryImgDB(Uri contentStore) {
|
||||
return this.cordova.getActivity().getContentResolver().query(
|
||||
contentStore,
|
||||
new String[] { MediaStore.Images.Media._ID },
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
|
||||
* @param newImage
|
||||
*/
|
||||
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
|
||||
if (bitmap != null) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
|
||||
// Clean up initial camera-written image file.
|
||||
(new File(FileUtils.stripFileProtocol(oldImage.toString()))).delete();
|
||||
|
||||
checkForDuplicateImage(imageType);
|
||||
// Scan for the gallery to update pic refs in gallery
|
||||
if (this.saveToPhotoAlbum && newImage != null) {
|
||||
this.scanForGallery(newImage);
|
||||
}
|
||||
|
||||
System.gc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to find out if we are in a situation where the Camera Intent adds to images
|
||||
* to the content store. If we are using a FILE_URI and the number of images in the DB
|
||||
@@ -494,19 +683,35 @@ public class CameraLauncher extends Plugin {
|
||||
*/
|
||||
private void checkForDuplicateImage(int type) {
|
||||
int diff = 1;
|
||||
Cursor cursor = queryImgDB();
|
||||
Uri contentStore = whichContentStore();
|
||||
Cursor cursor = queryImgDB(contentStore);
|
||||
int currentNumOfImages = cursor.getCount();
|
||||
|
||||
if (type == FILE_URI) {
|
||||
if (type == FILE_URI && this.saveToPhotoAlbum) {
|
||||
diff = 2;
|
||||
}
|
||||
|
||||
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
|
||||
if ((currentNumOfImages - numPics) == diff) {
|
||||
cursor.moveToLast();
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
|
||||
Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + id);
|
||||
this.ctx.getActivity().getContentResolver().delete(uri, null, null);
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
|
||||
if (diff == 2) {
|
||||
id--;
|
||||
}
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are storing the images in internal or external storage
|
||||
* @return Uri
|
||||
*/
|
||||
private Uri whichContentStore() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else {
|
||||
return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,7 +727,7 @@ public class CameraLauncher extends Plugin {
|
||||
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;
|
||||
@@ -539,6 +744,28 @@ public class CameraLauncher extends Plugin {
|
||||
* @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) {
|
||||
this.scanMe = newImage;
|
||||
if(this.conn != null) {
|
||||
this.conn.disconnect();
|
||||
}
|
||||
this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
|
||||
conn.connect();
|
||||
}
|
||||
|
||||
public void onMediaScannerConnected() {
|
||||
try{
|
||||
this.conn.scanFile(this.scanMe.toString(), "image/*");
|
||||
} catch (java.lang.IllegalStateException e){
|
||||
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void onScanCompleted(String path, Uri uri) {
|
||||
this.conn.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,11 +19,13 @@
|
||||
package org.apache.cordova;
|
||||
|
||||
import java.io.File;
|
||||
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;
|
||||
@@ -32,13 +34,15 @@ import org.json.JSONObject;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
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";
|
||||
@@ -54,13 +58,12 @@ 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;
|
||||
|
||||
@@ -73,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();
|
||||
@@ -86,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();
|
||||
@@ -102,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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,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("")) {
|
||||
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;
|
||||
}
|
||||
@@ -195,21 +192,23 @@ public class Capture extends Plugin {
|
||||
private void captureAudio() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture images. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureImage() {
|
||||
// Save the number of images currently on disk for later
|
||||
this.numPics = queryImgDB(whichContentStore()).getCount();
|
||||
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = new File(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()), "Capture.jpg");
|
||||
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.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +219,7 @@ public class Capture extends Plugin {
|
||||
// Introduced in API 8
|
||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO);
|
||||
this.cordova.startActivityForResult((CordovaPlugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -245,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();
|
||||
@@ -256,51 +255,42 @@ public class Capture extends Plugin {
|
||||
// It crashes in the emulator and on my phone with a null pointer exception
|
||||
// To work around it I had to grab the code from CameraLauncher.java
|
||||
try {
|
||||
// Create an ExifHelper to save the exif data that is lost during compression
|
||||
ExifHelper exif = new ExifHelper();
|
||||
exif.createInFile(DirectoryManager.getTempDirectoryPath(this.ctx.getActivity()) + "/Capture.jpg");
|
||||
exif.readExifData();
|
||||
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getActivity().getContentResolver(), imageUri);
|
||||
|
||||
// Create entry in media store for image
|
||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, IMAGE_JPEG);
|
||||
Uri uri = null;
|
||||
try {
|
||||
uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.ctx.getActivity().getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
|
||||
FileInputStream fis = new FileInputStream(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
|
||||
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||
byte[] buffer = new byte[4096];
|
||||
int len;
|
||||
while ((len = fis.read(buffer)) != -1) {
|
||||
os.write(buffer, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
os.close();
|
||||
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
|
||||
// Restore exif data to file
|
||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx));
|
||||
exif.writeExifData();
|
||||
fis.close();
|
||||
|
||||
// Add image to results
|
||||
results.put(createMediaFile(uri));
|
||||
|
||||
checkForDuplicateImage();
|
||||
|
||||
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();
|
||||
@@ -317,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);
|
||||
@@ -328,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 {
|
||||
@@ -339,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 {
|
||||
@@ -356,7 +346,7 @@ public class Capture extends Plugin {
|
||||
* @throws IOException
|
||||
*/
|
||||
private JSONObject createMediaFile(Uri data) {
|
||||
File fp = new File(FileUtils.getRealPathFromURI(data, this.ctx));
|
||||
File fp = new File(FileUtils.getRealPathFromURI(data, this.cordova));
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
@@ -403,6 +393,51 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a cursor that can be used to determine how many images we have.
|
||||
*
|
||||
* @return a cursor
|
||||
*/
|
||||
private Cursor queryImgDB(Uri contentStore) {
|
||||
return this.cordova.getActivity().getContentResolver().query(
|
||||
contentStore,
|
||||
new String[] { MediaStore.Images.Media._ID },
|
||||
null,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to find out if we are in a situation where the Camera Intent adds to images
|
||||
* to the content store.
|
||||
*/
|
||||
private void checkForDuplicateImage() {
|
||||
Uri contentStore = whichContentStore();
|
||||
Cursor cursor = queryImgDB(contentStore);
|
||||
int currentNumOfImages = cursor.getCount();
|
||||
|
||||
// delete the duplicate file if the difference is 2
|
||||
if ((currentNumOfImages - numPics) == 2) {
|
||||
cursor.moveToLast();
|
||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
|
||||
Uri uri = Uri.parse(contentStore + "/" + id);
|
||||
this.cordova.getActivity().getContentResolver().delete(uri, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we are storing the images in internal or external storage
|
||||
* @return Uri
|
||||
*/
|
||||
private Uri whichContentStore() {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||
} else {
|
||||
return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
@@ -67,96 +73,63 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -69,33 +68,55 @@ public class ContactManager extends Plugin {
|
||||
* older phones.
|
||||
*/
|
||||
if (this.contactAccessor == null) {
|
||||
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.ctx);
|
||||
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.
|
||||
@@ -45,16 +49,19 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
|
||||
private String TAG = "CordovaLog";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
private CordovaInterface ctx;
|
||||
private CordovaInterface cordova;
|
||||
private CordovaWebView appView;
|
||||
|
||||
// the video progress view
|
||||
private View mVideoProgressView;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
* @param cordova
|
||||
*/
|
||||
public CordovaChromeClient(CordovaInterface ctx) {
|
||||
this.ctx = ctx;
|
||||
public CordovaChromeClient(CordovaInterface cordova) {
|
||||
this.cordova = cordova;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,7 +71,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
* @param app
|
||||
*/
|
||||
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
||||
this.ctx = ctx;
|
||||
this.cordova = ctx;
|
||||
this.appView = app;
|
||||
}
|
||||
|
||||
@@ -87,7 +94,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx.getActivity());
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle("Alert");
|
||||
//Don't let alerts break the back button
|
||||
@@ -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() {
|
||||
@@ -131,7 +138,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx.getActivity());
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle("Confirm");
|
||||
dlg.setCancelable(true);
|
||||
@@ -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,30 +234,12 @@ 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;
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx.getActivity());
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.cordova.getActivity());
|
||||
dlg.setMessage(message);
|
||||
final EditText input = new EditText(this.ctx.getActivity());
|
||||
final EditText input = new EditText(this.cordova.getActivity());
|
||||
if (defaultValue != null) {
|
||||
input.setText(defaultValue);
|
||||
}
|
||||
@@ -310,6 +304,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||
}
|
||||
|
||||
@TargetApi(8)
|
||||
@Override
|
||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
||||
{
|
||||
@@ -329,4 +324,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();
|
||||
}
|
||||
|
||||
@@ -30,20 +30,33 @@ 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.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 {
|
||||
|
||||
@@ -54,43 +67,58 @@ public class CordovaWebView extends WebView {
|
||||
private HashMap<String, Boolean> whiteListCache = new HashMap<String, Boolean>();
|
||||
private ArrayList<Integer> keyDownCodes = new ArrayList<Integer>();
|
||||
private ArrayList<Integer> keyUpCodes = new ArrayList<Integer>();
|
||||
|
||||
public PluginManager pluginManager;
|
||||
public CallbackServer callbackServer;
|
||||
|
||||
|
||||
/** Actvities and other important classes **/
|
||||
private CordovaInterface mCtx;
|
||||
public PluginManager pluginManager;
|
||||
private boolean paused;
|
||||
|
||||
private BroadcastReceiver receiver;
|
||||
|
||||
|
||||
/** 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.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public CordovaWebView(Context context) {
|
||||
super(context);
|
||||
if (CordovaInterface.class.isInstance(context))
|
||||
{
|
||||
this.mCtx = (CordovaInterface) context;
|
||||
this.cordova = (CordovaInterface) context;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -102,7 +130,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param attrs
|
||||
*/
|
||||
@@ -110,72 +138,84 @@ public class CordovaWebView extends WebView {
|
||||
super(context, attrs);
|
||||
if (CordovaInterface.class.isInstance(context))
|
||||
{
|
||||
this.mCtx = (CordovaInterface) context;
|
||||
this.cordova = (CordovaInterface) context;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
||||
}
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.mCtx, this));
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.mCtx, this));
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova, this));
|
||||
this.initWebViewClient(this.cordova);
|
||||
this.loadConfiguration();
|
||||
this.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param attrs
|
||||
* @param defStyle
|
||||
*
|
||||
*
|
||||
*/
|
||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
if (CordovaInterface.class.isInstance(context))
|
||||
{
|
||||
this.mCtx = (CordovaInterface) context;
|
||||
this.cordova = (CordovaInterface) context;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
||||
}
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.mCtx, this));
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.mCtx, this));
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.cordova, this));
|
||||
this.loadConfiguration();
|
||||
this.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
*
|
||||
* @param context
|
||||
* @param attrs
|
||||
* @param defStyle
|
||||
* @param privateBrowsing
|
||||
*/
|
||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) {
|
||||
@TargetApi(11)
|
||||
public CordovaWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing) {
|
||||
super(context, attrs, defStyle, privateBrowsing);
|
||||
if (CordovaInterface.class.isInstance(context))
|
||||
{
|
||||
this.mCtx = (CordovaInterface) context;
|
||||
this.cordova = (CordovaInterface) context;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(TAG, "Your activity must implement CordovaInterface to work");
|
||||
}
|
||||
this.setWebChromeClient(new CordovaChromeClient(this.mCtx));
|
||||
this.setWebViewClient(new CordovaWebViewClient(this.mCtx));
|
||||
this.setWebChromeClient(new CordovaChromeClient(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();
|
||||
@@ -186,12 +226,17 @@ public class CordovaWebView extends WebView {
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
|
||||
|
||||
//Set the nav dump for HTC
|
||||
settings.setNavDump(true);
|
||||
|
||||
//Set the nav dump for HTC 2.x devices (disabling for ICS/Jellybean)
|
||||
if(android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.HONEYCOMB)
|
||||
settings.setNavDump(true);
|
||||
|
||||
// Jellybean rightfully tried to lock this down. Too bad they didn't give us a whitelist
|
||||
// while we do this
|
||||
if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)
|
||||
Level16Apis.enableUniversalAccess(settings);
|
||||
// Enable database
|
||||
settings.setDatabaseEnabled(true);
|
||||
String databasePath = this.mCtx.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
String databasePath = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabasePath(databasePath);
|
||||
|
||||
// Enable DOM storage
|
||||
@@ -199,19 +244,52 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
// Enable built-in geolocation
|
||||
settings.setGeolocationEnabled(true);
|
||||
|
||||
//Start up the plugin manager
|
||||
try {
|
||||
this.pluginManager = new PluginManager(this, this.mCtx);
|
||||
} 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() {
|
||||
if (Build.VERSION.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 (Build.VERSION.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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the WebViewClient.
|
||||
*
|
||||
*
|
||||
* @param client
|
||||
*/
|
||||
public void setWebViewClient(CordovaWebViewClient client) {
|
||||
@@ -221,7 +299,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Set the WebChromeClient.
|
||||
*
|
||||
*
|
||||
* @param client
|
||||
*/
|
||||
public void setWebChromeClient(CordovaChromeClient client) {
|
||||
@@ -231,7 +309,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Add entry to approved list of URLs (whitelist)
|
||||
*
|
||||
*
|
||||
* @param origin URL regular expression to allow
|
||||
* @param subdomains T=include all subdomains under origin
|
||||
*/
|
||||
@@ -269,7 +347,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Determine if URL is in approved list of URLs to load.
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
* @return
|
||||
*/
|
||||
@@ -297,7 +375,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
@Override
|
||||
@@ -323,7 +401,7 @@ public class CordovaWebView extends WebView {
|
||||
/**
|
||||
* Load the url into the webview after waiting for period of time.
|
||||
* This is used to display the splashscreen for certain amount of time.
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
* @param time The number of ms to wait before loading webview
|
||||
*/
|
||||
@@ -342,7 +420,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void loadUrlIntoView(final String url) {
|
||||
@@ -394,13 +472,13 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
// If timeout, then stop loading and handle error
|
||||
if (me.loadUrlTimeout == currentLoadUrlTimeout) {
|
||||
me.mCtx.getActivity().runOnUiThread(loadError);
|
||||
me.cordova.getActivity().runOnUiThread(loadError);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Load url
|
||||
this.mCtx.getActivity().runOnUiThread(new Runnable() {
|
||||
this.cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
Thread thread = new Thread(timeoutCheck);
|
||||
thread.start();
|
||||
@@ -411,18 +489,22 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Load URL in 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview after waiting for period of time.
|
||||
* This is used to display the splashscreen for certain amount of time.
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
* @param time The number of ms to wait before loading webview
|
||||
*/
|
||||
@@ -445,22 +527,30 @@ public class CordovaWebView extends WebView {
|
||||
// Load url
|
||||
this.loadUrlIntoView(url);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
* (This is a convenience method)
|
||||
*
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.sendJavascript(statement);
|
||||
}
|
||||
this.jsMessageQueue.addJavaScript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to all plugins.
|
||||
*
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to all plugins.
|
||||
*
|
||||
* @param id The message id
|
||||
* @param data The message data
|
||||
*/
|
||||
@@ -470,8 +560,8 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the top url on the stack without removing it from
|
||||
/**
|
||||
* Returns the top url on the stack without removing it from
|
||||
* the stack.
|
||||
*/
|
||||
public String peekAtUrlStack() {
|
||||
@@ -483,7 +573,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Add a url to the stack
|
||||
*
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void pushUrl(String url) {
|
||||
@@ -492,7 +582,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Go to previous page in history. (We manage our own history)
|
||||
*
|
||||
*
|
||||
* @return true if we went back, false if we are already at top
|
||||
*/
|
||||
public boolean backHistory() {
|
||||
@@ -500,12 +590,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);
|
||||
@@ -517,7 +609,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Return true if there is a history item.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean canGoBack() {
|
||||
@@ -532,7 +624,7 @@ public class CordovaWebView extends WebView {
|
||||
|
||||
/**
|
||||
* Load the specified URL in the Cordova webview or a new browser instance.
|
||||
*
|
||||
*
|
||||
* NOTE: If openExternal is false, only URLs listed in whitelist can be loaded.
|
||||
*
|
||||
* @param url The url to load.
|
||||
@@ -569,7 +661,7 @@ public class CordovaWebView extends WebView {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
mCtx.getActivity().startActivity(intent);
|
||||
cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading url " + url, e);
|
||||
}
|
||||
@@ -581,7 +673,7 @@ public class CordovaWebView extends WebView {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
mCtx.getActivity().startActivity(intent);
|
||||
cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading url " + url, e);
|
||||
}
|
||||
@@ -596,7 +688,12 @@ public class CordovaWebView extends WebView {
|
||||
* <log level="DEBUG" />
|
||||
*/
|
||||
private void loadConfiguration() {
|
||||
int id = getResources().getIdentifier("cordova", "xml", this.mCtx.getActivity().getPackageName());
|
||||
int id = getResources().getIdentifier("config", "xml", this.cordova.getActivity().getPackageName());
|
||||
if(id == 0)
|
||||
{
|
||||
id = getResources().getIdentifier("cordova", "xml", this.cordova.getActivity().getPackageName());
|
||||
Log.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
|
||||
}
|
||||
if (id == 0) {
|
||||
LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
|
||||
return;
|
||||
@@ -625,9 +722,10 @@ public class CordovaWebView extends WebView {
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
|
||||
LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
|
||||
Log.d("CordovaLog", "Found preference for " + name + "=" + value);
|
||||
|
||||
// Save preferences in Intent
|
||||
this.mCtx.getActivity().getIntent().putExtra(name, value);
|
||||
this.cordova.getActivity().getIntent().putExtra(name, value);
|
||||
}
|
||||
}
|
||||
try {
|
||||
@@ -639,29 +737,28 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
}
|
||||
|
||||
// Init preferences
|
||||
if ("true".equals(this.getProperty("useBrowserHistory", "true"))) {
|
||||
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.mCtx.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
this.mCtx.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
this.cordova.getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
this.cordova.getActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get string property for activity.
|
||||
*
|
||||
*
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
* @return
|
||||
*/
|
||||
public String getProperty(String name, String defaultValue) {
|
||||
Bundle bundle = this.mCtx.getActivity().getIntent().getExtras();
|
||||
Bundle bundle = this.cordova.getActivity().getIntent().getExtras();
|
||||
if (bundle == null) {
|
||||
return defaultValue;
|
||||
}
|
||||
@@ -671,18 +768,17 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
return p.toString();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* onKeyDown
|
||||
* onKeyDown
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
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;
|
||||
@@ -695,40 +791,58 @@ public class CordovaWebView extends WebView {
|
||||
}
|
||||
else
|
||||
{
|
||||
//Do some other stuff!
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
// A custom view is currently displayed (e.g. playing a video)
|
||||
if(mCustomView != null) {
|
||||
this.hideCustomView();
|
||||
} else {
|
||||
// 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;
|
||||
} 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
|
||||
@@ -739,18 +853,19 @@ public class CordovaWebView extends WebView {
|
||||
else if(keyUpCodes.contains(keyCode))
|
||||
{
|
||||
//What the hell should this do?
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
Log.d(TAG, "KeyUp has been triggered on the view");
|
||||
return false;
|
||||
//Does webkit change this behavior?
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
public void bindButton(boolean override)
|
||||
{
|
||||
this.bound = override;
|
||||
}
|
||||
|
||||
|
||||
public void bindButton(String button, boolean override) {
|
||||
// TODO Auto-generated method stub
|
||||
if (button.compareTo("volumeup")==0) {
|
||||
@@ -760,7 +875,7 @@ public class CordovaWebView extends WebView {
|
||||
keyDownCodes.add(KeyEvent.KEYCODE_VOLUME_DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void bindButton(int keyCode, boolean keyDown, boolean override) {
|
||||
if(keyDown)
|
||||
{
|
||||
@@ -771,4 +886,171 @@ public class CordovaWebView extends WebView {
|
||||
keyUpCodes.add(keyCode);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isBackButtonBound()
|
||||
{
|
||||
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,10 +21,13 @@ package org.apache.cordova;
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
|
||||
import org.apache.cordova.api.LOG;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -32,6 +35,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
||||
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;
|
||||
@@ -43,8 +47,9 @@ import android.webkit.WebViewClient;
|
||||
*/
|
||||
public class CordovaWebViewClient extends WebViewClient {
|
||||
|
||||
private static final String TAG = "Cordova";
|
||||
CordovaInterface ctx;
|
||||
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;
|
||||
|
||||
@@ -54,20 +59,20 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
* @param cordova
|
||||
*/
|
||||
public CordovaWebViewClient(CordovaInterface ctx) {
|
||||
this.ctx = ctx;
|
||||
public CordovaWebViewClient(CordovaInterface cordova) {
|
||||
this.cordova = cordova;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
* @param cordova
|
||||
* @param view
|
||||
*/
|
||||
public CordovaWebViewClient(CordovaInterface ctx, CordovaWebView view) {
|
||||
this.ctx = ctx;
|
||||
public CordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
||||
this.cordova = cordova;
|
||||
this.appView = view;
|
||||
}
|
||||
|
||||
@@ -80,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.
|
||||
@@ -88,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)
|
||||
@@ -100,7 +128,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.ctx.getActivity().startActivity(intent);
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error dialing " + url + ": " + e.toString());
|
||||
}
|
||||
@@ -111,7 +139,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.ctx.getActivity().startActivity(intent);
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error showing map " + url + ": " + e.toString());
|
||||
}
|
||||
@@ -122,7 +150,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.ctx.getActivity().startActivity(intent);
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error sending email " + url + ": " + e.toString());
|
||||
}
|
||||
@@ -154,7 +182,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
intent.setData(Uri.parse("sms:" + address));
|
||||
intent.putExtra("address", address);
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
this.ctx.getActivity().startActivity(intent);
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
||||
}
|
||||
@@ -165,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);
|
||||
@@ -178,7 +206,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
this.ctx.getActivity().startActivity(intent);
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
LOG.e(TAG, "Error loading url " + url, e);
|
||||
}
|
||||
@@ -224,23 +252,23 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has finished loading.
|
||||
* This method is called only for main frame. When onPageFinished() is called, the rendering picture may not be updated yet.
|
||||
*
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@@ -280,7 +308,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
||||
public void run() {
|
||||
try {
|
||||
Thread.sleep(2000);
|
||||
ctx.getActivity().runOnUiThread(new Runnable() {
|
||||
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
appView.postMessage("spinner", "stop");
|
||||
}
|
||||
@@ -294,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);
|
||||
}
|
||||
}
|
||||
@@ -339,11 +364,13 @@ 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) {
|
||||
|
||||
final String packageName = this.ctx.getActivity().getPackageName();
|
||||
final PackageManager pm = this.ctx.getActivity().getPackageManager();
|
||||
final String packageName = this.cordova.getActivity().getPackageName();
|
||||
final PackageManager pm = this.cordova.getActivity().getPackageManager();
|
||||
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||
|
||||
@@ -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 = "1.8.0"; // Cordova version
|
||||
public static String cordovaVersion = "2.2.0"; // Cordova version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
@@ -54,10 +55,11 @@ public class Device extends Plugin {
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
super.setContext(ctx);
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
Device.uuid = getUuid();
|
||||
this.initTelephonyReceiver();
|
||||
}
|
||||
@@ -65,52 +67,35 @@ 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);
|
||||
//JSONObject pg = new JSONObject();
|
||||
//pg.put("version", Device.CordovaVersion);
|
||||
//r.put("cordova", pg);
|
||||
callbackContext.success(r);
|
||||
}
|
||||
return false;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.ctx.getActivity().unregisterReceiver(this.telephonyReceiver);
|
||||
this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -125,7 +110,7 @@ public class Device extends Plugin {
|
||||
private void initTelephonyReceiver() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
||||
//final CordovaInterface myctx = this.ctx;
|
||||
//final CordovaInterface mycordova = this.cordova;
|
||||
this.telephonyReceiver = new BroadcastReceiver() {
|
||||
|
||||
@Override
|
||||
@@ -153,7 +138,7 @@ public class Device extends Plugin {
|
||||
};
|
||||
|
||||
// Register the receiver
|
||||
this.ctx.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
|
||||
this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +156,7 @@ public class Device extends Plugin {
|
||||
* @return
|
||||
*/
|
||||
public String getUuid() {
|
||||
String uuid = Settings.Secure.getString(this.ctx.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
String uuid = Settings.Secure.getString(this.cordova.getActivity().getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -38,6 +40,7 @@ import android.graphics.Color;
|
||||
import android.media.AudioManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.Menu;
|
||||
@@ -46,6 +49,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;
|
||||
|
||||
@@ -138,9 +142,10 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
protected CordovaWebViewClient webViewClient;
|
||||
|
||||
protected LinearLayout root;
|
||||
public boolean bound = false;
|
||||
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
|
||||
@@ -157,7 +162,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
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
|
||||
@@ -181,10 +186,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
// when another application (activity) is started.
|
||||
protected boolean keepRunning = true;
|
||||
|
||||
private boolean volumeupBound;
|
||||
|
||||
private boolean volumedownBound;
|
||||
|
||||
/**
|
||||
* Sets the authentication token.
|
||||
*
|
||||
@@ -260,7 +261,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
{
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
}
|
||||
|
||||
|
||||
if(this.getBooleanProperty("setFullscreen", false))
|
||||
{
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
@@ -300,7 +301,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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -409,6 +419,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);
|
||||
}
|
||||
|
||||
@@ -605,6 +617,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;
|
||||
@@ -613,21 +627,13 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
if (this.appView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send pause event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.appView.pluginManager != null) {
|
||||
this.appView.pluginManager.onPause(this.keepRunning);
|
||||
else
|
||||
{
|
||||
this.appView.handlePause(this.keepRunning);
|
||||
}
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning) {
|
||||
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.appView.pauseTimers();
|
||||
}
|
||||
// hide the splash screen to avoid leaking a window
|
||||
this.removeSplashScreen();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -636,11 +642,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
|
||||
@@ -650,6 +654,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;
|
||||
@@ -659,13 +664,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) {
|
||||
@@ -675,9 +674,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
this.keepRunning = this.activityResultKeepRunning;
|
||||
this.activityResultKeepRunning = false;
|
||||
}
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,18 +685,11 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
LOG.d(TAG, "onDestroy()");
|
||||
super.onDestroy();
|
||||
|
||||
// hide the splash screen to avoid leaking a window
|
||||
this.removeSplashScreen();
|
||||
|
||||
if (this.appView != null) {
|
||||
|
||||
// Send destroy event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{cordova.require('cordova/channel').onDestroy.fire();}catch(e){console.log('exception firing destroy event from native');};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
// Forward to plugins
|
||||
if (this.appView.pluginManager != null) {
|
||||
this.appView.pluginManager.onDestroy();
|
||||
}
|
||||
appView.handleDestroy();
|
||||
}
|
||||
else {
|
||||
this.endActivity();
|
||||
@@ -741,8 +730,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 +756,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;
|
||||
}
|
||||
@@ -784,16 +773,16 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
super.finish();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Launch an activity for which you would like a result when it finished. When this activity exits,
|
||||
* Launch an activity for which you would like a result when it finished. When this activity exits,
|
||||
* your onActivityResult() method will be called.
|
||||
*
|
||||
* @param command The command object
|
||||
* @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 +807,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,9 +828,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
|
||||
final DroidGap me = this;
|
||||
|
||||
// Stop "app loading" spinner if showing
|
||||
this.spinnerStop();
|
||||
|
||||
// If errorUrl specified, then load it
|
||||
final String errorUrl = me.getStringProperty("errorUrl", null);
|
||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||
@@ -849,6 +835,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
// Load URL on UI thread
|
||||
me.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
// Stop "app loading" spinner if showing
|
||||
me.spinnerStop();
|
||||
me.appView.showWebPage(errorUrl, false, true, null);
|
||||
}
|
||||
});
|
||||
@@ -930,7 +918,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
|
||||
@@ -945,25 +933,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
* @return
|
||||
*/
|
||||
public Context getContext() {
|
||||
return this.getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the backbutton.
|
||||
*
|
||||
* @param override
|
||||
*/
|
||||
public void bindBackButton(boolean override) {
|
||||
this.bound = override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine of backbutton is overridden.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isBackButtonBound() {
|
||||
return this.bound;
|
||||
LOG.d(TAG, "This will be deprecated December 2012");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -982,23 +953,13 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
}
|
||||
}
|
||||
|
||||
public void bindButton(String button, boolean override) {
|
||||
// TODO Auto-generated method stub
|
||||
if (button.compareTo("volumeup")==0) {
|
||||
this.volumeupBound = override;
|
||||
}
|
||||
else if (button.compareTo("volumedown")==0) {
|
||||
this.volumedownBound = override;
|
||||
}
|
||||
}
|
||||
|
||||
protected Dialog splashDialog;
|
||||
|
||||
/**
|
||||
* Removes the Dialog that displays the splash screen
|
||||
*/
|
||||
public void removeSplashScreen() {
|
||||
if (splashDialog != null) {
|
||||
if (splashDialog != null && splashDialog.isShowing()) {
|
||||
splashDialog.dismiss();
|
||||
splashDialog = null;
|
||||
}
|
||||
@@ -1008,42 +969,84 @@ public class DroidGap extends Activity implements CordovaInterface {
|
||||
* Shows the splash screen over the full Activity
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void showSplashScreen(int time) {
|
||||
protected void showSplashScreen(final int time) {
|
||||
final DroidGap that = this;
|
||||
|
||||
// Get reference to display
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
|
||||
// Create the layout for the dialog
|
||||
LinearLayout root = new LinearLayout(this);
|
||||
root.setMinimumHeight(display.getHeight());
|
||||
root.setMinimumWidth(display.getWidth());
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(this.getIntegerProperty("backgroundColor", Color.BLACK));
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
root.setBackgroundResource(this.splashscreen);
|
||||
|
||||
// Create and show the dialog
|
||||
splashDialog = new Dialog(this, android.R.style.Theme_Translucent_NoTitleBar);
|
||||
// check to see if the splash screen should be full screen
|
||||
if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
== WindowManager.LayoutParams.FLAG_FULLSCREEN) {
|
||||
splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
splashDialog.setContentView(root);
|
||||
splashDialog.setCancelable(false);
|
||||
splashDialog.show();
|
||||
|
||||
// Set Runnable to remove splash screen just in case
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
removeSplashScreen();
|
||||
// Get reference to display
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
|
||||
// Create the layout for the dialog
|
||||
LinearLayout root = new LinearLayout(that.getActivity());
|
||||
root.setMinimumHeight(display.getHeight());
|
||||
root.setMinimumWidth(display.getWidth());
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(that.getIntegerProperty("backgroundColor", Color.BLACK));
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
root.setBackgroundResource(that.splashscreen);
|
||||
|
||||
// Create and show the dialog
|
||||
splashDialog = new Dialog(that, android.R.style.Theme_Translucent_NoTitleBar);
|
||||
// check to see if the splash screen should be full screen
|
||||
if ((getWindow().getAttributes().flags & WindowManager.LayoutParams.FLAG_FULLSCREEN)
|
||||
== WindowManager.LayoutParams.FLAG_FULLSCREEN) {
|
||||
splashDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN);
|
||||
}
|
||||
splashDialog.setContentView(root);
|
||||
splashDialog.setCancelable(false);
|
||||
splashDialog.show();
|
||||
|
||||
// Set Runnable to remove splash screen just in case
|
||||
final Handler handler = new Handler();
|
||||
handler.postDelayed(new Runnable() {
|
||||
public void run() {
|
||||
removeSplashScreen();
|
||||
}
|
||||
}, time);
|
||||
}
|
||||
}, time);
|
||||
};
|
||||
this.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event)
|
||||
{
|
||||
//Determine if the focus is on the current view or not
|
||||
if (appView.getHitTestResult() != null &&
|
||||
appView.getHitTestResult().getType() == WebView.HitTestResult.EDIT_TEXT_TYPE &&
|
||||
(keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU)) {
|
||||
return appView.onKeyUp(keyCode, event);
|
||||
} else if (appView.isCustomViewShowing() && 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)
|
||||
{
|
||||
//Determine if the focus is on the current view or not
|
||||
if (appView.getHitTestResult() != null &&
|
||||
appView.getHitTestResult().getType() == WebView.HitTestResult.EDIT_TEXT_TYPE &&
|
||||
keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return appView.onKeyDown(keyCode, event);
|
||||
}
|
||||
else
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
*
|
||||
@@ -1058,8 +1061,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)) {
|
||||
@@ -1082,4 +1088,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);
|
||||
@@ -162,4 +162,24 @@ public class ExifHelper {
|
||||
|
||||
this.outFile.saveAttributes();
|
||||
}
|
||||
|
||||
public int getOrientation() {
|
||||
int o = Integer.parseInt(this.orientation);
|
||||
|
||||
if (o == ExifInterface.ORIENTATION_NORMAL) {
|
||||
return 0;
|
||||
} else if (o == ExifInterface.ORIENTATION_ROTATE_90) {
|
||||
return 90;
|
||||
} else if (o == ExifInterface.ORIENTATION_ROTATE_180) {
|
||||
return 180;
|
||||
} else if (o == ExifInterface.ORIENTATION_ROTATE_270) {
|
||||
return 270;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void resetOrientation() {
|
||||
this.orientation = "" + ExifInterface.ORIENTATION_NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
61
framework/src/org/apache/cordova/ExposedJsApi.java
Executable file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
Licensed to the Apache Software Foundation (ASF) under one
|
||||
or more contributor license agreements. See the NOTICE file
|
||||
distributed with this work for additional information
|
||||
regarding copyright ownership. The ASF licenses this file
|
||||
to you under the Apache License, Version 2.0 (the
|
||||
"License"); you may not use this file except in compliance
|
||||
with the License. You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on an
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
||||
*/
|
||||
package org.apache.cordova;
|
||||
|
||||
import org.apache.cordova.api.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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNativeToJsBridgeMode(int value) {
|
||||
jsMessageQueue.setBridgeMode(value);
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -50,7 +51,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 +85,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 +165,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,9 +220,9 @@ public class FileUtils extends Plugin {
|
||||
*/
|
||||
private void notifyDelete(String filePath) {
|
||||
String newFilePath = stripFileProtocol(filePath);
|
||||
int result = this.ctx.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[] { filePath });
|
||||
new String[] { newFilePath });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -246,7 +243,7 @@ public class FileUtils extends Plugin {
|
||||
|
||||
// Handle the special case where you get an Android content:// uri.
|
||||
if (decoded.startsWith("content:")) {
|
||||
Cursor cursor = this.ctx.getActivity().managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null);
|
||||
Cursor cursor = this.cordova.getActivity().managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null);
|
||||
// Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data"
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
cursor.moveToFirst();
|
||||
@@ -298,7 +295,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]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -407,19 +406,19 @@ 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();
|
||||
FileInputStream istream = new FileInputStream(srcFile);
|
||||
FileOutputStream ostream = new FileOutputStream(destFile);
|
||||
FileChannel input = istream.getChannel();
|
||||
FileChannel output = ostream.getChannel();
|
||||
|
||||
input.transferTo(0, input.size(), output);
|
||||
|
||||
input.close();
|
||||
output.close();
|
||||
|
||||
/*
|
||||
if (srcFile.length() != destFile.length()) {
|
||||
return false;
|
||||
try {
|
||||
input.transferTo(0, input.size(), output);
|
||||
} finally {
|
||||
istream.close();
|
||||
ostream.close();
|
||||
input.close();
|
||||
output.close();
|
||||
}
|
||||
*/
|
||||
|
||||
return getEntry(destFile);
|
||||
}
|
||||
@@ -478,7 +477,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;
|
||||
}
|
||||
@@ -728,9 +727,9 @@ public class FileUtils extends Plugin {
|
||||
private boolean atRootDirectory(String filePath) {
|
||||
filePath = stripFileProtocol(filePath);
|
||||
|
||||
if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getActivity().getPackageName() + "/cache") ||
|
||||
if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + cordova.getActivity().getPackageName() + "/cache") ||
|
||||
filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
|
||||
filePath.equals("/data/data/" + ctx.getActivity().getPackageName())) {
|
||||
filePath.equals("/data/data/" + cordova.getActivity().getPackageName())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -819,16 +818,16 @@ public class FileUtils extends Plugin {
|
||||
fs.put("name", "temporary");
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getActivity().getPackageName() + "/cache/");
|
||||
"/Android/data/" + cordova.getActivity().getPackageName() + "/cache/");
|
||||
// Create the cache dir if it doesn't exist.
|
||||
fp.mkdirs();
|
||||
fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getActivity().getPackageName() + "/cache/"));
|
||||
"/Android/data/" + cordova.getActivity().getPackageName() + "/cache/"));
|
||||
} else {
|
||||
fp = new File("/data/data/" + ctx.getActivity().getPackageName() + "/cache/");
|
||||
fp = new File("/data/data/" + cordova.getActivity().getPackageName() + "/cache/");
|
||||
// Create the cache dir if it doesn't exist.
|
||||
fp.mkdirs();
|
||||
fs.put("root", getEntry("/data/data/" + ctx.getActivity().getPackageName() + "/cache/"));
|
||||
fs.put("root", getEntry("/data/data/" + cordova.getActivity().getPackageName() + "/cache/"));
|
||||
}
|
||||
}
|
||||
else if (type == PERSISTENT) {
|
||||
@@ -836,7 +835,7 @@ public class FileUtils extends Plugin {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
|
||||
} else {
|
||||
fs.put("root", getEntry("/data/data/" + ctx.getActivity().getPackageName()));
|
||||
fs.put("root", getEntry("/data/data/" + cordova.getActivity().getPackageName()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -943,7 +942,7 @@ public class FileUtils extends Plugin {
|
||||
String contentType = null;
|
||||
if (filename.startsWith("content:")) {
|
||||
Uri fileUri = Uri.parse(filename);
|
||||
contentType = this.ctx.getActivity().getContentResolver().getType(fileUri);
|
||||
contentType = this.cordova.getActivity().getContentResolver().getType(fileUri);
|
||||
}
|
||||
else {
|
||||
contentType = getMimeType(filename);
|
||||
@@ -1006,14 +1005,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1026,7 +1028,7 @@ public class FileUtils extends Plugin {
|
||||
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
||||
if (path.startsWith("content")) {
|
||||
Uri uri = Uri.parse(path);
|
||||
return ctx.getActivity().getContentResolver().openInputStream(uri);
|
||||
return cordova.getActivity().getContentResolver().openInputStream(uri);
|
||||
}
|
||||
else {
|
||||
path = stripFileProtocol(path);
|
||||
@@ -1038,15 +1040,21 @@ 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 ctx) 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 ctx) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = ctx.getActivity().managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
|
||||
String uri = contentUri.toString();
|
||||
if (uri.startsWith("content:")) {
|
||||
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 {
|
||||
return uri;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.ctx.getActivity().getSystemService(Context.LOCATION_SERVICE);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
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,7 +69,8 @@ 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;
|
||||
@@ -84,12 +86,13 @@ public class NetworkManager extends Plugin {
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The CordovaWebView Cordova is running in.
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sockMan = (ConnectivityManager) ctx.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.connectionCallbackId = null;
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.sockMan = (ConnectivityManager) cordova.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.connectionCallbackContext = null;
|
||||
|
||||
// We need to listen to connectivity events to update navigator.connection
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
@@ -99,10 +102,13 @@ public class NetworkManager extends Plugin {
|
||||
@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((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
}
|
||||
};
|
||||
ctx.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||
this.registered = true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -110,49 +116,44 @@ 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.ctx.getActivity().unregisterReceiver(this.receiver);
|
||||
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||
this.registered = false;
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the network receiver on navigation.
|
||||
*/
|
||||
public void onReset() {
|
||||
this.onDestroy();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -194,11 +195,12 @@ 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);
|
||||
if (connectionCallbackContext != null) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||
result.setKeepCallback(true);
|
||||
connectionCallbackContext.sendPluginResult(result);
|
||||
}
|
||||
|
||||
// Send to all plugins
|
||||
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,12 +101,12 @@ public class Notification extends Plugin {
|
||||
|
||||
/**
|
||||
* Beep plays the default notification ringtone.
|
||||
*
|
||||
*
|
||||
* @param count Number of times to play notification
|
||||
*/
|
||||
public void beep(long count) {
|
||||
Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
Ringtone notification = RingtoneManager.getRingtone(this.ctx.getActivity().getBaseContext(), ringtone);
|
||||
Ringtone notification = RingtoneManager.getRingtone(this.cordova.getActivity().getBaseContext(), ringtone);
|
||||
|
||||
// If phone is not set to silent mode
|
||||
if (notification != null) {
|
||||
@@ -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) {
|
||||
@@ -171,65 +134,71 @@ public class Notification extends Plugin {
|
||||
if (time == 0) {
|
||||
time = 500;
|
||||
}
|
||||
Vibrator vibrator = (Vibrator) this.ctx.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
Vibrator vibrator = (Vibrator) this.cordova.getActivity().getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vibrator.vibrate(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ctx = this.ctx;
|
||||
final Notification notification = this;
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(ctx.getActivity());
|
||||
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();
|
||||
};
|
||||
};
|
||||
this.ctx.getActivity().runOnUiThread(runnable);
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ctx = this.ctx;
|
||||
final Notification notification = this;
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
final String[] fButtons = buttonLabels.split(",");
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(ctx.getActivity());
|
||||
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,22 +228,29 @@ 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();
|
||||
};
|
||||
};
|
||||
this.ctx.getActivity().runOnUiThread(runnable);
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the spinner.
|
||||
*
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
@@ -283,19 +259,18 @@ public class Notification extends Plugin {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
final Notification notification = this;
|
||||
final CordovaInterface ctx = this.ctx;
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
notification.spinnerDialog = ProgressDialog.show(ctx.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;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
this.ctx.getActivity().runOnUiThread(runnable);
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -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
|
||||
*/
|
||||
@@ -320,10 +295,10 @@ public class Notification extends Plugin {
|
||||
this.progressDialog = null;
|
||||
}
|
||||
final Notification notification = this;
|
||||
final CordovaInterface ctx = this.ctx;
|
||||
final CordovaInterface cordova = this.cordova;
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
notification.progressDialog = new ProgressDialog(ctx.getActivity());
|
||||
notification.progressDialog = new ProgressDialog(cordova.getActivity());
|
||||
notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
notification.progressDialog.setTitle(title);
|
||||
notification.progressDialog.setMessage(message);
|
||||
@@ -339,12 +314,12 @@ public class Notification extends Plugin {
|
||||
notification.progressDialog.show();
|
||||
}
|
||||
};
|
||||
this.ctx.getActivity().runOnUiThread(runnable);
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value of progress bar.
|
||||
*
|
||||
*
|
||||
* @param value 0-100
|
||||
*/
|
||||
public synchronized void progressValue(int value) {
|
||||
|
||||
@@ -19,25 +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")) {
|
||||
//((DroidGap)this.ctx).removeSplashScreen();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,8 @@ package org.apache.cordova;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
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;
|
||||
@@ -35,7 +36,7 @@ import android.database.sqlite.*;
|
||||
* Android 3.0 devices. It is not used for other versions of Android, since
|
||||
* HTML5 database is built in to the browser.
|
||||
*/
|
||||
public class Storage extends Plugin {
|
||||
public class Storage extends CordovaPlugin {
|
||||
|
||||
// Data Definition Language
|
||||
private static final String ALTER = "alter";
|
||||
@@ -60,47 +61,32 @@ public class Storage extends Plugin {
|
||||
* 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 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 = "";
|
||||
|
||||
try {
|
||||
if (action.equals("openDatabase")) {
|
||||
this.openDatabase(args.getString(0), args.getString(1),
|
||||
args.getString(2), args.getLong(3));
|
||||
} else if (action.equals("executeSql")) {
|
||||
String[] s = null;
|
||||
if (args.isNull(1)) {
|
||||
s = new String[0];
|
||||
} else {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
s = new String[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
s[i] = a.getString(i);
|
||||
}
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
if (action.equals("openDatabase")) {
|
||||
this.openDatabase(args.getString(0), args.getString(1),
|
||||
args.getString(2), args.getLong(3));
|
||||
} else if (action.equals("executeSql")) {
|
||||
String[] s = null;
|
||||
if (args.isNull(1)) {
|
||||
s = new String[0];
|
||||
} else {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
s = new String[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
s[i] = a.getString(i);
|
||||
}
|
||||
this.executeSql(args.getString(0), s, args.getString(2));
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
this.executeSql(args.getString(0), s, args.getString(2));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
callbackContext.success();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,6 +101,13 @@ public class Storage extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up on navigation/refresh.
|
||||
*/
|
||||
public void onReset() {
|
||||
this.onDestroy();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -141,10 +134,23 @@ public class Storage extends Plugin {
|
||||
|
||||
// If no database path, generate from application package
|
||||
if (this.path == null) {
|
||||
this.path = this.ctx.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
this.path = this.cordova.getActivity().getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
}
|
||||
|
||||
this.dbName = this.path + File.pathSeparator + db + ".db";
|
||||
this.dbName = this.path + File.separator + db + ".db";
|
||||
|
||||
/*
|
||||
* What is all this nonsense? Well the separator was incorrect so the db was showing up in the wrong
|
||||
* directory. This bit of code fixes that issue and moves the db to the correct directory.
|
||||
*/
|
||||
File oldDbFile = new File(this.path + File.pathSeparator + db + ".db");
|
||||
if (oldDbFile.exists()) {
|
||||
File dbPath = new File(this.path);
|
||||
File dbFile = new File(dbName);
|
||||
dbPath.mkdirs();
|
||||
oldDbFile.renameTo(dbFile);
|
||||
}
|
||||
|
||||
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
|
||||
}
|
||||
|
||||
@@ -162,7 +168,7 @@ public class Storage extends Plugin {
|
||||
try {
|
||||
if (isDDL(query)) {
|
||||
this.myDb.execSQL(query);
|
||||
this.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', '');");
|
||||
this.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', '');");
|
||||
}
|
||||
else {
|
||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||
@@ -175,12 +181,12 @@ public class Storage extends Plugin {
|
||||
System.out.println("Storage.executeSql(): Error=" + ex.getMessage());
|
||||
|
||||
// Send error message back to JavaScript
|
||||
this.sendJavascript("cordova.require('cordova/plugin/android/storage').failQuery('" + ex.getMessage() + "','" + tx_id + "');");
|
||||
this.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').failQuery('" + ex.getMessage() + "','" + tx_id + "');");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see the the query is a Data Definintion command
|
||||
* Checks to see the the query is a Data Definition command
|
||||
*
|
||||
* @param query to be executed
|
||||
* @return true if it is a DDL command, false otherwise
|
||||
@@ -233,7 +239,7 @@ public class Storage extends Plugin {
|
||||
}
|
||||
|
||||
// Let JavaScript know that there are no more rows
|
||||
this.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', " + result + ");");
|
||||
this.webView.sendJavascript("cordova.require('cordova/plugin/android/storage').completeQuery('" + tx_id + "', " + result + ");");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,112 +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.util.List;
|
||||
|
||||
import org.apache.cordova.api.CordovaInterface;
|
||||
import org.apache.cordova.api.Plugin;
|
||||
import org.apache.cordova.api.PluginResult;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
|
||||
public class TempListener extends Plugin implements SensorEventListener {
|
||||
|
||||
Sensor mSensor;
|
||||
private SensorManager sensorManager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public TempListener() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Command. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sensorManager = (SensorManager) ctx.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.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
if (action.equals("start")) {
|
||||
this.start();
|
||||
}
|
||||
else if (action.equals("stop")) {
|
||||
this.stop();
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by AccelBroker when listener is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
public void start() {
|
||||
@SuppressWarnings("deprecation")
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_TEMPERATURE);
|
||||
if (list.size() > 0) {
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.sensorManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
// We want to know what temp this is.
|
||||
float temp = event.values[0];
|
||||
this.sendJavascript("gotTemp(" + temp + ");");
|
||||
}
|
||||
|
||||
}
|
||||
111
framework/src/org/apache/cordova/api/CallbackContext.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package org.apache.cordova.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class CallbackContext {
|
||||
private static final String LOG_TAG = "CordovaPlugin";
|
||||
|
||||
private String callbackId;
|
||||
private CordovaWebView webView;
|
||||
private boolean finished;
|
||||
private int changingThreads;
|
||||
|
||||
public CallbackContext(String callbackId, CordovaWebView webView) {
|
||||
this.callbackId = callbackId;
|
||||
this.webView = webView;
|
||||
}
|
||||
|
||||
public boolean isFinished() {
|
||||
return finished;
|
||||
}
|
||||
|
||||
public boolean isChangingThreads() {
|
||||
return changingThreads > 0;
|
||||
}
|
||||
|
||||
public String getCallbackId() {
|
||||
return callbackId;
|
||||
}
|
||||
|
||||
public void sendPluginResult(PluginResult pluginResult) {
|
||||
synchronized (this) {
|
||||
if (finished) {
|
||||
Log.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage());
|
||||
return;
|
||||
} else {
|
||||
finished = !pluginResult.getKeepCallback();
|
||||
}
|
||||
}
|
||||
webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
*/
|
||||
public void success(JSONObject message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
*/
|
||||
public void success(String message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
*/
|
||||
public void success(JSONArray message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
*/
|
||||
public void success() {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.OK));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
*/
|
||||
public void error(JSONObject message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(String message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(int message) {
|
||||
sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message));
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,11 @@
|
||||
package org.apache.cordova.api;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
/**
|
||||
* The Cordova activity abstract class that is extended by DroidGap.
|
||||
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
||||
@@ -35,28 +38,14 @@ public interface CordovaInterface {
|
||||
* @param intent The intent to start
|
||||
* @param requestCode The request code that is passed to callback to identify the activity
|
||||
*/
|
||||
abstract public void startActivityForResult(IPlugin command, Intent intent, int requestCode);
|
||||
abstract public void startActivityForResult(CordovaPlugin command, Intent intent, int requestCode);
|
||||
|
||||
/**
|
||||
* Set the plugin to be called when a sub-activity exits.
|
||||
*
|
||||
* @param plugin The plugin on which onActivityResult is to be called
|
||||
*/
|
||||
abstract public void setActivityResultCallback(IPlugin plugin);
|
||||
|
||||
/**
|
||||
* Causes the Activity to override the back button behavior.
|
||||
*
|
||||
* @param override
|
||||
*/
|
||||
public abstract void bindBackButton(boolean override);
|
||||
|
||||
/**
|
||||
* A hook required to check if the Back Button is bound.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public abstract boolean isBackButtonBound();
|
||||
abstract public void setActivityResultCallback(CordovaPlugin plugin);
|
||||
|
||||
/**
|
||||
* Get the Android activity.
|
||||
@@ -64,7 +53,10 @@ public interface CordovaInterface {
|
||||
* @return
|
||||
*/
|
||||
public abstract Activity getActivity();
|
||||
|
||||
|
||||
@Deprecated
|
||||
public abstract Context getContext();
|
||||
|
||||
@Deprecated
|
||||
public abstract void cancelLoadUrl();
|
||||
|
||||
@@ -76,4 +68,9 @@ public interface CordovaInterface {
|
||||
* @return Object or null
|
||||
*/
|
||||
public Object onMessage(String id, Object data);
|
||||
|
||||
/**
|
||||
* Returns a shared thread pool that can be used for background tasks.
|
||||
*/
|
||||
public ExecutorService getThreadPool();
|
||||
}
|
||||
|
||||
113
framework/src/org/apache/cordova/api/IPlugin.java → framework/src/org/apache/cordova/api/CordovaPlugin.java
Executable file → Normal file
@@ -20,74 +20,92 @@ package org.apache.cordova.api;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
|
||||
//import android.content.Context;
|
||||
import org.json.JSONException;
|
||||
import android.content.Intent;
|
||||
|
||||
|
||||
/**
|
||||
* Plugin interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the PluginManager.
|
||||
* Plugins must extend this class and override one of the execute methods.
|
||||
*/
|
||||
public interface IPlugin {
|
||||
public class CordovaPlugin {
|
||||
public String id;
|
||||
public CordovaWebView webView; // WebView object
|
||||
public CordovaInterface cordova;
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
* @param cordova The context of the main Activity.
|
||||
* @param webView The associated CordovaWebView.
|
||||
*/
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
assert this.cordova == null;
|
||||
this.cordova = cordova;
|
||||
this.webView = webView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
PluginResult execute(String action, JSONArray args, String callbackId);
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||
* cordova.getThreadPool().execute(runnable);
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
* To run on the UI thread, use:
|
||||
* cordova.getActivity().runOnUiThread(runnable);
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param rawArgs The exec() arguments in JSON form.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return Whether the action was valid.
|
||||
*/
|
||||
public boolean isSynch(String action);
|
||||
public boolean execute(String action, String rawArgs, CallbackContext callbackContext) throws JSONException {
|
||||
JSONArray args = new JSONArray(rawArgs);
|
||||
return execute(action, args, callbackContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Plugin. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
* Executes the request.
|
||||
*
|
||||
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||
* cordova.getThreadPool().execute(runnable);
|
||||
*
|
||||
* To run on the UI thread, use:
|
||||
* cordova.getActivity().runOnUiThread(runnable);
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args The exec() arguments.
|
||||
* @param callbackContext The callback context used when calling back into JavaScript.
|
||||
* @return Whether the action was valid.
|
||||
*/
|
||||
void setContext(CordovaInterface ctx);
|
||||
|
||||
/**
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* a Cordova app runs.
|
||||
*
|
||||
* @param webView The Cordova WebView
|
||||
*/
|
||||
void setView(CordovaWebView webView);
|
||||
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onPause(boolean multitasking);
|
||||
public void onPause(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onResume(boolean multitasking);
|
||||
public void onResume(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
void onNewIntent(Intent intent);
|
||||
public void onNewIntent(Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
void onDestroy();
|
||||
public void onDestroy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
@@ -96,7 +114,9 @@ public interface IPlugin {
|
||||
* @param data The message data
|
||||
* @return Object to stop propagation or null
|
||||
*/
|
||||
public Object onMessage(String id, Object data);
|
||||
public Object onMessage(String id, Object data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
||||
@@ -107,7 +127,8 @@ public interface IPlugin {
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
void onActivityResult(int requestCode, int resultCode, Intent intent);
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* By specifying a <url-filter> in plugins.xml you can map a URL (using startsWith atm) to this method.
|
||||
@@ -115,5 +136,17 @@ public interface IPlugin {
|
||||
* @param url The URL that is trying to be loaded in the Cordova webview.
|
||||
* @return Return true to prevent the URL from loading. Default is false.
|
||||
*/
|
||||
boolean onOverrideUrlLoading(String url);
|
||||
public boolean onOverrideUrlLoading(String url) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the WebView does a top-level navigation or refreshes.
|
||||
*
|
||||
* Plugins should stop any long-running processes and clean up internal state.
|
||||
*
|
||||
* Does nothing by default.
|
||||
*/
|
||||
public void onReset() {
|
||||
}
|
||||
}
|
||||
155
framework/src/org/apache/cordova/api/LegacyContext.java
Normal file
@@ -0,0 +1,155 @@
|
||||
// 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.api;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
@Deprecated
|
||||
public class LegacyContext implements CordovaInterface {
|
||||
private static final String LOG_TAG = "Deprecation Notice";
|
||||
private CordovaInterface cordova;
|
||||
|
||||
public LegacyContext(CordovaInterface cordova) {
|
||||
this.cordova = cordova;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void cancelLoadUrl() {
|
||||
Log.i(LOG_TAG, "Replace ctx.cancelLoadUrl() with cordova.cancelLoadUrl()");
|
||||
this.cordova.cancelLoadUrl();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Activity getActivity() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getActivity() with cordova.getActivity()");
|
||||
return this.cordova.getActivity();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Context getContext() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getContext() with cordova.getContext()");
|
||||
return this.cordova.getContext();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Object onMessage(String arg0, Object arg1) {
|
||||
Log.i(LOG_TAG, "Replace ctx.onMessage() with cordova.onMessage()");
|
||||
return this.cordova.onMessage(arg0, arg1);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void setActivityResultCallback(CordovaPlugin arg0) {
|
||||
Log.i(LOG_TAG, "Replace ctx.setActivityResultCallback() with cordova.setActivityResultCallback()");
|
||||
this.cordova.setActivityResultCallback(arg0);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void startActivityForResult(CordovaPlugin arg0, Intent arg1, int arg2) {
|
||||
Log.i(LOG_TAG, "Replace ctx.startActivityForResult() with cordova.startActivityForResult()");
|
||||
this.cordova.startActivityForResult(arg0, arg1, arg2);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void startActivity(Intent intent) {
|
||||
Log.i(LOG_TAG, "Replace ctx.startActivity() with cordova.getActivity().startActivity()");
|
||||
this.cordova.getActivity().startActivity(intent);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Object getSystemService(String name) {
|
||||
Log.i(LOG_TAG, "Replace ctx.getSystemService() with cordova.getActivity().getSystemService()");
|
||||
return this.cordova.getActivity().getSystemService(name);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public AssetManager getAssets() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getAssets() with cordova.getActivity().getAssets()");
|
||||
return this.cordova.getActivity().getAssets();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void runOnUiThread(Runnable runnable) {
|
||||
Log.i(LOG_TAG, "Replace ctx.runOnUiThread() with cordova.getActivity().runOnUiThread()");
|
||||
this.cordova.getActivity().runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Context getApplicationContext() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getApplicationContext() with cordova.getActivity().getApplicationContext()");
|
||||
return this.cordova.getActivity().getApplicationContext();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public PackageManager getPackageManager() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getPackageManager() with cordova.getActivity().getPackageManager()");
|
||||
return this.cordova.getActivity().getPackageManager();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public SharedPreferences getSharedPreferences(String name, int mode) {
|
||||
Log.i(LOG_TAG, "Replace ctx.getSharedPreferences() with cordova.getActivity().getSharedPreferences()");
|
||||
return this.cordova.getActivity().getSharedPreferences(name, mode);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void unregisterReceiver(BroadcastReceiver receiver) {
|
||||
Log.i(LOG_TAG, "Replace ctx.unregisterReceiver() with cordova.getActivity().unregisterReceiver()");
|
||||
this.cordova.getActivity().unregisterReceiver(receiver);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Resources getResources() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getResources() with cordova.getActivity().getResources()");
|
||||
return this.cordova.getActivity().getResources();
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public ComponentName startService(Intent service) {
|
||||
Log.i(LOG_TAG, "Replace ctx.startService() with cordova.getActivity().startService()");
|
||||
return this.cordova.getActivity().startService(service);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
|
||||
Log.i(LOG_TAG, "Replace ctx.bindService() with cordova.getActivity().bindService()");
|
||||
return this.cordova.getActivity().bindService(service, conn, flags);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public void unbindService(ServiceConnection conn) {
|
||||
Log.i(LOG_TAG, "Replace ctx.unbindService() with cordova.getActivity().unbindService()");
|
||||
this.cordova.getActivity().unbindService(conn);
|
||||
}
|
||||
|
||||
public ExecutorService getThreadPool() {
|
||||
Log.i(LOG_TAG, "Replace ctx.getThreadPool() with cordova.getThreadPool()");
|
||||
return this.cordova.getThreadPool();
|
||||
}
|
||||
}
|
||||
@@ -20,39 +20,29 @@ package org.apache.cordova.api;
|
||||
|
||||
import org.apache.cordova.CordovaWebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* Plugin interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the PluginManager.
|
||||
* Legacy Plugin class. This acts as a shim to support the old execute() signature.
|
||||
* New plugins should extend CordovaPlugin directly.
|
||||
*/
|
||||
public abstract class Plugin implements IPlugin {
|
||||
@Deprecated
|
||||
public abstract class Plugin extends CordovaPlugin {
|
||||
public LegacyContext ctx; // LegacyContext object
|
||||
|
||||
public String id;
|
||||
public CordovaWebView webView; // WebView object
|
||||
public CordovaInterface ctx; // CordovaActivity object
|
||||
|
||||
/**
|
||||
* 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 abstract PluginResult execute(String action, JSONArray args, String callbackId);
|
||||
|
||||
/**
|
||||
* 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 false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||
super.initialize(cordova, webView);
|
||||
this.setContext(cordova);
|
||||
this.setView(webView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Plugin. This can then be used to do things like
|
||||
@@ -61,90 +51,65 @@ public abstract class Plugin implements IPlugin {
|
||||
* @param ctx The context of the main Activity.
|
||||
*/
|
||||
public void setContext(CordovaInterface ctx) {
|
||||
this.ctx = ctx;
|
||||
this.cordova = ctx;
|
||||
this.ctx = new LegacyContext(cordova);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* a Cordova app runs.
|
||||
*
|
||||
*
|
||||
* @param webView The Cordova WebView
|
||||
*/
|
||||
public void setView(CordovaWebView webView) {
|
||||
this.webView = webView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a message is sent to plugin.
|
||||
*
|
||||
* @param id The message id
|
||||
* @param data The message data
|
||||
* @return Object to stop propagation or null
|
||||
*/
|
||||
public Object onMessage(String id, Object data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
||||
* the resultCode it returned, and any additional data from it.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* By specifying a <url-filter> in plugins.xml you can map a URL (using startsWith atm) to this method.
|
||||
*
|
||||
* @param url The URL that is trying to be loaded in the Cordova webview.
|
||||
* @return Return true to prevent the URL from loading. Default is false.
|
||||
*/
|
||||
public boolean onOverrideUrlLoading(String url) {
|
||||
return false;
|
||||
|
||||
@Override
|
||||
public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||
final String callbackId = callbackContext.getCallbackId();
|
||||
boolean runAsync = !isSynch(action);
|
||||
if (runAsync) {
|
||||
// Run this on a different thread so that this one can return back to JS
|
||||
cordova.getThreadPool().execute(new Runnable() {
|
||||
public void run() {
|
||||
PluginResult cr;
|
||||
try {
|
||||
cr = execute(action, args, callbackId);
|
||||
} catch (Throwable e) {
|
||||
cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
||||
}
|
||||
sendPluginResult(cr, callbackId);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
PluginResult cr = execute(action, args, callbackId);
|
||||
|
||||
// Interpret a null response as NO_RESULT, which *does* clear the callbacks on the JS side.
|
||||
if (cr == null) {
|
||||
cr = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
}
|
||||
|
||||
callbackContext.sendPluginResult(cr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send generic JavaScript statement back to JavaScript.
|
||||
* success(...) and error(...) should be used instead where possible.
|
||||
*
|
||||
* @param statement
|
||||
* sendPluginResult() should be used instead where possible.
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
this.webView.sendJavascript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send generic JavaScript statement back to JavaScript.
|
||||
*/
|
||||
public void sendPluginResult(PluginResult pluginResult, String callbackId) {
|
||||
this.webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript success callback for this plugin.
|
||||
*
|
||||
@@ -152,60 +117,61 @@ public abstract class Plugin implements IPlugin {
|
||||
* that execute should return null and the callback from the async operation can
|
||||
* call success(...) or error(...)
|
||||
*
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(PluginResult pluginResult, String callbackId) {
|
||||
this.webView.sendJavascript(pluginResult.toSuccessCallbackString(callbackId));
|
||||
this.webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(JSONObject message, String callbackId) {
|
||||
this.webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(String message, String callbackId) {
|
||||
this.webView.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.OK, message), callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript error callback for this plugin.
|
||||
*
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(PluginResult pluginResult, String callbackId) {
|
||||
this.webView.sendJavascript(pluginResult.toErrorCallbackString(callbackId));
|
||||
this.webView.sendPluginResult(pluginResult, callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(JSONObject message, String callbackId) {
|
||||
this.webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(String message, String callbackId) {
|
||||
this.webView.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
|
||||
this.webView.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, message), callbackId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||