Compare commits
484 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a120614617 | ||
|
|
0311f0db38 | ||
|
|
29230d0316 | ||
|
|
57fc49ddc2 | ||
|
|
5ac6853fed | ||
|
|
b870214cca | ||
|
|
55074b925f | ||
|
|
958424ce59 | ||
|
|
d04fc289ac | ||
|
|
e14edf134d | ||
|
|
dbb127447f | ||
|
|
dc94fc39ec | ||
|
|
6db9a7cb12 | ||
|
|
1f39386616 | ||
|
|
25aef945d1 | ||
|
|
c9aa43afe0 | ||
|
|
913e177f6f | ||
|
|
ae431aec12 | ||
|
|
8ac15048cd | ||
|
|
a1cfe87f1e | ||
|
|
c130396d4e | ||
|
|
bc2e7cf317 | ||
|
|
7ace1d652d | ||
|
|
26effd1def | ||
|
|
5f6824e5dd | ||
|
|
4589bdd31f | ||
|
|
72e0b49e0b | ||
|
|
3caa45d860 | ||
|
|
7c069f14f8 | ||
|
|
552885dbd3 | ||
|
|
6efeb1471c | ||
|
|
01f062d2ba | ||
|
|
2a42c463d2 | ||
|
|
182843edf6 | ||
|
|
9a9d36e9d9 | ||
|
|
7d5249eea6 | ||
|
|
f7910c41c3 | ||
|
|
3973f4f952 | ||
|
|
8a19769a47 | ||
|
|
c0ee593c10 | ||
|
|
c806451b8a | ||
|
|
00e5ff1964 | ||
|
|
432aec62a9 | ||
|
|
c8ec7e5191 | ||
|
|
a0d2b96de6 | ||
|
|
2c202b82d7 | ||
|
|
a42dc08756 | ||
|
|
48f58110fe | ||
|
|
7b3724972b | ||
|
|
9ca2a16218 | ||
|
|
f1e8400abf | ||
|
|
11e0ffa90a | ||
|
|
2ee4326a4d | ||
|
|
226e72ac18 | ||
|
|
65c78b8f3f | ||
|
|
6137c7ca06 | ||
|
|
5bebf11b37 | ||
|
|
68161d2714 | ||
|
|
a6473cb826 | ||
|
|
0084c6f96a | ||
|
|
a87825dbee | ||
|
|
3566154cd0 | ||
|
|
92d69e320f | ||
|
|
08a190ef5b | ||
|
|
98339ee5d8 | ||
|
|
fa387fd758 | ||
|
|
54979f2fc4 | ||
|
|
538e90f23a | ||
|
|
d9107bcac6 | ||
|
|
3f3a0b9140 | ||
|
|
e1347e434e | ||
|
|
7657faa9c3 | ||
|
|
28ef765913 | ||
|
|
d2f59391a2 | ||
|
|
df90bdb350 | ||
|
|
c416c77d7a | ||
|
|
ce05a720d1 | ||
|
|
6c19a440f5 | ||
|
|
f4612fdb5d | ||
|
|
04b9a0b09e | ||
|
|
f93c438067 | ||
|
|
e1d608443a | ||
|
|
9233c3a898 | ||
|
|
dfa514334b | ||
|
|
5810a96e62 | ||
|
|
70473a80af | ||
|
|
525fd30cb2 | ||
|
|
5212cd4dcd | ||
|
|
e95bde62a2 | ||
|
|
4fe73cf6ad | ||
|
|
78b2835da4 | ||
|
|
f9a49efae9 | ||
|
|
b9ddc9e678 | ||
|
|
dc459c84a3 | ||
|
|
1d26239809 | ||
|
|
5ca233779d | ||
|
|
e51b4897a3 | ||
|
|
81f283e56f | ||
|
|
ccdd2fd2ca | ||
|
|
69f11a29e1 | ||
|
|
cf494f3238 | ||
|
|
d5895c635a | ||
|
|
2ac9873613 | ||
|
|
eb59e76cde | ||
|
|
d9db845b43 | ||
|
|
e55327b064 | ||
|
|
bdd5a4e053 | ||
|
|
ac2e2c9a42 | ||
|
|
76f9d49e24 | ||
|
|
6ec8ab95fc | ||
|
|
9c98625610 | ||
|
|
f270cde47d | ||
|
|
9de7efd072 | ||
|
|
7b81d317a0 | ||
|
|
876f975aa2 | ||
|
|
3c5815ac0f | ||
|
|
678ae2d684 | ||
|
|
e4f8f44fb0 | ||
|
|
49566d29f8 | ||
|
|
7f4ee7b20a | ||
|
|
32526a8c16 | ||
|
|
71a7f72ab9 | ||
|
|
4d0824f4a4 | ||
|
|
d56dd40d06 | ||
|
|
6aafd6dc3a | ||
|
|
011b512f28 | ||
|
|
aa2d17e489 | ||
|
|
0eee2293dc | ||
|
|
a2f35d2bda | ||
|
|
58f58d9ee8 | ||
|
|
412bb349ac | ||
|
|
652f15f893 | ||
|
|
8512ebb923 | ||
|
|
f0ac173ec8 | ||
|
|
bef0d47924 | ||
|
|
cba0d59021 | ||
|
|
7d3afcab94 | ||
|
|
5f1cda07e7 | ||
|
|
e11beade4b | ||
|
|
6a1e089b73 | ||
|
|
0aa98ac2da | ||
|
|
f9ef38cc7a | ||
|
|
a3a215a1ba | ||
|
|
d3ee322d7c | ||
|
|
7ec20e7752 | ||
|
|
08dfb13dbf | ||
|
|
6a5cddd907 | ||
|
|
dc5078306d | ||
|
|
1bc032853c | ||
|
|
e562e4e7b9 | ||
|
|
0ffffa9029 | ||
|
|
0f2303e8d5 | ||
|
|
31f7f8149e | ||
|
|
fe1f57c23f | ||
|
|
29a0b010da | ||
|
|
621e1163f8 | ||
|
|
17d64cfcbe | ||
|
|
7379d2135d | ||
|
|
c55fd06b99 | ||
|
|
d81727a08c | ||
|
|
b582e1592a | ||
|
|
dd8533a320 | ||
|
|
d72a8cbf89 | ||
|
|
fe0876ded6 | ||
|
|
fa15763c5d | ||
|
|
205215d409 | ||
|
|
076bfcde87 | ||
|
|
10510484b5 | ||
|
|
e1dea5b4d3 | ||
|
|
891f8d00cf | ||
|
|
0d409f0fe3 | ||
|
|
4e0c8982c9 | ||
|
|
a741c66c97 | ||
|
|
3e6a7cbdf5 | ||
|
|
5d34aa0afe | ||
|
|
979ae94698 | ||
|
|
8d7b85b26a | ||
|
|
686977a986 | ||
|
|
9c6c782146 | ||
|
|
ca9539b5b6 | ||
|
|
ff25be8839 | ||
|
|
d1ab1b59be | ||
|
|
05bc1865a6 | ||
|
|
6e6e0275ad | ||
|
|
ec3c5b2ca2 | ||
|
|
5289d569b0 | ||
|
|
6f873ff6b5 | ||
|
|
467cbe972c | ||
|
|
bfd1bfe9f0 | ||
|
|
3404a6c699 | ||
|
|
17a4b5155e | ||
|
|
d406e2ed22 | ||
|
|
0bfc9935b2 | ||
|
|
64c6cbe303 | ||
|
|
2245db3e80 | ||
|
|
6f19a50c98 | ||
|
|
c7ce9598a8 | ||
|
|
afcdccf783 | ||
|
|
1bf12842ca | ||
|
|
da8fbee256 | ||
|
|
4021f26e76 | ||
|
|
8eab8438cf | ||
|
|
1b4096b01d | ||
|
|
54caa6e438 | ||
|
|
486eb149f2 | ||
|
|
faa034a205 | ||
|
|
2cd3ebc7a8 | ||
|
|
7e3af6c235 | ||
|
|
dd4de16d1d | ||
|
|
ba8577fa5f | ||
|
|
6192319f8c | ||
|
|
fed368d553 | ||
|
|
20c885418e | ||
|
|
9318ee30bd | ||
|
|
8b6c9574df | ||
|
|
313148136a | ||
|
|
6e1fdc77ae | ||
|
|
2a9582ebb1 | ||
|
|
dd1cd46719 | ||
|
|
9961d9e54d | ||
|
|
7eb12110d1 | ||
|
|
3d62744601 | ||
|
|
17af417235 | ||
|
|
df9d314361 | ||
|
|
610e0c984a | ||
|
|
3688fca126 | ||
|
|
9bc89c784f | ||
|
|
79682f5d52 | ||
|
|
c206ac0335 | ||
|
|
34840175f3 | ||
|
|
6312457425 | ||
|
|
f71e664952 | ||
|
|
80d559f17e | ||
|
|
772aedc263 | ||
|
|
45d7c124c8 | ||
|
|
73abb20b3d | ||
|
|
0baf104a75 | ||
|
|
302d51cdfd | ||
|
|
d3cbfd5467 | ||
|
|
9e3e7e1820 | ||
|
|
18893bf6cd | ||
|
|
f53161d6f5 | ||
|
|
4c9a571106 | ||
|
|
365edcad16 | ||
|
|
ae9047a708 | ||
|
|
9c0e58df8d | ||
|
|
ee34f11c29 | ||
|
|
6ca6d88bff | ||
|
|
65a397fb63 | ||
|
|
0a669077fb | ||
|
|
451688a12e | ||
|
|
d181d89dd2 | ||
|
|
ac14b0d73b | ||
|
|
0f42c65792 | ||
|
|
37b3e980dc | ||
|
|
eb49e011e2 | ||
|
|
e0a73f72ee | ||
|
|
e217ab28c5 | ||
|
|
ca583865ea | ||
|
|
5e7efde311 | ||
|
|
2c7c13420b | ||
|
|
ac4fc3e54e | ||
|
|
46db36a05e | ||
|
|
3d073be990 | ||
|
|
1bc49fe450 | ||
|
|
1f7fe9abcc | ||
|
|
5217abf57a | ||
|
|
2ecbde891a | ||
|
|
bf7fc66646 | ||
|
|
5a94b38e2f | ||
|
|
1bc55f5937 | ||
|
|
04c9542f94 | ||
|
|
17e739f68a | ||
|
|
4f5515fde3 | ||
|
|
ae3ba129ea | ||
|
|
6b92a0fff7 | ||
|
|
d859bb8e67 | ||
|
|
f12bbf71ed | ||
|
|
b723beb545 | ||
|
|
47daaaf14f | ||
|
|
9ba5bae34d | ||
|
|
dbfa2d7994 | ||
|
|
8134f86d1f | ||
|
|
5c60b09bf4 | ||
|
|
20a19d67d0 | ||
|
|
311a2f6023 | ||
|
|
59a3cf93e6 | ||
|
|
a42f095cef | ||
|
|
a29340523f | ||
|
|
5ad7a7c014 | ||
|
|
c6fa7e4aad | ||
|
|
d4b248fbe3 | ||
|
|
48881d081a | ||
|
|
331024414e | ||
|
|
9d0c5349bb | ||
|
|
dc40d8afac | ||
|
|
005877b4b8 | ||
|
|
774d21747a | ||
|
|
12e5b39c05 | ||
|
|
4d5e452ece | ||
|
|
1ba3ecbef3 | ||
|
|
db6695cb02 | ||
|
|
b3f5e039f2 | ||
|
|
c3e17fb185 | ||
|
|
f7ae7fe43a | ||
|
|
e07822350e | ||
|
|
07439ff99c | ||
|
|
f111c245c1 | ||
|
|
c3502da4a0 | ||
|
|
4012108d48 | ||
|
|
4a0605e09b | ||
|
|
250380d73e | ||
|
|
b30f5d782d | ||
|
|
b00cd9b557 | ||
|
|
e11f8f646b | ||
|
|
92b1de8cf8 | ||
|
|
bbafe53a2b | ||
|
|
e239fd970f | ||
|
|
7fa4515c28 | ||
|
|
b40eb0a454 | ||
|
|
5e3e9ddb8e | ||
|
|
a9a5284a67 | ||
|
|
afe504dbbf | ||
|
|
0c484ddcf7 | ||
|
|
8d0e80620a | ||
|
|
1d28506b09 | ||
|
|
1b33dbe2ae | ||
|
|
80654c059d | ||
|
|
999c548e6e | ||
|
|
e42913ae8a | ||
|
|
ee07cbecba | ||
|
|
fffaa9bced | ||
|
|
6195b2c99d | ||
|
|
06aafc96c9 | ||
|
|
2dc0727e36 | ||
|
|
a219feaa60 | ||
|
|
f3a09da340 | ||
|
|
946e345a3f | ||
|
|
6cb8d11b22 | ||
|
|
fdcf9c5327 | ||
|
|
45c714cbb5 | ||
|
|
7352a309a0 | ||
|
|
b297fe6f59 | ||
|
|
e575212c49 | ||
|
|
c52dc10c9e | ||
|
|
d35c913249 | ||
|
|
9bac59b952 | ||
|
|
5016253922 | ||
|
|
03893071fc | ||
|
|
d3dc94c04b | ||
|
|
af0feabb6a | ||
|
|
81ab0a414f | ||
|
|
ecd6ca0172 | ||
|
|
db7ee192f7 | ||
|
|
2ec0b601fa | ||
|
|
79feb6d5d2 | ||
|
|
8013b760e3 | ||
|
|
a29b8e5b36 | ||
|
|
9ef487a7a5 | ||
|
|
563fa46ba4 | ||
|
|
7865c06863 | ||
|
|
3d53b9244d | ||
|
|
f2afa4dd50 | ||
|
|
893ecec55e | ||
|
|
401584dbd8 | ||
|
|
b234b0bded | ||
|
|
b9b2c6a013 | ||
|
|
1d2efa0d25 | ||
|
|
93ec092eaf | ||
|
|
29ae492983 | ||
|
|
b9f6a59a20 | ||
|
|
d74551216f | ||
|
|
d4302ae51b | ||
|
|
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 |
26
.gitignore
vendored
@@ -2,19 +2,31 @@
|
|||||||
default.properties
|
default.properties
|
||||||
gen
|
gen
|
||||||
assets/www/cordova.js
|
assets/www/cordova.js
|
||||||
|
framework/assets/www/.tmp*
|
||||||
local.properties
|
local.properties
|
||||||
framework/proguard.cfg
|
framework/lib
|
||||||
framework/cordova.jar
|
proguard.cfg
|
||||||
framework/cordova-*.jar
|
proguard.cfg
|
||||||
framework/phonegap-*.jar
|
proguard-project.txt
|
||||||
framework/bin
|
framework/bin
|
||||||
framework/test/org/apache/cordova/*.class
|
framework/test/org/apache/cordova/*.class
|
||||||
framework/assets/www/.DS_Store
|
framework/assets/www/.DS_Store
|
||||||
framework/assets/www/cordova-*.js
|
framework/assets/www/cordova-*.js
|
||||||
framework/assets/www/phonegap-*.js
|
framework/assets/www/phonegap-*.js
|
||||||
|
framework/libs
|
||||||
|
test/libs
|
||||||
example
|
example
|
||||||
./test
|
./test
|
||||||
tmp
|
test/bin
|
||||||
*.tmp
|
test/assets/www/.tmp*
|
||||||
test/libs/*.jar
|
tmp/**
|
||||||
bin/node_modules
|
bin/node_modules
|
||||||
|
.metadata
|
||||||
|
tmp/**/*
|
||||||
|
Thumbs.db
|
||||||
|
Desktop.ini
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*.class
|
||||||
|
*.jar
|
||||||
|
|||||||
21
README.md
@@ -15,8 +15,8 @@ indicate that the project has yet to be fully endorsed by the ASF.
|
|||||||
Requires
|
Requires
|
||||||
---
|
---
|
||||||
|
|
||||||
- Java JDK 1.5
|
- Java JDK 1.5 or greater
|
||||||
- Apache ANT
|
- Apache ANT 1.8.0 or greater
|
||||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||||
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
|
- Apache Commons Codec [http://commons.apache.org/codec/](http://commons.apache.org/codec/)
|
||||||
|
|
||||||
@@ -29,11 +29,11 @@ Building
|
|||||||
|
|
||||||
To create your cordova.jar, copy the commons codec:
|
To create your cordova.jar, copy the commons codec:
|
||||||
|
|
||||||
mv commons-codec-1.6.jar framework/libs
|
mv commons-codec-1.7.jar framework/libs
|
||||||
|
|
||||||
then run in the framework directory:
|
then run in the framework directory:
|
||||||
|
|
||||||
android update project -p . -t android-15
|
android update project -p . -t android-17
|
||||||
ant jar
|
ant jar
|
||||||
|
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ Project Commands
|
|||||||
|
|
||||||
These commands live in a generated Cordova Android project.
|
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/emulate .......................... start avd (emulator) named default
|
||||||
./cordova/log .............................. starts logcat
|
./cordova/log .............................. starts logcat
|
||||||
|
|
||||||
@@ -82,15 +82,10 @@ Start adb logcat (console.log calls output here):
|
|||||||
|
|
||||||
./cordova/log
|
./cordova/log
|
||||||
|
|
||||||
Running the [callback/callback-test](http://github.com/callback/callback-test) tests:
|
|
||||||
---
|
|
||||||
|
|
||||||
./bin/test
|
|
||||||
|
|
||||||
Creating a new Cordova Android Project
|
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
|
Importing a Cordova Android Project into Eclipse
|
||||||
----
|
----
|
||||||
@@ -110,5 +105,5 @@ Further Reading
|
|||||||
---
|
---
|
||||||
|
|
||||||
- [http://developer.android.com](http://developer.android.com)
|
- [http://developer.android.com](http://developer.android.com)
|
||||||
- [http://docs.phonegap.com](http://docs.phonegap.com)
|
- [http://incubator.apache.org/cordova/](http://incubator.apache.org/cordova/)
|
||||||
- [http://wiki.phonegap.com](http://wiki.phonegap.com)
|
- [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
|
|
||||||
140
bin/create
@@ -1,4 +1,4 @@
|
|||||||
#! /bin/sh
|
#! /bin/bash
|
||||||
# Licensed to the Apache Software Foundation (ASF) under one
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
# or more contributor license agreements. See the NOTICE file
|
# or more contributor license agreements. See the NOTICE file
|
||||||
# distributed with this work for additional information
|
# distributed with this work for additional information
|
||||||
@@ -23,22 +23,22 @@
|
|||||||
#
|
#
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
if [ -n "$1" ] && [ "$1" == "-h" ]
|
if [ -z "$1" ] || [ "$1" == "-h" ]
|
||||||
then
|
then
|
||||||
echo 'usage: create path package activity'
|
echo 'usage: create path package activity'
|
||||||
echo "Make sure the Android SDK tools folder is in your PATH!"
|
echo "Make sure the Android SDK tools folder is in your PATH!"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
BUILD_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
BUILD_PATH="$( cd "$( dirname "$0" )/.." && pwd )"
|
||||||
VERSION=$(cat $BUILD_PATH/VERSION)
|
VERSION=$(cat "$BUILD_PATH"/VERSION)
|
||||||
|
|
||||||
PROJECT_PATH=${1:-"./example"}
|
PROJECT_PATH="${1:-'./example'}"
|
||||||
PACKAGE=${2:-"org.apache.cordova.example"}
|
PACKAGE=${2:-"org.apache.cordova.example"}
|
||||||
ACTIVITY=${3:-"cordovaExample"}
|
ACTIVITY=${3:-"cordovaExample"}
|
||||||
|
|
||||||
# clobber any existing example
|
# clobber any existing example
|
||||||
if [ -d $PROJECT_PATH ]
|
if [ -d "$PROJECT_PATH" ]
|
||||||
then
|
then
|
||||||
echo "Project already exists! Delete and recreate"
|
echo "Project already exists! Delete and recreate"
|
||||||
exit 1
|
exit 1
|
||||||
@@ -46,70 +46,114 @@ fi
|
|||||||
|
|
||||||
# cleanup after exit and/or on error
|
# cleanup after exit and/or on error
|
||||||
function on_exit {
|
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
|
||||||
# [ -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
|
||||||
# [ -d $BUILD_PATH/framework/libs ] && rmdir $BUILD_PATH/framework/libs
|
if [ -f "$BUILD_PATH"/framework/assets/www/cordova-$VERSION.js ]
|
||||||
[ -f $BUILD_PATH/framework/assets/www/cordova-$VERSION.js ] && rm $BUILD_PATH/framework/assets/www/cordova-$VERSION.js
|
then
|
||||||
[ -f $BUILD_PATH/framework/cordova-$VERSION.jar ] && rm $BUILD_PATH/framework/cordova-$VERSION.jar
|
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 {
|
function on_error {
|
||||||
echo "An error occured. Deleting project..."
|
echo "An unexpected error occurred: $previous_command exited with $?"
|
||||||
[ -d $PROJECT_PATH ] && rm -rf $PROJECT_PATH
|
echo "Deleting project..."
|
||||||
|
[ -d "$PROJECT_PATH" ] && rm -rf "$PROJECT_PATH"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
# we do not want the script to silently fail
|
||||||
|
trap 'previous_command=$this_command; this_command=$BASH_COMMAND' DEBUG
|
||||||
trap on_error ERR
|
trap on_error ERR
|
||||||
trap on_exit EXIT
|
trap on_exit EXIT
|
||||||
|
|
||||||
ANDROID_BIN=$( which android )
|
ANDROID_BIN="${ANDROID_BIN:=$( which android )}"
|
||||||
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
PACKAGE_AS_PATH=$(echo $PACKAGE | sed 's/\./\//g')
|
||||||
ACTIVITY_PATH=$PROJECT_PATH/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
ACTIVITY_PATH="$PROJECT_PATH"/src/$PACKAGE_AS_PATH/$ACTIVITY.java
|
||||||
MANIFEST_PATH=$PROJECT_PATH/AndroidManifest.xml
|
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
|
# update the cordova-android framework for the desired target
|
||||||
$ANDROID_BIN update project --target $TARGET --path $BUILD_PATH/framework &> /dev/null
|
"$ANDROID_BIN" update project --target $TARGET --path "$BUILD_PATH"/framework &> /dev/null
|
||||||
|
|
||||||
if [ ! -e $BUILD_PATH/framework/libs/commons-codec-1.6.jar ]; then
|
if [ ! -e "$BUILD_PATH"/framework/libs/commons-codec-1.7.jar ]; then
|
||||||
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
# Use curl to get the jar (TODO: Support Apache Mirrors)
|
||||||
echo "Downloading common-codecs-1.6 ..."
|
curl -OL http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.7-bin.zip &> /dev/null
|
||||||
curl -OL http://mirror.symnds.com/software/Apache//commons/codec/binaries/commons-codec-1.6-bin.zip &> /dev/null
|
unzip commons-codec-1.7-bin.zip &> /dev/null
|
||||||
unzip commons-codec-1.6-bin.zip &> /dev/null
|
mkdir -p "$BUILD_PATH"/framework/libs
|
||||||
mkdir -p $BUILD_PATH/framework/libs
|
cp commons-codec-1.7/commons-codec-1.7.jar "$BUILD_PATH"/framework/libs
|
||||||
cp commons-codec-1.6/commons-codec-1.6.jar $BUILD_PATH/framework/libs
|
# cleanup yo
|
||||||
# cleanup yo
|
rm commons-codec-1.7-bin.zip && rm -rf commons-codec-1.7
|
||||||
rm commons-codec-1.6-bin.zip && rm -rf commons-codec-1.6
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
# compile cordova.js and cordova.jar
|
# 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
|
# 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
|
# 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/assets $PROJECT_PATH
|
cp -r "$BUILD_PATH"/bin/templates/project/res "$PROJECT_PATH"
|
||||||
cp -r $BUILD_PATH/bin/templates/project/res $PROJECT_PATH
|
|
||||||
|
|
||||||
# copy cordova.js, cordova.jar and res/xml
|
# copy cordova.js, cordova.jar and res/xml
|
||||||
echo "Setting up config and plugins list ..."
|
if [ -d "$BUILD_PATH"/framework ]
|
||||||
cp -r $BUILD_PATH/framework/res/xml $PROJECT_PATH/res
|
then
|
||||||
|
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/assets/www/cordova-$VERSION.js $PROJECT_PATH/assets/www/cordova-$VERSION.js
|
cp "$BUILD_PATH"/framework/cordova-$VERSION.jar "$PROJECT_PATH"/libs/cordova-$VERSION.jar
|
||||||
cp $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
|
# interpolate the activity name and package
|
||||||
echo "Updating Activity and AndroidManifest ..."
|
cp "$BUILD_PATH"/bin/templates/project/Activity.java "$ACTIVITY_PATH"
|
||||||
cp $BUILD_PATH/bin/templates/project/Activity.java $ACTIVITY_PATH
|
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$ACTIVITY_PATH"
|
||||||
sed -i '' -e "s/__ACTIVITY__/${ACTIVITY}/g" $ACTIVITY_PATH
|
replace "s/__ID__/${PACKAGE}/g" "$ACTIVITY_PATH"
|
||||||
sed -i '' -e "s/__ID__/${PACKAGE}/g" $ACTIVITY_PATH
|
|
||||||
|
|
||||||
cp $BUILD_PATH/bin/templates/project/AndroidManifest.xml $MANIFEST_PATH
|
cp "$BUILD_PATH"/bin/templates/project/AndroidManifest.xml "$MANIFEST_PATH"
|
||||||
sed -i '' -e "s/__ACTIVITY__/${ACTIVITY}/g" $MANIFEST_PATH
|
replace "s/__ACTIVITY__/${ACTIVITY}/g" "$MANIFEST_PATH"
|
||||||
sed -i '' -e "s/__PACKAGE__/${PACKAGE}/g" $MANIFEST_PATH
|
replace "s/__PACKAGE__/${PACKAGE}/g" "$MANIFEST_PATH"
|
||||||
|
replace "s/__APILEVEL__/${API_LEVEL}/g" "$MANIFEST_PATH"
|
||||||
|
|
||||||
|
# creating cordova folder and copying run/build/log/launch scripts
|
||||||
|
mkdir "$PROJECT_PATH"/cordova
|
||||||
|
createAppInfoJar
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/appinfo.jar "$PROJECT_PATH"/cordova/appinfo.jar
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/cordova "$PROJECT_PATH"/cordova/cordova
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/build "$PROJECT_PATH"/cordova/build
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/release "$PROJECT_PATH"/cordova/release
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/clean "$PROJECT_PATH"/cordova/clean
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/log "$PROJECT_PATH"/cordova/log
|
||||||
|
cp "$BUILD_PATH"/bin/templates/cordova/run "$PROJECT_PATH"/cordova/run
|
||||||
|
|||||||
@@ -1,10 +1,27 @@
|
|||||||
|
:: Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
:: or more contributor license agreements. See the NOTICE file
|
||||||
|
:: distributed with this work for additional information
|
||||||
|
:: regarding copyright ownership. The ASF licenses this file
|
||||||
|
:: to you under the Apache License, Version 2.0 (the
|
||||||
|
:: "License"); you may not use this file except in compliance
|
||||||
|
:: with the License. You may obtain a copy of the License at
|
||||||
|
::
|
||||||
|
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
::
|
||||||
|
:: Unless required by applicable law or agreed to in writing,
|
||||||
|
:: software distributed under the License is distributed on an
|
||||||
|
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
:: KIND, either express or implied. See the License for the
|
||||||
|
:: specific language governing permissions and limitations
|
||||||
|
:: under the License.
|
||||||
|
|
||||||
@ECHO OFF
|
@ECHO OFF
|
||||||
IF NOT DEFINED JAVA_HOME GOTO MISSING
|
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
|
SET FOUND=%%~$PATH:X
|
||||||
IF NOT DEFINED FOUND GOTO MISSING
|
IF NOT DEFINED FOUND GOTO MISSING
|
||||||
)
|
)
|
||||||
cscript %~dp0\create.js %*
|
cscript "%~dp0\create.js" %*
|
||||||
GOTO END
|
GOTO END
|
||||||
:MISSING
|
:MISSING
|
||||||
ECHO Missing one of the following:
|
ECHO Missing one of the following:
|
||||||
|
|||||||
110
bin/create.js
@@ -33,6 +33,14 @@ function read(filename) {
|
|||||||
f.Close();
|
f.Close();
|
||||||
return s;
|
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) {
|
function write(filename, contents) {
|
||||||
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
var fso=WScript.CreateObject("Scripting.FileSystemObject");
|
||||||
var f=fso.OpenTextFile(filename, 2, true);
|
var f=fso.OpenTextFile(filename, 2, true);
|
||||||
@@ -42,12 +50,27 @@ function write(filename, contents) {
|
|||||||
function replaceInFile(filename, regexp, replacement) {
|
function replaceInFile(filename, regexp, replacement) {
|
||||||
write(filename, read(filename).replace(regexp, replacement));
|
write(filename, read(filename).replace(regexp, replacement));
|
||||||
}
|
}
|
||||||
function exec(s, output) {
|
function exec(command) {
|
||||||
var o=shell.Exec(s);
|
var oShell=shell.Exec(command);
|
||||||
while (o.Status == 0) {
|
while (oShell.Status == 0) {
|
||||||
WScript.Sleep(100);
|
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() {
|
function cleanup() {
|
||||||
@@ -65,11 +88,11 @@ function cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function downloadCommonsCodec() {
|
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
|
// 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 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.FileExists(savePath)) {
|
||||||
if(!fso.FolderExists(ROOT + '\\framework\\libs')) {
|
if(!fso.FolderExists(ROOT + '\\framework\\libs')) {
|
||||||
fso.CreateFolder(libsPath);
|
fso.CreateFolder(libsPath);
|
||||||
@@ -96,11 +119,11 @@ function downloadCommonsCodec() {
|
|||||||
target.CopyHere(source, 256);
|
target.CopyHere(source, 256);
|
||||||
|
|
||||||
// Move the jar into libs
|
// 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
|
// Clean up
|
||||||
fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.6-bin.zip');
|
fso.DeleteFile(ROOT + '\\framework\\libs\\commons-codec-1.7-bin.zip');
|
||||||
fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.6', true);
|
fso.DeleteFolder(ROOT + '\\framework\\libs\\commons-codec-1.7', true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,38 +148,67 @@ if(fso.FolderExists(PROJECT_PATH)) {
|
|||||||
var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
|
var PACKAGE_AS_PATH=PACKAGE.replace(/\./g, '\\');
|
||||||
var ACTIVITY_PATH=PROJECT_PATH+'\\src\\'+PACKAGE_AS_PATH+'\\'+ACTIVITY+'.java';
|
var ACTIVITY_PATH=PROJECT_PATH+'\\src\\'+PACKAGE_AS_PATH+'\\'+ACTIVITY+'.java';
|
||||||
var MANIFEST_PATH=PROJECT_PATH+'\\AndroidManifest.xml';
|
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/,'');
|
var VERSION=read(ROOT+'\\VERSION').replace(/\r\n/,'').replace(/\n/,'');
|
||||||
|
|
||||||
// create the project
|
// create the project
|
||||||
|
WScript.Echo("Creating new android project...");
|
||||||
exec('android.bat create project --target '+TARGET+' --path '+PROJECT_PATH+' --package '+PACKAGE+' --activity '+ACTIVITY);
|
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
|
// build from source. distro should have these files
|
||||||
exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
|
if (!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.jar') &&
|
||||||
|
!fso.FileExists(ROOT+'\\cordova-'+VERSION+'.js')) {
|
||||||
// pull down commons codec if necessary
|
WScript.Echo("Building jar and js files...");
|
||||||
downloadCommonsCodec();
|
// update the cordova framework project to a target that exists on this machine
|
||||||
|
exec('android.bat update project --target '+TARGET+' --path '+ROOT+'\\framework');
|
||||||
exec('ant.bat -f '+ ROOT +'\\framework\\build.xml jar');
|
// pull down commons codec if necessary
|
||||||
|
downloadCommonsCodec();
|
||||||
|
exec('ant.bat -f \"'+ ROOT +'\\framework\\build.xml\" jar');
|
||||||
|
}
|
||||||
|
|
||||||
// copy in the project template
|
// 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
|
// check if we have the source or the distro files
|
||||||
exec('cmd /c copy '+ROOT+'\\framework\\assets\\www\\cordova-'+VERSION+'.js '+PROJECT_PATH+'\\assets\\www\\cordova-'+VERSION+'.js /Y');
|
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
|
// copy cordova scripts
|
||||||
exec('cmd /c copy '+ROOT+'\\framework\\cordova-'+VERSION+'.jar '+PROJECT_PATH+'\\libs\\cordova-'+VERSION+'.jar /Y');
|
fso.CreateFolder(PROJECT_PATH + '\\cordova');
|
||||||
|
createAppInfoJar();
|
||||||
// copy in xml
|
WScript.Echo("Copying cordova command tools...");
|
||||||
exec('cmd /c copy '+ROOT+'\\framework\\res\\xml\\cordova.xml ' + PROJECT_PATH + '\\res\\xml\\cordova.xml /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\appinfo.jar ' + PROJECT_PATH + '\\cordova\\appinfo.jar /Y');
|
||||||
exec('cmd /c copy '+ROOT+'\\framework\\res\\xml\\plugins.xml ' + PROJECT_PATH + '\\res\\xml\\plugins.xml /Y');
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.js ' + PROJECT_PATH + '\\cordova\\cordova.js /Y');
|
||||||
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\cordova.bat ' + PROJECT_PATH + '\\cordova\\cordova.bat /Y');
|
||||||
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\clean.bat ' + PROJECT_PATH + '\\cordova\\clean.bat /Y');
|
||||||
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\build.bat ' + PROJECT_PATH + '\\cordova\\build.bat /Y');
|
||||||
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\log.bat ' + PROJECT_PATH + '\\cordova\\log.bat /Y');
|
||||||
|
exec('%comspec% /c copy "'+ROOT+'"\\bin\\templates\\cordova\\run.bat ' + PROJECT_PATH + '\\cordova\\run.bat /Y');
|
||||||
|
|
||||||
// interpolate the activity name and package
|
// interpolate the activity name and package
|
||||||
|
WScript.Echo("Updating AndroidManifest.xml and Main Activity...");
|
||||||
replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
|
replaceInFile(ACTIVITY_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||||
replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
|
replaceInFile(ACTIVITY_PATH, /__ID__/, PACKAGE);
|
||||||
|
|
||||||
replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
|
replaceInFile(MANIFEST_PATH, /__ACTIVITY__/, ACTIVITY);
|
||||||
replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
|
replaceInFile(MANIFEST_PATH, /__PACKAGE__/, PACKAGE);
|
||||||
|
replaceInFile(MANIFEST_PATH, /__APILEVEL__/, API_LEVEL);
|
||||||
|
|
||||||
cleanup();
|
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/build
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||||
|
|
||||||
|
bash "$CORDOVA_PATH"/cordova build
|
||||||
18
bin/templates/cordova/build.bat
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
:: Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
:: or more contributor license agreements. See the NOTICE file
|
||||||
|
:: distributed with this work for additional information
|
||||||
|
:: regarding copyright ownership. The ASF licenses this file
|
||||||
|
:: to you under the Apache License, Version 2.0 (the
|
||||||
|
:: "License"); you may not use this file except in compliance
|
||||||
|
:: with the License. You may obtain a copy of the License at
|
||||||
|
::
|
||||||
|
:: http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
::
|
||||||
|
:: Unless required by applicable law or agreed to in writing,
|
||||||
|
:: software distributed under the License is distributed on an
|
||||||
|
:: "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
:: KIND, either express or implied. See the License for the
|
||||||
|
:: specific language governing permissions and limitations
|
||||||
|
:: under the License.
|
||||||
|
|
||||||
|
%~dp0\cordova.bat build
|
||||||
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
|
||||||
159
bin/templates/cordova/cordova
Executable file
@@ -0,0 +1,159 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
|
PROJECT_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||||
|
|
||||||
|
function check_devices {
|
||||||
|
# FIXME
|
||||||
|
local devices=`adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device`
|
||||||
|
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
|
||||||
|
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
|
||||||
|
read -t 5 -p "> " avd_id
|
||||||
|
# default value if input timeout
|
||||||
|
if [ $avd_id -eq 1000 ] ; then avd_id=0 ; fi
|
||||||
|
done
|
||||||
|
emulator -cpu-delay 0 -no-boot-anim -cache /tmp/cache -avd ${avd_list[$avd_id]} 1> /dev/null 2>&1 &
|
||||||
|
fi
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function clean {
|
||||||
|
ant clean
|
||||||
|
}
|
||||||
|
# has to be used independently and not in conjunction with other commands
|
||||||
|
function log {
|
||||||
|
adb logcat
|
||||||
|
}
|
||||||
|
|
||||||
|
function run {
|
||||||
|
clean && emulate && wait_for_device && install && launch
|
||||||
|
}
|
||||||
|
|
||||||
|
function install {
|
||||||
|
|
||||||
|
declare -a devices=($(adb devices | awk '/List of devices attached/ { while(getline > 0) { print }}' | grep device | cut -f 1))
|
||||||
|
local device_id="1000" #FIXME: hopefully user does not have 1000 AVDs
|
||||||
|
|
||||||
|
if [ ${#devices[@]} == 0 ]
|
||||||
|
then
|
||||||
|
# should not reach here. Emulator should launch or device should be attached
|
||||||
|
echo "Emulator not running or device not attached. Could not install debug package"
|
||||||
|
exit 70
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${#devices[@]} == 1 ]
|
||||||
|
then
|
||||||
|
export ANDROID_SERIAL=${devices[0]}
|
||||||
|
# User has more than 1 AVD
|
||||||
|
elif [ ${#devices[@]} -gt 1 ]
|
||||||
|
then
|
||||||
|
while [ -z ${devices[$device_id]} ]
|
||||||
|
do
|
||||||
|
echo "Choose from one of the following devices/emulators [0 to $((${#devices[@]}-1))]:"
|
||||||
|
for(( i = 0 ; i < ${#devices[@]} ; i++ ))
|
||||||
|
do
|
||||||
|
echo "$i) ${devices[$i]}"
|
||||||
|
done
|
||||||
|
read -t 5 -p "> " device_id
|
||||||
|
# default value if input timeout
|
||||||
|
if [ $device_id -eq 1000 ] ; then device_id=0 ; fi
|
||||||
|
done
|
||||||
|
export ANDROID_SERIAL=${devices[$device_id]}
|
||||||
|
fi
|
||||||
|
|
||||||
|
ant debug install
|
||||||
|
}
|
||||||
|
|
||||||
|
function build {
|
||||||
|
ant debug
|
||||||
|
}
|
||||||
|
|
||||||
|
function release {
|
||||||
|
ant release
|
||||||
|
}
|
||||||
|
|
||||||
|
function wait_for_device {
|
||||||
|
local i="0"
|
||||||
|
echo -n "Waiting for device..."
|
||||||
|
|
||||||
|
while [ $i -lt 300 ]
|
||||||
|
do
|
||||||
|
if [ $(check_devices) -eq 0 ]
|
||||||
|
then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
sleep 1
|
||||||
|
i=$[i+1]
|
||||||
|
echo -n "."
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Device timeout: emulator has not started in time or device not attached
|
||||||
|
if [ $i -eq 300 ]
|
||||||
|
then
|
||||||
|
echo "device timeout!"
|
||||||
|
exit 69
|
||||||
|
else
|
||||||
|
echo "connected!"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
function launch {
|
||||||
|
local launch_str=$(java -jar "$PROJECT_PATH"/cordova/appinfo.jar "$PROJECT_PATH"/AndroidManifest.xml)
|
||||||
|
adb shell am start -n $launch_str
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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
|
||||||
137
bin/templates/cordova/cordova.js
vendored
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
// 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 device_running() {
|
||||||
|
var local_devices = shell.Exec("%comspec% /c adb devices").StdOut.ReadAll();
|
||||||
|
if(local_devices.match(/\w+\tdevice/)) {
|
||||||
|
WScript.Echo("Yes");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
WScript.Echo("No");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function emulate() {
|
||||||
|
// don't run emulator if a device is plugged in or if emulator is already running
|
||||||
|
if(device_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() {
|
||||||
|
WScript.Echo("Cleaning project...");
|
||||||
|
exec("%comspec% /c ant.bat clean -f "+ROOT+"\\build.xml 2>&1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function build() {
|
||||||
|
WScript.Echo("Building project...");
|
||||||
|
exec("%comspec% /c ant.bat debug -f "+ROOT+"\\build.xml 2>&1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function install() {
|
||||||
|
WScript.Echo("Building/Installing project...");
|
||||||
|
exec("%comspec% /c ant.bat debug install -f "+ROOT+"\\build.xml 2>&1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function log() {
|
||||||
|
shell.Run("%comspec% /c adb logcat");
|
||||||
|
}
|
||||||
|
|
||||||
|
function launch() {
|
||||||
|
WScript.Echo("Launching app...");
|
||||||
|
var launch_str=exec("%comspec% /c java -jar "+ROOT+"\\cordova\\appinfo.jar "+ROOT+"\\AndroidManifest.xml");
|
||||||
|
//WScript.Echo(launch_str);
|
||||||
|
exec("%comspec% /c adb shell am start -n "+launch_str+" 2>&1");
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
var i=0;
|
||||||
|
clean();
|
||||||
|
emulate();
|
||||||
|
WScript.Stdout.Write('Waiting for device...');
|
||||||
|
while(!device_running() && i < 300) {
|
||||||
|
WScript.Stdout.Write('.');
|
||||||
|
WScript.sleep(1000);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
if(i == 300) {
|
||||||
|
WScript.Stderr.WriteLine("device/emulator timeout!");
|
||||||
|
} else {
|
||||||
|
WScript.Stdout.WriteLine("connected!");
|
||||||
|
}
|
||||||
|
install();
|
||||||
|
launch();
|
||||||
|
}
|
||||||
|
var args = WScript.Arguments;
|
||||||
|
if(args.count() != 1) {
|
||||||
|
WScript.StdErr.Write("An error has occured!\n");
|
||||||
|
WScript.Quit(1);
|
||||||
|
}
|
||||||
|
eval(args(0)+"()");
|
||||||
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
|
||||||
|
|
||||||
|
CORDOVA_PATH=$( cd "$( dirname "$0" )/.." && pwd )
|
||||||
|
|
||||||
|
bash "$CORDOVA_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
|
||||||
24
bin/templates/cordova/release
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||||
|
|
||||||
|
bash "$CORDOVA_PATH"/cordova release
|
||||||
24
bin/templates/cordova/run
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
# or more contributor license agreements. See the NOTICE file
|
||||||
|
# distributed with this work for additional information
|
||||||
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
|
# to you under the Apache License, Version 2.0 (the
|
||||||
|
# "License"); you may not use this file except in compliance
|
||||||
|
# with the License. You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
CORDOVA_PATH=$( cd "$( dirname "$0" )" && pwd )
|
||||||
|
|
||||||
|
bash "$CORDOVA_PATH"/cordova run
|
||||||
1
bin/templates/cordova/run.bat
Normal file
@@ -0,0 +1 @@
|
|||||||
|
%~dp0\cordova.bat run
|
||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package __ID__;
|
package __ID__;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import org.apache.cordova.*;
|
import org.apache.cordova.*;
|
||||||
|
|
||||||
@@ -29,7 +28,10 @@ public class __ACTIVITY__ extends DroidGap
|
|||||||
public void onCreate(Bundle savedInstanceState)
|
public void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
super.loadUrl("file:///android_asset/www/index.html");
|
Config.init(this);
|
||||||
|
// Set by <content src="index.html" /> in config.xml
|
||||||
|
super.loadUrl(Config.getStartUrl());
|
||||||
|
//super.loadUrl("file:///android_asset/www/index.html")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
under the License.
|
under the License.
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
<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
|
<supports-screens
|
||||||
android:largeScreens="true"
|
android:largeScreens="true"
|
||||||
android:normalScreens="true"
|
android:normalScreens="true"
|
||||||
@@ -47,9 +47,11 @@
|
|||||||
|
|
||||||
|
|
||||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||||
|
android:hardwareAccelerated="true"
|
||||||
android:debuggable="true">
|
android:debuggable="true">
|
||||||
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
|
<activity android:name="__ACTIVITY__" android:label="@string/app_name"
|
||||||
android:configChanges="orientation|keyboardHidden">
|
android:theme="@android:style/Theme.Black.NoTitleBar"
|
||||||
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@@ -57,5 +59,5 @@
|
|||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="5" />
|
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="__APILEVEL__"/>
|
||||||
</manifest>
|
</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
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
or more contributor license agreements. See the NOTICE file
|
or more contributor license agreements. See the NOTICE file
|
||||||
distributed with this work for additional information
|
distributed with this work for additional information
|
||||||
regarding copyright ownership. The ASF licenses this file
|
regarding copyright ownership. The ASF licenses this file
|
||||||
to you under the Apache License, Version 2.0 (the
|
to you under the Apache License, Version 2.0 (the
|
||||||
"License"); you may not use this file except in compliance
|
"License"); you may not use this file except in compliance
|
||||||
with the License. You may obtain a copy of the License at
|
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,
|
Unless required by applicable law or agreed to in writing,
|
||||||
software distributed under the License is distributed on an
|
software distributed under the License is distributed on an
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
KIND, either express or implied. See the License for the
|
KIND, either express or implied. See the License for the
|
||||||
specific language governing permissions and limitations
|
specific language governing permissions and limitations
|
||||||
under the License.
|
under the License.
|
||||||
-->
|
-->
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=320; user-scalable=no" />
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
<meta name="format-detection" content="telephone=no" />
|
||||||
<title>PhoneGap</title>
|
<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" href="master.css" type="text/css" media="screen" title="no title">
|
<link rel="stylesheet" type="text/css" href="css/index.css" />
|
||||||
<script type="text/javascript" charset="utf-8" src="cordova-1.8.0.js"></script>
|
<title>Hello World</title>
|
||||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
</head>
|
||||||
|
<body>
|
||||||
</head>
|
<div class="app">
|
||||||
<body onload="init();" id="stage" class="theme">
|
<h1>Apache Cordova</h1>
|
||||||
<h1>Welcome to Cordova!</h1>
|
<div id="deviceready" class="blink">
|
||||||
<h2>this file is located at assets/www/index.html</h2>
|
<p class="event listening">Connecting to Device</p>
|
||||||
<div id="info">
|
<p class="event received">Device is Ready</p>
|
||||||
<h4>Platform: <span id="platform"> </span>, Version: <span id="version"> </span></h4>
|
</div>
|
||||||
<h4>UUID: <span id="uuid"> </span>, Name: <span id="name"> </span></h4>
|
</div>
|
||||||
<h4>Width: <span id="width"> </span>, Height: <span id="height">
|
<script type="text/javascript" src="cordova-2.3.0.js"></script>
|
||||||
</span>, Color Depth: <span id="colorDepth"></span></h4>
|
<script type="text/javascript" src="js/index.js"></script>
|
||||||
</div>
|
<script type="text/javascript">
|
||||||
<dl id="accel-data">
|
app.initialize();
|
||||||
<dt>X:</dt><dd id="x"> </dd>
|
</script>
|
||||||
<dt>Y:</dt><dd id="y"> </dd>
|
</body>
|
||||||
<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>
|
|
||||||
</html>
|
</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 + '/../..',
|
var build_path = __dirname + '/../..',
|
||||||
project_path = '/tmp/example',
|
project_path = '/tmp/example',
|
||||||
package_name = 'org.apache.cordova.example',
|
package_name = 'org.apache.cordova.example',
|
||||||
@@ -58,7 +75,7 @@ create_project.on('exit', function(code) {
|
|||||||
// make sure main Activity was added
|
// make sure main Activity was added
|
||||||
path.exists(util.format('%s/src/%s/%s.java', project_path, package_as_path, project_name), function(exists) {
|
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');
|
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
|
// 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) {
|
path.exists(util.format('%s/assets/www/cordova-%s.js', project_path, version), function(exists) {
|
||||||
assert(exists, 'cordova.js did not get added');
|
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
|
// check that project compiles && creates a cordovaExample-debug.apk
|
||||||
var compile_project = spawn('ant', ['debug'], {cwd: project_path});
|
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 exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
<classpathentry kind="src" path="src"/>
|
<classpathentry kind="src" path="src"/>
|
||||||
<classpathentry kind="src" path="gen"/>
|
<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"/>
|
<classpathentry kind="output" path="bin/classes"/>
|
||||||
</classpath>
|
</classpath>
|
||||||
|
|||||||
@@ -51,7 +51,7 @@
|
|||||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||||
android:debuggable="true">
|
android:debuggable="true">
|
||||||
<activity android:name=".StandAlone" android:windowSoftInputMode="adjustPan"
|
<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>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@@ -64,5 +64,5 @@
|
|||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="2" />
|
<uses-sdk android:minSdkVersion="7" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title></title>
|
<title></title>
|
||||||
<script src="cordova-1.8.0.js"></script>
|
<script src="cordova-2.3.0.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|||||||
@@ -26,9 +26,37 @@
|
|||||||
</filterchain>
|
</filterchain>
|
||||||
</loadfile>
|
</loadfile>
|
||||||
|
|
||||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
<!-- check that the version of ant is at least 1.8.0, as is needed
|
||||||
It contains the path to the SDK. It should *NOT* be checked into
|
for the dblQuote property -->
|
||||||
Version Control Systems. -->
|
<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" />
|
<loadproperties srcFile="local.properties" />
|
||||||
|
|
||||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
<!-- 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. -->
|
application and should be checked into Version Control Systems. -->
|
||||||
<loadproperties srcFile="project.properties" />
|
<loadproperties srcFile="project.properties" />
|
||||||
|
|
||||||
<!-- quick check on sdk.dir -->
|
<!-- quick check on sdk.dir -->
|
||||||
<fail
|
<fail
|
||||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
|
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project'"
|
||||||
unless="sdk.dir"
|
unless="sdk.dir"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- version-tag: custom -->
|
<!-- version-tag: custom -->
|
||||||
<!-- extension targets. Uncomment the ones where you want to do custom work
|
<!-- extension targets. Uncomment the ones where you want to do custom work
|
||||||
in between standard targets -->
|
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 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"
|
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. -->
|
<!-- Combine JavaScript files into one cordova-uncompressed.js file. -->
|
||||||
<target name="build-javascript" depends="clean">
|
<target name="build-javascript" depends="clean">
|
||||||
|
|||||||
@@ -10,5 +10,7 @@
|
|||||||
# Indicates whether an apk should be generated for each density.
|
# Indicates whether an apk should be generated for each density.
|
||||||
split.density=false
|
split.density=false
|
||||||
# Project target.
|
# Project target.
|
||||||
target=Google Inc.:Google APIs:15
|
target=Google Inc.:Google APIs:17
|
||||||
apk-configurations=
|
apk-configurations=
|
||||||
|
renderscript.opt.level=O0
|
||||||
|
android.library=true
|
||||||
|
|||||||
60
framework/res/xml/config.xml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?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=".*"/>
|
||||||
|
|
||||||
|
<!-- <content src="http://mysite.com/myapp.html" /> for external pages -->
|
||||||
|
<content src="index.html" />
|
||||||
|
|
||||||
|
<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"/>
|
||||||
|
<plugin name="Device" value="org.apache.cordova.Device"/>
|
||||||
|
<plugin name="Accelerometer" value="org.apache.cordova.AccelListener"/>
|
||||||
|
<plugin name="Compass" value="org.apache.cordova.CompassListener"/>
|
||||||
|
<plugin name="Media" value="org.apache.cordova.AudioHandler"/>
|
||||||
|
<plugin name="Camera" value="org.apache.cordova.CameraLauncher"/>
|
||||||
|
<plugin name="Contacts" value="org.apache.cordova.ContactManager"/>
|
||||||
|
<plugin name="File" value="org.apache.cordova.FileUtils"/>
|
||||||
|
<plugin name="NetworkStatus" value="org.apache.cordova.NetworkManager"/>
|
||||||
|
<plugin name="Notification" value="org.apache.cordova.Notification"/>
|
||||||
|
<plugin name="Storage" value="org.apache.cordova.Storage"/>
|
||||||
|
<plugin name="FileTransfer" value="org.apache.cordova.FileTransfer"/>
|
||||||
|
<plugin name="Capture" value="org.apache.cordova.Capture"/>
|
||||||
|
<plugin name="Battery" value="org.apache.cordova.BatteryListener"/>
|
||||||
|
<plugin name="SplashScreen" value="org.apache.cordova.SplashScreen"/>
|
||||||
|
<plugin name="Echo" value="org.apache.cordova.Echo" />
|
||||||
|
<plugin name="Globalization" value="org.apache.cordova.Globalization"/>
|
||||||
|
<plugin name="InAppBrowser" value="org.apache.cordova.InAppBrowser"/>
|
||||||
|
</plugins>
|
||||||
|
</cordova>
|
||||||
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
-->
|
|
||||||
<cordova>
|
|
||||||
<!--
|
|
||||||
access elements control the Android whitelist.
|
|
||||||
Domains are assumed blocked unless set otherwise
|
|
||||||
-->
|
|
||||||
|
|
||||||
<access origin="http://127.0.0.1*"/> <!-- allow local pages -->
|
|
||||||
|
|
||||||
<!-- <access origin="https://example.com" /> allow any secure requests to example.com -->
|
|
||||||
<!-- <access origin="https://example.com" subdomains="true" /> such as above, but including subdomains, such as www -->
|
|
||||||
<!-- <access origin=".*"/> Allow all domains, suggested development use only -->
|
|
||||||
|
|
||||||
<log level="DEBUG"/>
|
|
||||||
<preference name="useBrowserHistory" value="false" />
|
|
||||||
</cordova>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
package com.phonegap.api;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin interface must be implemented by any plugin classes.
|
|
||||||
*
|
|
||||||
* The execute method is called by the PluginManager.
|
|
||||||
*/
|
|
||||||
public interface IPlugin extends org.apache.cordova.api.IPlugin {
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
package com.phonegap.api;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log to Android logging system.
|
|
||||||
*
|
|
||||||
* Log message can be a string or a printf formatted string with arguments.
|
|
||||||
* See http://developer.android.com/reference/java/util/Formatter.html
|
|
||||||
*/
|
|
||||||
public class LOG extends org.apache.cordova.api.LOG {
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
package com.phonegap.api;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Cordova activity abstract class that is extended by DroidGap.
|
|
||||||
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
|
||||||
*/
|
|
||||||
public abstract class PhonegapActivity extends Activity implements org.apache.cordova.api.CordovaInterface {
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
package com.phonegap.api;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin interface must be implemented by any plugin classes.
|
|
||||||
*
|
|
||||||
* The execute method is called by the PluginManager.
|
|
||||||
*/
|
|
||||||
public abstract class Plugin extends org.apache.cordova.api.Plugin {
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
or more contributor license agreements. See the NOTICE file
|
|
||||||
distributed with this work for additional information
|
|
||||||
regarding copyright ownership. The ASF licenses this file
|
|
||||||
to you under the Apache License, Version 2.0 (the
|
|
||||||
"License"); you may not use this file except in compliance
|
|
||||||
with the License. You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing,
|
|
||||||
software distributed under the License is distributed on an
|
|
||||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
KIND, either express or implied. See the License for the
|
|
||||||
specific language governing permissions and limitations
|
|
||||||
under the License.
|
|
||||||
*/
|
|
||||||
package com.phonegap.api;
|
|
||||||
|
|
||||||
import org.apache.cordova.CordovaWebView;
|
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
|
||||||
|
|
||||||
import android.webkit.WebView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PluginManager is exposed to JavaScript in the Cordova WebView.
|
|
||||||
*
|
|
||||||
* Calling native plugin code can be done by calling PluginManager.exec(...)
|
|
||||||
* from JavaScript.
|
|
||||||
*/
|
|
||||||
public class PluginManager extends org.apache.cordova.api.PluginManager {
|
|
||||||
|
|
||||||
public PluginManager(WebView app, CordovaInterface ctx) throws Exception {
|
|
||||||
super((CordovaWebView) app, ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,24 +19,29 @@
|
|||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
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.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.hardware.Sensor;
|
import android.hardware.Sensor;
|
||||||
import android.hardware.SensorEvent;
|
import android.hardware.SensorEvent;
|
||||||
import android.hardware.SensorEventListener;
|
import android.hardware.SensorEventListener;
|
||||||
import android.hardware.SensorManager;
|
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
|
* This class listens to the accelerometer sensor and stores the latest
|
||||||
* acceleration values x,y,z.
|
* 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 STOPPED = 0;
|
||||||
public static int STARTING = 1;
|
public static int STARTING = 1;
|
||||||
@@ -51,7 +56,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
|||||||
private SensorManager sensorManager; // Sensor manager
|
private SensorManager sensorManager; // Sensor manager
|
||||||
private Sensor mSensor; // Acceleration sensor returned by 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.
|
* 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
|
* Sets the context of the Command. This can then be used to do things like
|
||||||
* get file paths associated with the Activity.
|
* 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.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void setContext(CordovaInterface ctx) {
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
super.setContext(ctx);
|
super.initialize(cordova, webView);
|
||||||
this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
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 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.
|
* @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) {
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
PluginResult.Status status = PluginResult.Status.NO_RESULT;
|
|
||||||
String message = "";
|
|
||||||
PluginResult result = new PluginResult(status, message);
|
|
||||||
result.setKeepCallback(true);
|
|
||||||
|
|
||||||
if (action.equals("start")) {
|
if (action.equals("start")) {
|
||||||
this.callbackId = callbackId;
|
this.callbackContext = callbackContext;
|
||||||
if (this.status != AccelListener.RUNNING) {
|
if (this.status != AccelListener.RUNNING) {
|
||||||
// If not running, then this is an async call, so don't worry about waiting
|
// 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
|
// 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 {
|
} else {
|
||||||
// Unsupported action
|
// 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.");
|
this.fail(AccelListener.ERROR_FAILED_TO_START, "No sensors found to register accelerometer listening to.");
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait until running
|
// Set a timeout callback on the main thread.
|
||||||
long timeout = 2000;
|
Handler handler = new Handler(Looper.getMainLooper());
|
||||||
while ((this.status == STARTING) && (timeout > 0)) {
|
handler.postDelayed(new Runnable() {
|
||||||
timeout = timeout - 100;
|
public void run() {
|
||||||
try {
|
AccelListener.this.timeout();
|
||||||
Thread.sleep(100);
|
}
|
||||||
} catch (InterruptedException e) {
|
}, 2000);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (timeout == 0) {
|
|
||||||
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
|
|
||||||
this.fail(AccelListener.ERROR_FAILED_TO_START, "Accelerometer could not be started.");
|
|
||||||
}
|
|
||||||
return this.status;
|
return this.status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,6 +175,18 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
|||||||
this.accuracy = SensorManager.SENSOR_STATUS_UNRELIABLE;
|
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.
|
* 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) {
|
if (this.accuracy >= SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM) {
|
||||||
|
|
||||||
// Save time that event was received
|
// Save time that event was received
|
||||||
this.timestamp = System.nanoTime();
|
this.timestamp = System.currentTimeMillis();
|
||||||
this.x = event.values[0];
|
this.x = event.values[0];
|
||||||
this.y = event.values[1];
|
this.y = event.values[1];
|
||||||
this.z = event.values[2];
|
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
|
// Sends an error back to JS
|
||||||
private void fail(int code, String message) {
|
private void fail(int code, String message) {
|
||||||
// Error object
|
// Error object
|
||||||
@@ -236,16 +257,14 @@ public class AccelListener extends Plugin implements SensorEventListener {
|
|||||||
}
|
}
|
||||||
PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
|
PluginResult err = new PluginResult(PluginResult.Status.ERROR, errorObj);
|
||||||
err.setKeepCallback(true);
|
err.setKeepCallback(true);
|
||||||
|
callbackContext.sendPluginResult(err);
|
||||||
this.error(err, this.callbackId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void win() {
|
private void win() {
|
||||||
// Success return object
|
// Success return object
|
||||||
PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
|
PluginResult result = new PluginResult(PluginResult.Status.OK, this.getAccelerationJSON());
|
||||||
result.setKeepCallback(true);
|
result.setKeepCallback(true);
|
||||||
|
callbackContext.sendPluginResult(result);
|
||||||
this.success(result, this.callbackId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStatus(int status) {
|
private void setStatus(int status) {
|
||||||
|
|||||||
@@ -19,28 +19,30 @@
|
|||||||
|
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
|
import org.apache.cordova.api.CordovaPlugin;
|
||||||
import org.apache.cordova.api.LOG;
|
import org.apache.cordova.api.LOG;
|
||||||
import org.apache.cordova.api.Plugin;
|
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||||
*/
|
*/
|
||||||
public class App extends Plugin {
|
public class App extends CordovaPlugin {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArry of arguments for the plugin.
|
||||||
* @param callbackId The callback id used when calling back into JavaScript.
|
* @param callbackContext The callback context from which we were invoked.
|
||||||
* @return A PluginResult object with a status and message.
|
* @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;
|
PluginResult.Status status = PluginResult.Status.OK;
|
||||||
String result = "";
|
String result = "";
|
||||||
|
|
||||||
@@ -48,8 +50,11 @@ public class App extends Plugin {
|
|||||||
if (action.equals("clearCache")) {
|
if (action.equals("clearCache")) {
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
}
|
}
|
||||||
else if (action.equals("show")) { // TODO @bc - Not in master branch. When should this be called?
|
else if (action.equals("show")) {
|
||||||
ctx.getActivity().runOnUiThread(new Runnable() {
|
// 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() {
|
public void run() {
|
||||||
webView.postMessage("spinner", "stop");
|
webView.postMessage("spinner", "stop");
|
||||||
}
|
}
|
||||||
@@ -59,7 +64,7 @@ public class App extends Plugin {
|
|||||||
this.loadUrl(args.getString(0), args.optJSONObject(1));
|
this.loadUrl(args.getString(0), args.optJSONObject(1));
|
||||||
}
|
}
|
||||||
else if (action.equals("cancelLoadUrl")) {
|
else if (action.equals("cancelLoadUrl")) {
|
||||||
this.cancelLoadUrl();
|
//this.cancelLoadUrl();
|
||||||
}
|
}
|
||||||
else if (action.equals("clearHistory")) {
|
else if (action.equals("clearHistory")) {
|
||||||
this.clearHistory();
|
this.clearHistory();
|
||||||
@@ -73,16 +78,14 @@ public class App extends Plugin {
|
|||||||
else if (action.equals("overrideBackbutton")) {
|
else if (action.equals("overrideBackbutton")) {
|
||||||
this.overrideBackbutton(args.getBoolean(0));
|
this.overrideBackbutton(args.getBoolean(0));
|
||||||
}
|
}
|
||||||
else if (action.equals("isBackbuttonOverridden")) {
|
|
||||||
boolean b = this.isBackbuttonOverridden();
|
|
||||||
return new PluginResult(status, b);
|
|
||||||
}
|
|
||||||
else if (action.equals("exitApp")) {
|
else if (action.equals("exitApp")) {
|
||||||
this.exitApp();
|
this.exitApp();
|
||||||
}
|
}
|
||||||
return new PluginResult(status, result);
|
callbackContext.sendPluginResult(new PluginResult(status, result));
|
||||||
|
return true;
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,14 +160,6 @@ public class App extends Plugin {
|
|||||||
this.webView.showWebPage(url, openExternal, clearHistory, params);
|
this.webView.showWebPage(url, openExternal, clearHistory, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel loadUrl before it has been loaded (Only works on a CordovaInterface class)
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void cancelLoadUrl() {
|
|
||||||
this.ctx.cancelLoadUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear page history for the app.
|
* Clear page history for the app.
|
||||||
*/
|
*/
|
||||||
@@ -177,7 +172,11 @@ public class App extends Plugin {
|
|||||||
* This is the same as pressing the backbutton on Android device.
|
* This is the same as pressing the backbutton on Android device.
|
||||||
*/
|
*/
|
||||||
public void backHistory() {
|
public void backHistory() {
|
||||||
this.webView.backHistory();
|
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
webView.backHistory();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -202,13 +201,14 @@ public class App extends Plugin {
|
|||||||
LOG.i("DroidGap", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!");
|
LOG.i("DroidGap", "WARNING: Volume Button Default Behaviour will be overridden. The volume event will be fired!");
|
||||||
webView.bindButton(button, override);
|
webView.bindButton(button, override);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the Android back button is overridden by the user.
|
* Return whether the Android back button is overridden by the user.
|
||||||
*
|
*
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public boolean isBackbuttonOverridden() {
|
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;
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
|
import org.apache.cordova.api.CordovaPlugin;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import org.apache.cordova.api.Plugin;
|
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -39,7 +42,7 @@ import java.util.HashMap;
|
|||||||
* android_asset: file name must start with /android_asset/sound.mp3
|
* android_asset: file name must start with /android_asset/sound.mp3
|
||||||
* sdcard: file name is just sound.mp3
|
* sdcard: file name is just sound.mp3
|
||||||
*/
|
*/
|
||||||
public class AudioHandler extends Plugin {
|
public class AudioHandler extends CordovaPlugin {
|
||||||
|
|
||||||
public static String TAG = "AudioHandler";
|
public static String TAG = "AudioHandler";
|
||||||
HashMap<String, AudioPlayer> players; // Audio player object
|
HashMap<String, AudioPlayer> players; // Audio player object
|
||||||
@@ -57,69 +60,64 @@ public class AudioHandler extends Plugin {
|
|||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @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.
|
* @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;
|
PluginResult.Status status = PluginResult.Status.OK;
|
||||||
String result = "";
|
String result = "";
|
||||||
|
|
||||||
try {
|
if (action.equals("startRecordingAudio")) {
|
||||||
if (action.equals("startRecordingAudio")) {
|
this.startRecordingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
else if (action.equals("stopRecordingAudio")) {
|
||||||
|
this.stopRecordingAudio(args.getString(0));
|
||||||
/**
|
}
|
||||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
else if (action.equals("startPlayingAudio")) {
|
||||||
* @param action The action to execute
|
this.startPlayingAudio(args.getString(0), FileUtils.stripFileProtocol(args.getString(1)));
|
||||||
* @return T=returns value
|
}
|
||||||
*/
|
else if (action.equals("seekToAudio")) {
|
||||||
public boolean isSynch(String action) {
|
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||||
if (action.equals("getCurrentPositionAudio")) {
|
}
|
||||||
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
else if (action.equals("getDurationAudio")) {
|
else if (action.equals("getDurationAudio")) {
|
||||||
|
float f = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||||
|
callbackContext.sendPluginResult(new PluginResult(status, f));
|
||||||
return true;
|
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();
|
this.players.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop all audio players and recorders on navigate.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onReset() {
|
||||||
|
onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a message is sent to plugin.
|
* Called when a message is sent to plugin.
|
||||||
*
|
*
|
||||||
@@ -149,7 +155,7 @@ public class AudioHandler extends Plugin {
|
|||||||
|
|
||||||
// Get all audio players and pause them
|
// Get all audio players and pause them
|
||||||
for (AudioPlayer audio : this.players.values()) {
|
for (AudioPlayer audio : this.players.values()) {
|
||||||
if (audio.getState() == AudioPlayer.MEDIA_RUNNING) {
|
if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) {
|
||||||
this.pausedForPhone.add(audio);
|
this.pausedForPhone.add(audio);
|
||||||
audio.pausePlaying();
|
audio.pausePlaying();
|
||||||
}
|
}
|
||||||
@@ -192,12 +198,11 @@ public class AudioHandler extends Plugin {
|
|||||||
* @param file The name of the file
|
* @param file The name of the file
|
||||||
*/
|
*/
|
||||||
public void startRecordingAudio(String id, String file) {
|
public void startRecordingAudio(String id, String file) {
|
||||||
// If already recording, then just return;
|
AudioPlayer audio = this.players.get(id);
|
||||||
if (this.players.containsKey(id)) {
|
if ( audio == null) {
|
||||||
return;
|
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);
|
audio.startRecording(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,7 +214,6 @@ public class AudioHandler extends Plugin {
|
|||||||
AudioPlayer audio = this.players.get(id);
|
AudioPlayer audio = this.players.get(id);
|
||||||
if (audio != null) {
|
if (audio != null) {
|
||||||
audio.stopRecording();
|
audio.stopRecording();
|
||||||
this.players.remove(id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,7 +225,7 @@ public class AudioHandler extends Plugin {
|
|||||||
public void startPlayingAudio(String id, String file) {
|
public void startPlayingAudio(String id, String file) {
|
||||||
AudioPlayer audio = this.players.get(id);
|
AudioPlayer audio = this.players.get(id);
|
||||||
if (audio == null) {
|
if (audio == null) {
|
||||||
audio = new AudioPlayer(this, id);
|
audio = new AudioPlayer(this, id, file);
|
||||||
this.players.put(id, audio);
|
this.players.put(id, audio);
|
||||||
}
|
}
|
||||||
audio.startPlaying(file);
|
audio.startPlaying(file);
|
||||||
@@ -230,7 +234,7 @@ public class AudioHandler extends Plugin {
|
|||||||
/**
|
/**
|
||||||
* Seek to a location.
|
* Seek to a location.
|
||||||
* @param id The id of the audio player
|
* @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) {
|
public void seekToAudio(String id, int milliseconds) {
|
||||||
AudioPlayer audio = this.players.get(id);
|
AudioPlayer audio = this.players.get(id);
|
||||||
@@ -292,7 +296,7 @@ public class AudioHandler extends Plugin {
|
|||||||
|
|
||||||
// If not already open, then open the file
|
// If not already open, then open the file
|
||||||
else {
|
else {
|
||||||
audio = new AudioPlayer(this, id);
|
audio = new AudioPlayer(this, id, file);
|
||||||
this.players.put(id, audio);
|
this.players.put(id, audio);
|
||||||
return (audio.getDuration(file));
|
return (audio.getDuration(file));
|
||||||
}
|
}
|
||||||
@@ -305,7 +309,7 @@ public class AudioHandler extends Plugin {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public void setAudioOutputDevice(int output) {
|
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) {
|
if (output == 2) {
|
||||||
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
|
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
|
||||||
}
|
}
|
||||||
@@ -324,7 +328,7 @@ public class AudioHandler extends Plugin {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public int getAudioOutputDevice() {
|
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) {
|
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
|
||||||
return 1;
|
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 {
|
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
|
||||||
|
|
||||||
private static final String LOG_TAG = "AudioPlayer";
|
// AudioPlayer modes
|
||||||
|
public enum MODE { NONE, PLAY, RECORD };
|
||||||
|
|
||||||
// AudioPlayer states
|
// AudioPlayer states
|
||||||
public static int MEDIA_NONE = 0;
|
public enum STATE { MEDIA_NONE,
|
||||||
public static int MEDIA_STARTING = 1;
|
MEDIA_STARTING,
|
||||||
public static int MEDIA_RUNNING = 2;
|
MEDIA_RUNNING,
|
||||||
public static int MEDIA_PAUSED = 3;
|
MEDIA_PAUSED,
|
||||||
public static int MEDIA_STOPPED = 4;
|
MEDIA_STOPPED,
|
||||||
|
MEDIA_LOADING
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "AudioPlayer";
|
||||||
|
|
||||||
// AudioPlayer message ids
|
// AudioPlayer message ids
|
||||||
private static int MEDIA_STATE = 1;
|
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_NETWORK = 2;
|
||||||
private static int MEDIA_ERR_DECODE = 3;
|
private static int MEDIA_ERR_DECODE = 3;
|
||||||
private static int MEDIA_ERR_NONE_SUPPORTED = 4;
|
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 AudioHandler handler; // The AudioHandler object
|
||||||
private String tempFile = null; // Temporary recording file name
|
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 MediaPlayer mPlayer = null; // Audio player object
|
private STATE state = STATE.MEDIA_NONE; // State of recording or playback
|
||||||
private boolean prepareOnly = false;
|
|
||||||
|
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.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param handler The audio handler object
|
* @param handler The audio handler object
|
||||||
* @param id The id of this audio player
|
* @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.handler = handler;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
this.audioFile = file;
|
||||||
|
this.recorder = new MediaRecorder();
|
||||||
|
|
||||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
|
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.3gp";
|
||||||
} else {
|
} 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() {
|
public void destroy() {
|
||||||
// Stop any play or record
|
// Stop any play or record
|
||||||
if (this.mPlayer != null) {
|
if (this.player != null) {
|
||||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||||
this.mPlayer.stop();
|
this.player.stop();
|
||||||
this.setState(MEDIA_STOPPED);
|
this.setState(STATE.MEDIA_STOPPED);
|
||||||
}
|
}
|
||||||
this.mPlayer.release();
|
this.player.release();
|
||||||
this.mPlayer = null;
|
this.player = null;
|
||||||
}
|
}
|
||||||
if (this.recorder != null) {
|
if (this.recorder != null) {
|
||||||
this.stopRecording();
|
this.stopRecording();
|
||||||
@@ -114,18 +126,17 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Start recording the specified file.
|
* Start recording the specified file.
|
||||||
*
|
*
|
||||||
* @param file The name of the file
|
* @param file The name of the file
|
||||||
*/
|
*/
|
||||||
public void startRecording(String 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.");
|
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
|
||||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||||
}
|
break;
|
||||||
// Make sure we're not already recording
|
case NONE:
|
||||||
else if (this.recorder == null) {
|
|
||||||
this.audioFile = file;
|
this.audioFile = file;
|
||||||
this.recorder = new MediaRecorder();
|
|
||||||
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||||
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
||||||
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
||||||
@@ -133,18 +144,18 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
try {
|
try {
|
||||||
this.recorder.prepare();
|
this.recorder.prepare();
|
||||||
this.recorder.start();
|
this.recorder.start();
|
||||||
this.setState(MEDIA_RUNNING);
|
this.setState(STATE.MEDIA_RUNNING);
|
||||||
return;
|
return;
|
||||||
} catch (IllegalStateException e) {
|
} catch (IllegalStateException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', "+MEDIA_ERROR+", { \"code\":"+MEDIA_ERR_ABORTED+"});");
|
||||||
}
|
break;
|
||||||
else {
|
case RECORD:
|
||||||
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
|
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()
|
f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()
|
||||||
+ File.separator + file));
|
+ File.separator + file));
|
||||||
} else {
|
} 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() {
|
public void stopRecording() {
|
||||||
if (this.recorder != null) {
|
if (this.recorder != null) {
|
||||||
try{
|
try{
|
||||||
if (this.state == MEDIA_RUNNING) {
|
if (this.state == STATE.MEDIA_RUNNING) {
|
||||||
this.recorder.stop();
|
this.recorder.stop();
|
||||||
this.setState(MEDIA_STOPPED);
|
this.setState(STATE.MEDIA_STOPPED);
|
||||||
}
|
}
|
||||||
|
this.recorder.reset();
|
||||||
this.moveFile(this.audioFile);
|
this.moveFile(this.audioFile);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
@@ -183,81 +195,22 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
// Playback
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start or resume playing audio file.
|
* Start or resume playing audio file.
|
||||||
*
|
*
|
||||||
* @param file The name of the audio file.
|
* @param file The name of the audio file.
|
||||||
*/
|
*/
|
||||||
public void startPlaying(String file) {
|
public void startPlaying(String file) {
|
||||||
if (this.recorder != null) {
|
if (this.readyPlayer(file) && this.player != null) {
|
||||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
this.player.start();
|
||||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_ABORTED + "});");
|
this.setState(STATE.MEDIA_RUNNING);
|
||||||
}
|
this.seekOnPrepared = 0; //insures this is always reset
|
||||||
|
} else {
|
||||||
// If this is a new request to play audio, or stopped
|
this.prepareOnly = false;
|
||||||
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 + "});");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,10 +218,13 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
* Seek or jump to a new time in the track.
|
* Seek or jump to a new time in the track.
|
||||||
*/
|
*/
|
||||||
public void seekToPlaying(int milliseconds) {
|
public void seekToPlaying(int milliseconds) {
|
||||||
if (this.mPlayer != null) {
|
if (this.readyPlayer(this.audioFile)) {
|
||||||
this.mPlayer.seekTo(milliseconds);
|
this.player.seekTo(milliseconds);
|
||||||
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
|
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() {
|
public void pausePlaying() {
|
||||||
|
|
||||||
// If playing, then pause
|
// If playing, then pause
|
||||||
if (this.state == MEDIA_RUNNING) {
|
if (this.state == STATE.MEDIA_RUNNING && this.player != null) {
|
||||||
this.mPlayer.pause();
|
this.player.pause();
|
||||||
this.setState(MEDIA_PAUSED);
|
this.setState(STATE.MEDIA_PAUSED);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state);
|
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: " + this.state.ordinal());
|
||||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
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.
|
* Stop playing the audio file.
|
||||||
*/
|
*/
|
||||||
public void stopPlaying() {
|
public void stopPlaying() {
|
||||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||||
this.mPlayer.stop();
|
this.player.pause();
|
||||||
this.setState(MEDIA_STOPPED);
|
this.player.seekTo(0);
|
||||||
|
Log.d(LOG_TAG, "stopPlaying is calling stopped");
|
||||||
|
this.setState(STATE.MEDIA_STOPPED);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state);
|
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: " + this.state.ordinal());
|
||||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_ERROR + ", { \"code\":" + MEDIA_ERR_NONE_ACTIVE + "});");
|
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.
|
* 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) {
|
public void onCompletion(MediaPlayer player) {
|
||||||
this.setState(MEDIA_STOPPED);
|
Log.d(LOG_TAG, "on completion is calling stopped");
|
||||||
|
this.setState(STATE.MEDIA_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current position of playback.
|
* Get current position of playback.
|
||||||
*
|
*
|
||||||
* @return position in msec or -1 if not playing
|
* @return position in msec or -1 if not playing
|
||||||
*/
|
*/
|
||||||
public long getCurrentPosition() {
|
public long getCurrentPosition() {
|
||||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
if ((this.state == STATE.MEDIA_RUNNING) || (this.state == STATE.MEDIA_PAUSED)) {
|
||||||
int curPos = this.mPlayer.getCurrentPosition();
|
int curPos = this.player.getCurrentPosition();
|
||||||
this.handler.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
this.handler.webView.sendJavascript("cordova.require('cordova/plugin/Media').onStatus('" + this.id + "', " + MEDIA_POSITION + ", " + curPos / 1000.0f + ");");
|
||||||
return curPos;
|
return curPos;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -330,7 +289,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
/**
|
/**
|
||||||
* Determine if playback file is streaming or local.
|
* Determine if playback file is streaming or local.
|
||||||
* It is streaming if file name starts with "http://"
|
* It is streaming if file name starts with "http://"
|
||||||
*
|
*
|
||||||
* @param file The file name
|
* @param file The file name
|
||||||
* @return T=streaming, F=local
|
* @return T=streaming, F=local
|
||||||
*/
|
*/
|
||||||
@@ -345,7 +304,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the duration of the audio file.
|
* Get the duration of the audio file.
|
||||||
*
|
*
|
||||||
* @param file The name of the audio file.
|
* @param file The name of the audio file.
|
||||||
* @return The duration in msec.
|
* @return The duration in msec.
|
||||||
* -1=can't be determined
|
* -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 audio file already loaded and started, then return duration
|
||||||
if (this.mPlayer != null) {
|
if (this.player != null) {
|
||||||
return this.duration;
|
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.
|
* Callback to be invoked when the media source is ready for playback.
|
||||||
*
|
*
|
||||||
* @param mPlayer The MediaPlayer that is ready for playback
|
* @param player The MediaPlayer that is ready for playback
|
||||||
*/
|
*/
|
||||||
public void onPrepared(MediaPlayer mPlayer) {
|
public void onPrepared(MediaPlayer player) {
|
||||||
// Listen for playback completion
|
// 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 start playing after prepared
|
||||||
if (!this.prepareOnly) {
|
if (!this.prepareOnly) {
|
||||||
|
this.player.start();
|
||||||
// Start playing
|
this.setState(STATE.MEDIA_RUNNING);
|
||||||
this.mPlayer.start();
|
this.seekOnPrepared = 0; //reset only when played
|
||||||
|
} else {
|
||||||
// Set player init flag
|
this.setState(STATE.MEDIA_STARTING);
|
||||||
this.setState(MEDIA_RUNNING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save off duration
|
// Save off duration
|
||||||
this.duration = getDurationInSeconds();
|
this.duration = getDurationInSeconds();
|
||||||
this.prepareOnly = false;
|
// reset prepare only flag
|
||||||
|
this.prepareOnly = true;
|
||||||
|
|
||||||
// Send status notification to JavaScript
|
// 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
|
* By default Android returns the length of audio in mills but we want seconds
|
||||||
*
|
*
|
||||||
* @return length of clip in seconds
|
* @return length of clip in seconds
|
||||||
*/
|
*/
|
||||||
private float getDurationInSeconds() {
|
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
|
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||||
* (other errors will throw exceptions at method call time).
|
* (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 arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED)
|
||||||
* @param arg2 an extra code, specific to the error.
|
* @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 + ")");
|
Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2 + ")");
|
||||||
|
|
||||||
// TODO: Not sure if this needs to be sent?
|
// TODO: Not sure if this needs to be sent?
|
||||||
this.mPlayer.stop();
|
this.player.stop();
|
||||||
this.mPlayer.release();
|
this.player.release();
|
||||||
|
|
||||||
// Send error notification to JavaScript
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the state and send it to JavaScript.
|
* Set the state and send it to JavaScript.
|
||||||
*
|
*
|
||||||
* @param state
|
* @param state
|
||||||
*/
|
*/
|
||||||
private void setState(int state) {
|
private void setState(STATE state) {
|
||||||
if (this.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;
|
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.
|
* Get the audio state.
|
||||||
*
|
*
|
||||||
* @return int
|
* @return int
|
||||||
*/
|
*/
|
||||||
public int getState() {
|
public int getState() {
|
||||||
return this.state;
|
return this.state.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -459,6 +429,120 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
|||||||
* @param volume
|
* @param volume
|
||||||
*/
|
*/
|
||||||
public void setVolume(float 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;
|
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.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -30,13 +31,13 @@ import android.content.Intent;
|
|||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class BatteryListener extends Plugin {
|
public class BatteryListener extends CordovaPlugin {
|
||||||
|
|
||||||
private static final String LOG_TAG = "BatteryManager";
|
private static final String LOG_TAG = "BatteryManager";
|
||||||
|
|
||||||
BroadcastReceiver receiver;
|
BroadcastReceiver receiver;
|
||||||
|
|
||||||
private String batteryCallbackId = null;
|
private CallbackContext batteryCallbackContext = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* 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 action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @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.
|
* @return True if the action was valid, false if not.
|
||||||
*/
|
*/
|
||||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
|
|
||||||
String result = "Unsupported Operation: " + action;
|
|
||||||
|
|
||||||
if (action.equals("start")) {
|
if (action.equals("start")) {
|
||||||
if (this.batteryCallbackId != null) {
|
if (this.batteryCallbackContext != null) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, "Battery listener already running.");
|
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
|
// We need to listen to power events to update battery status
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
@@ -73,23 +72,25 @@ public class BatteryListener extends Plugin {
|
|||||||
updateBatteryInfo(intent);
|
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
|
// 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 pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||||
pluginResult.setKeepCallback(true);
|
pluginResult.setKeepCallback(true);
|
||||||
return pluginResult;
|
callbackContext.sendPluginResult(pluginResult);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (action.equals("stop")) {
|
else if (action.equals("stop")) {
|
||||||
removeBatteryListener();
|
removeBatteryListener();
|
||||||
this.sendUpdate(new JSONObject(), false); // release status callback in JS side
|
this.sendUpdate(new JSONObject(), false); // release status callback in JS side
|
||||||
this.batteryCallbackId = null;
|
this.batteryCallbackContext = null;
|
||||||
return new PluginResult(PluginResult.Status.OK);
|
callbackContext.success();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PluginResult(status, result);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,13 +100,20 @@ public class BatteryListener extends Plugin {
|
|||||||
removeBatteryListener();
|
removeBatteryListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop battery receiver.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
removeBatteryListener();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the battery receiver and set it to null.
|
* Stop the battery receiver and set it to null.
|
||||||
*/
|
*/
|
||||||
private void removeBatteryListener() {
|
private void removeBatteryListener() {
|
||||||
if (this.receiver != null) {
|
if (this.receiver != null) {
|
||||||
try {
|
try {
|
||||||
this.ctx.getActivity().unregisterReceiver(this.receiver);
|
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||||
this.receiver = null;
|
this.receiver = null;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "Error unregistering battery receiver: " + e.getMessage(), 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
|
* @param connection the network info to set as navigator.connection
|
||||||
*/
|
*/
|
||||||
private void sendUpdate(JSONObject info, boolean keepCallback) {
|
private void sendUpdate(JSONObject info, boolean keepCallback) {
|
||||||
if (this.batteryCallbackId != null) {
|
if (this.batteryCallbackContext != null) {
|
||||||
PluginResult result = new PluginResult(PluginResult.Status.OK, info);
|
PluginResult result = new PluginResult(PluginResult.Status.OK, info);
|
||||||
result.setKeepCallback(keepCallback);
|
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.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
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.LOG;
|
||||||
import org.apache.cordova.api.Plugin;
|
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
//import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Bitmap.CompressFormat;
|
import android.graphics.Bitmap.CompressFormat;
|
||||||
|
import android.media.MediaScannerConnection;
|
||||||
|
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Environment;
|
||||||
import android.provider.MediaStore;
|
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,
|
* 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
|
* and returns the captured image. When the camera view is closed, the screen displayed before
|
||||||
* the camera view was shown is redisplayed.
|
* 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 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)
|
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 Uri imageUri; // Uri of captured image
|
||||||
private int encodingType; // Type of encoding to use
|
private int encodingType; // Type of encoding to use
|
||||||
private int mediaType; // What type of media to retrieve
|
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 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!
|
//This should never be null!
|
||||||
//private CordovaInterface cordova;
|
//private CordovaInterface cordova;
|
||||||
|
|
||||||
@@ -100,49 +111,56 @@ public class CameraLauncher extends Plugin {
|
|||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArry of arguments for the plugin.
|
||||||
* @param callbackId The callback id used when calling back into JavaScript.
|
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||||
* @return A PluginResult object with a status and message.
|
* @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;
|
this.callbackContext = callbackContext;
|
||||||
String result = "";
|
|
||||||
this.callbackId = callbackId;
|
|
||||||
|
|
||||||
try {
|
if (action.equals("takePicture")) {
|
||||||
if (action.equals("takePicture")) {
|
int srcType = CAMERA;
|
||||||
int srcType = CAMERA;
|
int destType = FILE_URI;
|
||||||
int destType = FILE_URI;
|
this.saveToPhotoAlbum = false;
|
||||||
this.targetHeight = 0;
|
this.targetHeight = 0;
|
||||||
this.targetWidth = 0;
|
this.targetWidth = 0;
|
||||||
this.encodingType = JPEG;
|
this.encodingType = JPEG;
|
||||||
this.mediaType = PICTURE;
|
this.mediaType = PICTURE;
|
||||||
this.mQuality = 80;
|
this.mQuality = 80;
|
||||||
|
|
||||||
this.mQuality = args.getInt(0);
|
this.mQuality = args.getInt(0);
|
||||||
destType = args.getInt(1);
|
destType = args.getInt(1);
|
||||||
srcType = args.getInt(2);
|
srcType = args.getInt(2);
|
||||||
this.targetWidth = args.getInt(3);
|
this.targetWidth = args.getInt(3);
|
||||||
this.targetHeight = args.getInt(4);
|
this.targetHeight = args.getInt(4);
|
||||||
this.encodingType = args.getInt(5);
|
this.encodingType = args.getInt(5);
|
||||||
this.mediaType = args.getInt(6);
|
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) {
|
// If the user specifies a 0 or smaller width/height
|
||||||
this.takePicture(destType, encodingType);
|
// make it -1 so later comparisons succeed
|
||||||
}
|
if (this.targetWidth < 1) {
|
||||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
this.targetWidth = -1;
|
||||||
this.getImage(srcType, destType);
|
|
||||||
}
|
|
||||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
|
||||||
r.setKeepCallback(true);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
return new PluginResult(status, result);
|
if (this.targetHeight < 1) {
|
||||||
} catch (JSONException e) {
|
this.targetHeight = -1;
|
||||||
e.printStackTrace();
|
}
|
||||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
|
||||||
|
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) {
|
public void takePicture(int returnType, int encodingType) {
|
||||||
// Save the number of images currently on disk for later
|
// Save the number of images currently on disk for later
|
||||||
this.numPics = queryImgDB().getCount();
|
this.numPics = queryImgDB(whichContentStore()).getCount();
|
||||||
|
|
||||||
// Display camera
|
// Display camera
|
||||||
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
|
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
|
||||||
|
|
||||||
// Specify file so that large image is captured and returned
|
// Specify file so that large image is captured and returned
|
||||||
// TODO: What if there isn't any external storage?
|
|
||||||
File photo = createCaptureFile(encodingType);
|
File photo = createCaptureFile(encodingType);
|
||||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||||
this.imageUri = Uri.fromFile(photo);
|
this.imageUri = Uri.fromFile(photo);
|
||||||
|
|
||||||
if (this.ctx != null) {
|
if (this.cordova != null) {
|
||||||
this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
|
||||||
}
|
}
|
||||||
// else
|
// else
|
||||||
// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
|
// 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) {
|
private File createCaptureFile(int encodingType) {
|
||||||
File photo = null;
|
File photo = null;
|
||||||
if (encodingType == JPEG) {
|
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) {
|
} 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 {
|
} else {
|
||||||
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
|
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
|
||||||
}
|
}
|
||||||
@@ -228,27 +245,362 @@ public class CameraLauncher extends Plugin {
|
|||||||
|
|
||||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
if (this.ctx != null) {
|
if (this.cordova != null) {
|
||||||
this.ctx.startActivityForResult((Plugin) this, Intent.createChooser(intent,
|
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
||||||
new String(title)), (srcType + 1) * 16 + returnType + 1);
|
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.
|
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||||
* @return Bitmap A new Bitmap object of the same bitmap after scaling.
|
* 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 (bitmap == null) {
|
||||||
|
// Try to get the bitmap from intent.
|
||||||
|
bitmap = (Bitmap)intent.getExtras().get("data");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double-check the bitmap.
|
||||||
|
if (bitmap == null) {
|
||||||
|
Log.d(LOG_TAG, "I either have a null image path or bitmap");
|
||||||
|
this.failPicture("Unable to create bitmap!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rotate != 0 && this.correctOrientation) {
|
||||||
|
bitmap = getRotatedBitmap(rotate, bitmap, exif);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 &&
|
||||||
|
!this.correctOrientation) {
|
||||||
|
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);
|
||||||
|
if (unscaledBitmap == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 newWidth = this.targetWidth;
|
||||||
int newHeight = this.targetHeight;
|
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 no new width or height were specified return the original bitmap
|
||||||
if (newWidth <= 0 && newHeight <= 0) {
|
if (newWidth <= 0 && newHeight <= 0) {
|
||||||
return bitmap;
|
newWidth = origWidth;
|
||||||
|
newHeight = origHeight;
|
||||||
}
|
}
|
||||||
// Only the width was specified
|
// Only the width was specified
|
||||||
else if (newWidth > 0 && newHeight <= 0) {
|
else if (newWidth > 0 && newHeight <= 0) {
|
||||||
@@ -275,216 +627,68 @@ public class CameraLauncher extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bitmap retval = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
|
int[] retval = new int[2];
|
||||||
bitmap.recycle();
|
retval[0] = newWidth;
|
||||||
|
retval[1] = newHeight;
|
||||||
return retval;
|
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(),
|
* @param srcWidth
|
||||||
* allowing you to identify who this result came from.
|
* @param srcHeight
|
||||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
* @param dstWidth
|
||||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
* @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
|
if (srcAspect > dstAspect) {
|
||||||
int srcType = (requestCode / 16) - 1;
|
return srcWidth / dstWidth;
|
||||||
int destType = (requestCode % 16) - 1;
|
} else {
|
||||||
int rotate = 0;
|
return srcHeight / dstHeight;
|
||||||
|
|
||||||
// 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 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.
|
* Creates a cursor that can be used to determine how many images we have.
|
||||||
*
|
*
|
||||||
* @return a cursor
|
* @return a cursor
|
||||||
*/
|
*/
|
||||||
private Cursor queryImgDB() {
|
private Cursor queryImgDB(Uri contentStore) {
|
||||||
return this.ctx.getActivity().getContentResolver().query(
|
return this.cordova.getActivity().getContentResolver().query(
|
||||||
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
contentStore,
|
||||||
new String[] { MediaStore.Images.Media._ID },
|
new String[] { MediaStore.Images.Media._ID },
|
||||||
null,
|
null,
|
||||||
null,
|
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
|
* 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
|
* to the content store. If we are using a FILE_URI and the number of images in the DB
|
||||||
@@ -494,19 +698,35 @@ public class CameraLauncher extends Plugin {
|
|||||||
*/
|
*/
|
||||||
private void checkForDuplicateImage(int type) {
|
private void checkForDuplicateImage(int type) {
|
||||||
int diff = 1;
|
int diff = 1;
|
||||||
Cursor cursor = queryImgDB();
|
Uri contentStore = whichContentStore();
|
||||||
|
Cursor cursor = queryImgDB(contentStore);
|
||||||
int currentNumOfImages = cursor.getCount();
|
int currentNumOfImages = cursor.getCount();
|
||||||
|
|
||||||
if (type == FILE_URI) {
|
if (type == FILE_URI && this.saveToPhotoAlbum) {
|
||||||
diff = 2;
|
diff = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
|
// delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
|
||||||
if ((currentNumOfImages - numPics) == diff) {
|
if ((currentNumOfImages - numPics) == diff) {
|
||||||
cursor.moveToLast();
|
cursor.moveToLast();
|
||||||
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
|
int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
|
||||||
Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + id);
|
if (diff == 2) {
|
||||||
this.ctx.getActivity().getContentResolver().delete(uri, null, null);
|
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 +742,7 @@ public class CameraLauncher extends Plugin {
|
|||||||
byte[] code = jpeg_data.toByteArray();
|
byte[] code = jpeg_data.toByteArray();
|
||||||
byte[] output = Base64.encodeBase64(code);
|
byte[] output = Base64.encodeBase64(code);
|
||||||
String js_out = new String(output);
|
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;
|
js_out = null;
|
||||||
output = null;
|
output = null;
|
||||||
code = null;
|
code = null;
|
||||||
@@ -539,6 +759,28 @@ public class CameraLauncher extends Plugin {
|
|||||||
* @param err
|
* @param err
|
||||||
*/
|
*/
|
||||||
public void failPicture(String 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;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
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.LOG;
|
||||||
import org.apache.cordova.api.Plugin;
|
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -32,13 +34,15 @@ import org.json.JSONObject;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.database.Cursor;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.media.MediaPlayer;
|
import android.media.MediaPlayer;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.util.Log;
|
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_3GPP = "video/3gpp";
|
||||||
private static final String VIDEO_MP4 = "video/mp4";
|
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_APPLICATION_BUSY = 1;
|
||||||
// private static final int CAPTURE_INVALID_ARGUMENT = 2;
|
// private static final int CAPTURE_INVALID_ARGUMENT = 2;
|
||||||
private static final int CAPTURE_NO_MEDIA_FILES = 3;
|
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 long limit; // the number of pics/vids/clips to take
|
||||||
private double duration; // optional duration parameter for video recording
|
private double duration; // optional duration parameter for video recording
|
||||||
private JSONArray results; // The array of results to be returned to the user
|
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;
|
//private CordovaInterface cordova;
|
||||||
|
|
||||||
@@ -73,8 +76,8 @@ public class Capture extends Plugin {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
this.callbackId = callbackId;
|
this.callbackContext = callbackContext;
|
||||||
this.limit = 1;
|
this.limit = 1;
|
||||||
this.duration = 0.0f;
|
this.duration = 0.0f;
|
||||||
this.results = new JSONArray();
|
this.results = new JSONArray();
|
||||||
@@ -86,12 +89,9 @@ public class Capture extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.equals("getFormatData")) {
|
if (action.equals("getFormatData")) {
|
||||||
try {
|
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
callbackContext.success(obj);
|
||||||
return new PluginResult(PluginResult.Status.OK, obj);
|
return true;
|
||||||
} catch (JSONException e) {
|
|
||||||
return new PluginResult(PluginResult.Status.ERROR);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (action.equals("captureAudio")) {
|
else if (action.equals("captureAudio")) {
|
||||||
this.captureAudio();
|
this.captureAudio();
|
||||||
@@ -102,10 +102,11 @@ public class Capture extends Plugin {
|
|||||||
else if (action.equals("captureVideo")) {
|
else if (action.equals("captureVideo")) {
|
||||||
this.captureVideo(duration);
|
this.captureVideo(duration);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
return true;
|
||||||
r.setKeepCallback(true);
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,34 +116,30 @@ public class Capture extends Plugin {
|
|||||||
* @param mimeType of the file
|
* @param mimeType of the file
|
||||||
* @return a MediaFileData object
|
* @return a MediaFileData object
|
||||||
*/
|
*/
|
||||||
private JSONObject getFormatData(String filePath, String mimeType) {
|
private JSONObject getFormatData(String filePath, String mimeType) throws JSONException {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
try {
|
// setup defaults
|
||||||
// setup defaults
|
obj.put("height", 0);
|
||||||
obj.put("height", 0);
|
obj.put("width", 0);
|
||||||
obj.put("width", 0);
|
obj.put("bitrate", 0);
|
||||||
obj.put("bitrate", 0);
|
obj.put("duration", 0);
|
||||||
obj.put("duration", 0);
|
obj.put("codecs", "");
|
||||||
obj.put("codecs", "");
|
|
||||||
|
|
||||||
// If the mimeType isn't set the rest will fail
|
// If the mimeType isn't set the rest will fail
|
||||||
// so let's see if we can determine it.
|
// so let's see if we can determine it.
|
||||||
if (mimeType == null || mimeType.equals("")) {
|
if (mimeType == null || mimeType.equals("") || "null".equals(mimeType)) {
|
||||||
mimeType = FileUtils.getMimeType(filePath);
|
mimeType = FileUtils.getMimeType(filePath);
|
||||||
}
|
}
|
||||||
Log.d(LOG_TAG, "Mime type = " + mimeType);
|
Log.d(LOG_TAG, "Mime type = " + mimeType);
|
||||||
|
|
||||||
if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
|
if (mimeType.equals(IMAGE_JPEG) || filePath.endsWith(".jpg")) {
|
||||||
obj = getImageData(filePath, obj);
|
obj = getImageData(filePath, obj);
|
||||||
}
|
}
|
||||||
else if (mimeType.endsWith(AUDIO_3GPP)) {
|
else if (mimeType.endsWith(AUDIO_3GPP)) {
|
||||||
obj = getAudioVideoData(filePath, obj, false);
|
obj = getAudioVideoData(filePath, obj, false);
|
||||||
}
|
}
|
||||||
else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
|
else if (mimeType.equals(VIDEO_3GPP) || mimeType.equals(VIDEO_MP4)) {
|
||||||
obj = getAudioVideoData(filePath, obj, true);
|
obj = getAudioVideoData(filePath, obj, true);
|
||||||
}
|
|
||||||
} catch (JSONException e) {
|
|
||||||
Log.d(LOG_TAG, "Error: setting media file data object");
|
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
@@ -195,21 +192,23 @@ public class Capture extends Plugin {
|
|||||||
private void captureAudio() {
|
private void captureAudio() {
|
||||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
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()
|
* Sets up an intent to capture images. Result handled by onActivityResult()
|
||||||
*/
|
*/
|
||||||
private void captureImage() {
|
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);
|
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||||
|
|
||||||
// Specify file so that large image is captured and returned
|
// 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));
|
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
|
// Introduced in API 8
|
||||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
//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) {
|
if (results.length() >= limit) {
|
||||||
// Send Uri back to JavaScript for listening to audio
|
// 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 {
|
} else {
|
||||||
// still need to capture more audio clips
|
// still need to capture more audio clips
|
||||||
captureAudio();
|
captureAudio();
|
||||||
@@ -256,51 +255,42 @@ public class Capture extends Plugin {
|
|||||||
// It crashes in the emulator and on my phone with a null pointer exception
|
// 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
|
// To work around it I had to grab the code from CameraLauncher.java
|
||||||
try {
|
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
|
// Create entry in media store for image
|
||||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, IMAGE_JPEG);
|
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, IMAGE_JPEG);
|
||||||
Uri uri = null;
|
Uri uri = null;
|
||||||
try {
|
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) {
|
} catch (UnsupportedOperationException e) {
|
||||||
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
LOG.d(LOG_TAG, "Can't write to external media storage.");
|
||||||
try {
|
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) {
|
} catch (UnsupportedOperationException ex) {
|
||||||
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
LOG.d(LOG_TAG, "Can't write to internal media storage.");
|
||||||
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found."));
|
this.fail(createErrorObject(CAPTURE_INTERNAL_ERR, "Error capturing image - no media storage found."));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
FileInputStream fis = new FileInputStream(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
|
||||||
// Add compressed version of captured image to returned media store Uri
|
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
|
||||||
OutputStream os = this.ctx.getActivity().getContentResolver().openOutputStream(uri);
|
byte[] buffer = new byte[4096];
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
|
int len;
|
||||||
|
while ((len = fis.read(buffer)) != -1) {
|
||||||
|
os.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
os.flush();
|
||||||
os.close();
|
os.close();
|
||||||
|
fis.close();
|
||||||
bitmap.recycle();
|
|
||||||
bitmap = null;
|
|
||||||
System.gc();
|
|
||||||
|
|
||||||
// Restore exif data to file
|
|
||||||
exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.ctx));
|
|
||||||
exif.writeExifData();
|
|
||||||
|
|
||||||
// Add image to results
|
// Add image to results
|
||||||
results.put(createMediaFile(uri));
|
results.put(createMediaFile(uri));
|
||||||
|
|
||||||
|
checkForDuplicateImage();
|
||||||
|
|
||||||
if (results.length() >= limit) {
|
if (results.length() >= limit) {
|
||||||
// Send Uri back to JavaScript for viewing image
|
// 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 {
|
} else {
|
||||||
// still need to capture more images
|
// still need to capture more images
|
||||||
captureImage();
|
captureImage();
|
||||||
@@ -317,7 +307,7 @@ public class Capture extends Plugin {
|
|||||||
|
|
||||||
if (results.length() >= limit) {
|
if (results.length() >= limit) {
|
||||||
// Send Uri back to JavaScript for viewing video
|
// 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 {
|
} else {
|
||||||
// still need to capture more video clips
|
// still need to capture more video clips
|
||||||
captureVideo(duration);
|
captureVideo(duration);
|
||||||
@@ -328,7 +318,7 @@ public class Capture extends Plugin {
|
|||||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
// If we have partial results send them back to the user
|
// If we have partial results send them back to the user
|
||||||
if (results.length() > 0) {
|
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
|
// user canceled the action
|
||||||
else {
|
else {
|
||||||
@@ -339,7 +329,7 @@ public class Capture extends Plugin {
|
|||||||
else {
|
else {
|
||||||
// If we have partial results send them back to the user
|
// If we have partial results send them back to the user
|
||||||
if (results.length() > 0) {
|
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
|
// something bad happened
|
||||||
else {
|
else {
|
||||||
@@ -356,7 +346,7 @@ public class Capture extends Plugin {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
private JSONObject createMediaFile(Uri data) {
|
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();
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -403,6 +393,51 @@ public class Capture extends Plugin {
|
|||||||
* @param err
|
* @param err
|
||||||
*/
|
*/
|
||||||
public void fail(JSONObject 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 java.util.List;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
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.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -33,10 +34,13 @@ import android.hardware.SensorEventListener;
|
|||||||
import android.hardware.SensorManager;
|
import android.hardware.SensorManager;
|
||||||
import android.content.Context;
|
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.
|
* 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 STOPPED = 0;
|
||||||
public static int STARTING = 1;
|
public static int STARTING = 1;
|
||||||
@@ -54,6 +58,8 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
|||||||
private SensorManager sensorManager;// Sensor manager
|
private SensorManager sensorManager;// Sensor manager
|
||||||
Sensor mSensor; // Compass sensor returned by sensor manager
|
Sensor mSensor; // Compass sensor returned by sensor manager
|
||||||
|
|
||||||
|
private CallbackContext callbackContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* 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
|
* Sets the context of the Command. This can then be used to do things like
|
||||||
* get file paths associated with the Activity.
|
* 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) {
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
super.setContext(ctx);
|
super.initialize(cordova, webView);
|
||||||
this.sensorManager = (SensorManager) ctx.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
this.sensorManager = (SensorManager) cordova.getActivity().getSystemService(Context.SENSOR_SERVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArry of arguments for the plugin.
|
||||||
* @param callbackId The callback id used when calling back into JavaScript.
|
* @param callbackS=Context The callback id used when calling back into JavaScript.
|
||||||
* @return A PluginResult object with a status and message.
|
* @return True if the action was valid.
|
||||||
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
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;
|
if (action.equals("start")) {
|
||||||
String result = "";
|
this.start();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
else if (action.equals("stop")) {
|
||||||
|
this.stop();
|
||||||
/**
|
}
|
||||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
else if (action.equals("getStatus")) {
|
||||||
*
|
int i = this.getStatus();
|
||||||
* @param action The action to execute
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, i));
|
||||||
* @return T=returns value
|
|
||||||
*/
|
|
||||||
public boolean isSynch(String action) {
|
|
||||||
if (action.equals("getStatus")) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else if (action.equals("getHeading")) {
|
else if (action.equals("getHeading")) {
|
||||||
// Can only return value if RUNNING
|
// If not running, then this is an async call, so don't worry about waiting
|
||||||
if (this.status == CompassListener.RUNNING) {
|
if (this.status != CompassListener.RUNNING) {
|
||||||
return true;
|
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")) {
|
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();
|
this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when app has navigated and JS listeners have been destroyed.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
// LOCAL METHODS
|
// LOCAL METHODS
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -216,6 +196,18 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
|||||||
// TODO Auto-generated method stub
|
// 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.
|
* Sensor listener event.
|
||||||
*
|
*
|
||||||
@@ -287,19 +279,15 @@ public class CompassListener extends Plugin implements SensorEventListener {
|
|||||||
*
|
*
|
||||||
* @return a compass heading
|
* @return a compass heading
|
||||||
*/
|
*/
|
||||||
private JSONObject getCompassHeading() {
|
private JSONObject getCompassHeading() throws JSONException {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
|
|
||||||
try {
|
obj.put("magneticHeading", this.getHeading());
|
||||||
obj.put("magneticHeading", this.getHeading());
|
obj.put("trueHeading", this.getHeading());
|
||||||
obj.put("trueHeading", this.getHeading());
|
// Since the magnetic and true heading are always the same our and accuracy
|
||||||
// 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
|
||||||
// is defined as the difference between true and magnetic always return zero
|
obj.put("headingAccuracy", 0);
|
||||||
obj.put("headingAccuracy", 0);
|
obj.put("timestamp", this.timeStamp);
|
||||||
obj.put("timestamp", this.timeStamp);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
// Should never happen
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|||||||
228
framework/src/org/apache/cordova/Config.java
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
/*
|
||||||
|
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.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.LOG;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import android.content.res.XmlResourceParser;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class Config {
|
||||||
|
|
||||||
|
public static final String TAG = "Config";
|
||||||
|
|
||||||
|
private ArrayList<Pattern> whiteList = new ArrayList<Pattern>();
|
||||||
|
private HashMap<String, Boolean> whiteListCache = new HashMap<String, Boolean>();
|
||||||
|
private String startUrl;
|
||||||
|
|
||||||
|
private static Config self = null;
|
||||||
|
|
||||||
|
public static void init(Activity action) {
|
||||||
|
if (self == null) {
|
||||||
|
self = new Config(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intended to be used for testing only; creates an empty configuration.
|
||||||
|
public static void init() {
|
||||||
|
if (self == null) {
|
||||||
|
self = new Config();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intended to be used for testing only; creates an empty configuration.
|
||||||
|
private Config() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private Config(Activity action) {
|
||||||
|
if (action == null) {
|
||||||
|
LOG.i("CordovaLog", "There is no activity. Is this on the lock screen?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int id = action.getResources().getIdentifier("config", "xml", action.getPackageName());
|
||||||
|
if (id == 0) {
|
||||||
|
id = action.getResources().getIdentifier("cordova", "xml", action.getPackageName());
|
||||||
|
LOG.i("CordovaLog", "config.xml missing, reverting to cordova.xml");
|
||||||
|
}
|
||||||
|
if (id == 0) {
|
||||||
|
LOG.i("CordovaLog", "cordova.xml missing. Ignoring...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlResourceParser xml = action.getResources().getXml(id);
|
||||||
|
int eventType = -1;
|
||||||
|
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||||
|
if (eventType == XmlResourceParser.START_TAG) {
|
||||||
|
String strNode = xml.getName();
|
||||||
|
|
||||||
|
if (strNode.equals("access")) {
|
||||||
|
String origin = xml.getAttributeValue(null, "origin");
|
||||||
|
String subdomains = xml.getAttributeValue(null, "subdomains");
|
||||||
|
if (origin != null) {
|
||||||
|
this._addWhiteListEntry(origin, (subdomains != null) && (subdomains.compareToIgnoreCase("true") == 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strNode.equals("log")) {
|
||||||
|
String level = xml.getAttributeValue(null, "level");
|
||||||
|
LOG.i("CordovaLog", "Found log level %s", level);
|
||||||
|
if (level != null) {
|
||||||
|
LOG.setLogLevel(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (strNode.equals("preference")) {
|
||||||
|
String name = xml.getAttributeValue(null, "name");
|
||||||
|
String value = xml.getAttributeValue(null, "value");
|
||||||
|
|
||||||
|
LOG.i("CordovaLog", "Found preference for %s=%s", name, value);
|
||||||
|
Log.d("CordovaLog", "Found preference for " + name + "=" + value);
|
||||||
|
|
||||||
|
action.getIntent().putExtra(name, value);
|
||||||
|
}
|
||||||
|
else if (strNode.equals("content")) {
|
||||||
|
String src = xml.getAttributeValue(null, "src");
|
||||||
|
|
||||||
|
LOG.i("CordovaLog", "Found start page location: %s", src);
|
||||||
|
|
||||||
|
if (src != null) {
|
||||||
|
Pattern schemeRegex = Pattern.compile("^[a-z]+://");
|
||||||
|
Matcher matcher = schemeRegex.matcher(src);
|
||||||
|
if (matcher.find()) {
|
||||||
|
startUrl = src;
|
||||||
|
} else {
|
||||||
|
if (src.charAt(0) == '/') {
|
||||||
|
src = src.substring(1);
|
||||||
|
}
|
||||||
|
startUrl = "file:///android_asset/www/" + src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
eventType = xml.next();
|
||||||
|
} catch (XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add entry to approved list of URLs (whitelist)
|
||||||
|
*
|
||||||
|
* @param origin URL regular expression to allow
|
||||||
|
* @param subdomains T=include all subdomains under origin
|
||||||
|
*/
|
||||||
|
public static void addWhiteListEntry(String origin, boolean subdomains) {
|
||||||
|
if (self == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self._addWhiteListEntry(origin, subdomains);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void _addWhiteListEntry(String origin, boolean subdomains) {
|
||||||
|
try {
|
||||||
|
// Unlimited access to network resources
|
||||||
|
if (origin.compareTo("*") == 0) {
|
||||||
|
LOG.d(TAG, "Unlimited access to network resources");
|
||||||
|
this.whiteList.add(Pattern.compile(".*"));
|
||||||
|
} else { // specific access
|
||||||
|
// check if subdomains should be included
|
||||||
|
// TODO: we should not add more domains if * has already been added
|
||||||
|
if (subdomains) {
|
||||||
|
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||||
|
if (origin.startsWith("http")) {
|
||||||
|
this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?")));
|
||||||
|
} else {
|
||||||
|
this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin));
|
||||||
|
}
|
||||||
|
LOG.d(TAG, "Origin to allow with subdomains: %s", origin);
|
||||||
|
} else {
|
||||||
|
// XXX making it stupid friendly for people who forget to include protocol/SSL
|
||||||
|
if (origin.startsWith("http")) {
|
||||||
|
this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://")));
|
||||||
|
} else {
|
||||||
|
this.whiteList.add(Pattern.compile("^https?://" + origin));
|
||||||
|
}
|
||||||
|
LOG.d(TAG, "Origin to allow: %s", origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOG.d(TAG, "Failed to add origin %s", origin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if URL is in approved list of URLs to load.
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isUrlWhiteListed(String url) {
|
||||||
|
if (self == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if we have matched url previously
|
||||||
|
if (self.whiteListCache.get(url) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for match in white list
|
||||||
|
Iterator<Pattern> pit = self.whiteList.iterator();
|
||||||
|
while (pit.hasNext()) {
|
||||||
|
Pattern p = pit.next();
|
||||||
|
Matcher m = p.matcher(url);
|
||||||
|
|
||||||
|
// If match found, then cache it to speed up subsequent comparisons
|
||||||
|
if (m.find()) {
|
||||||
|
self.whiteListCache.put(url, true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getStartUrl() {
|
||||||
|
if (self == null || self.startUrl == null) {
|
||||||
|
return "file:///android_asset/www/index.html";
|
||||||
|
}
|
||||||
|
return self.startUrl;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,14 +18,15 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
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.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class ContactManager extends Plugin {
|
public class ContactManager extends CordovaPlugin {
|
||||||
|
|
||||||
private ContactAccessor contactAccessor;
|
private ContactAccessor contactAccessor;
|
||||||
private static final String LOG_TAG = "Contact Query";
|
private static final String LOG_TAG = "Contact Query";
|
||||||
@@ -47,21 +48,19 @@ public class ContactManager extends Plugin {
|
|||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArray 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.
|
* @return True if the action was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
public boolean execute(String action, JSONArray args, final CallbackContext callbackContext) throws JSONException {
|
||||||
PluginResult.Status status = PluginResult.Status.OK;
|
|
||||||
String result = "";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to see if we are on an Android 1.X device. If we are return an error as we
|
* 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.
|
* do not support this as of Cordova 1.0.
|
||||||
*/
|
*/
|
||||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
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.
|
* older phones.
|
||||||
*/
|
*/
|
||||||
if (this.contactAccessor == null) {
|
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")) {
|
||||||
if (action.equals("search")) {
|
final JSONArray filter = args.getJSONArray(0);
|
||||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
|
final JSONObject options = args.getJSONObject(1);
|
||||||
return new PluginResult(status, res);
|
this.cordova.getThreadPool().execute(new Runnable() {
|
||||||
}
|
public void run() {
|
||||||
else if (action.equals("save")) {
|
JSONArray res = contactAccessor.search(filter, options);
|
||||||
String id = contactAccessor.save(args.getJSONObject(0));
|
callbackContext.success(res);
|
||||||
if (id != null) {
|
}
|
||||||
JSONObject res = contactAccessor.getContactById(id);
|
});
|
||||||
|
}
|
||||||
|
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) {
|
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,38 +23,55 @@ import org.apache.cordova.api.LOG;
|
|||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
//import android.app.Activity;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
//import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Gravity;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
//import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
import android.webkit.ConsoleMessage;
|
import android.webkit.ConsoleMessage;
|
||||||
import android.webkit.JsPromptResult;
|
import android.webkit.JsPromptResult;
|
||||||
import android.webkit.JsResult;
|
import android.webkit.JsResult;
|
||||||
|
import android.webkit.ValueCallback;
|
||||||
import android.webkit.WebChromeClient;
|
import android.webkit.WebChromeClient;
|
||||||
import android.webkit.WebStorage;
|
import android.webkit.WebStorage;
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
import android.webkit.GeolocationPermissions.Callback;
|
import android.webkit.GeolocationPermissions.Callback;
|
||||||
import android.widget.EditText;
|
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.
|
* This class is the WebChromeClient that implements callbacks for our web view.
|
||||||
*/
|
*/
|
||||||
public class CordovaChromeClient extends WebChromeClient {
|
public class CordovaChromeClient extends WebChromeClient {
|
||||||
|
|
||||||
|
public static final int FILECHOOSER_RESULTCODE = 5173;
|
||||||
|
private static final String LOG_TAG = "CordovaChromeClient";
|
||||||
private String TAG = "CordovaLog";
|
private String TAG = "CordovaLog";
|
||||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||||
private CordovaInterface ctx;
|
private CordovaInterface cordova;
|
||||||
private CordovaWebView appView;
|
private CordovaWebView appView;
|
||||||
|
|
||||||
|
// the video progress view
|
||||||
|
private View mVideoProgressView;
|
||||||
|
|
||||||
|
// File Chooser
|
||||||
|
public ValueCallback<Uri> mUploadMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ctx
|
* @param cordova
|
||||||
*/
|
*/
|
||||||
public CordovaChromeClient(CordovaInterface ctx) {
|
public CordovaChromeClient(CordovaInterface cordova) {
|
||||||
this.ctx = ctx;
|
this.cordova = cordova;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,7 +81,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
* @param app
|
* @param app
|
||||||
*/
|
*/
|
||||||
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
public CordovaChromeClient(CordovaInterface ctx, CordovaWebView app) {
|
||||||
this.ctx = ctx;
|
this.cordova = ctx;
|
||||||
this.appView = app;
|
this.appView = app;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +104,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
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.setMessage(message);
|
||||||
dlg.setTitle("Alert");
|
dlg.setTitle("Alert");
|
||||||
//Don't let alerts break the back button
|
//Don't let alerts break the back button
|
||||||
@@ -101,7 +118,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
dlg.setOnCancelListener(
|
dlg.setOnCancelListener(
|
||||||
new DialogInterface.OnCancelListener() {
|
new DialogInterface.OnCancelListener() {
|
||||||
public void onCancel(DialogInterface dialog) {
|
public void onCancel(DialogInterface dialog) {
|
||||||
result.confirm();
|
result.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
dlg.setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||||
@@ -131,7 +148,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
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.setMessage(message);
|
||||||
dlg.setTitle("Confirm");
|
dlg.setTitle("Confirm");
|
||||||
dlg.setCancelable(true);
|
dlg.setCancelable(true);
|
||||||
@@ -190,7 +207,7 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
// Security check to make sure any requests are coming from the page initially
|
// Security check to make sure any requests are coming from the page initially
|
||||||
// loaded in webview and not another loaded in an iframe.
|
// loaded in webview and not another loaded in an iframe.
|
||||||
boolean reqOk = false;
|
boolean reqOk = false;
|
||||||
if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || this.appView.isUrlWhiteListed(url)) {
|
if (url.startsWith("file://") || url.indexOf(this.appView.baseUrl) == 0 || Config.isUrlWhiteListed(url)) {
|
||||||
reqOk = true;
|
reqOk = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,18 +220,23 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
String service = array.getString(0);
|
String service = array.getString(0);
|
||||||
String action = array.getString(1);
|
String action = array.getString(1);
|
||||||
String callbackId = array.getString(2);
|
String callbackId = array.getString(2);
|
||||||
boolean async = array.getBoolean(3);
|
String r = this.appView.exposedJsApi.exec(service, action, callbackId, message);
|
||||||
String r = this.appView.pluginManager.exec(service, action, callbackId, message, async);
|
result.confirm(r == null ? "" : r);
|
||||||
result.confirm(r);
|
|
||||||
} catch (JSONException e) {
|
} catch (JSONException e) {
|
||||||
e.printStackTrace();
|
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
|
// Polling for JavaScript messages
|
||||||
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
else if (reqOk && defaultValue != null && defaultValue.equals("gap_poll:")) {
|
||||||
String r = this.appView.callbackServer.getJavascript();
|
String r = this.appView.exposedJsApi.retrieveJsMessages();
|
||||||
result.confirm(r);
|
result.confirm(r == null ? "" : r);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do NO-OP so older code doesn't display dialog
|
// Do NO-OP so older code doesn't display dialog
|
||||||
@@ -222,30 +244,12 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
result.confirm("OK");
|
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
|
// Show dialog
|
||||||
else {
|
else {
|
||||||
final JsPromptResult res = result;
|
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);
|
dlg.setMessage(message);
|
||||||
final EditText input = new EditText(this.ctx.getActivity());
|
final EditText input = new EditText(this.cordova.getActivity());
|
||||||
if (defaultValue != null) {
|
if (defaultValue != null) {
|
||||||
input.setText(defaultValue);
|
input.setText(defaultValue);
|
||||||
}
|
}
|
||||||
@@ -302,14 +306,20 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
||||||
|
// Expect this to not compile in a future Android release!
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
||||||
{
|
{
|
||||||
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
//This is only for Android 2.1
|
||||||
super.onConsoleMessage(message, lineNumber, sourceID);
|
if(android.os.Build.VERSION.SDK_INT == android.os.Build.VERSION_CODES.ECLAIR_MR1)
|
||||||
|
{
|
||||||
|
LOG.d(TAG, "%s: Line %d : %s", sourceID, lineNumber, message);
|
||||||
|
super.onConsoleMessage(message, lineNumber, sourceID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(8)
|
||||||
@Override
|
@Override
|
||||||
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
public boolean onConsoleMessage(ConsoleMessage consoleMessage)
|
||||||
{
|
{
|
||||||
@@ -329,4 +339,61 @@ public class CordovaChromeClient extends WebChromeClient {
|
|||||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||||
callback.invoke(origin, true, false);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
|
||||||
|
this.openFileChooser(uploadMsg, "*/*");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void openFileChooser( ValueCallback<Uri> uploadMsg, String acceptType ) {
|
||||||
|
mUploadMessage = uploadMsg;
|
||||||
|
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
|
i.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
i.setType("*/*");
|
||||||
|
this.cordova.getActivity().startActivityForResult(Intent.createChooser(i, "File Browser"),
|
||||||
|
FILECHOOSER_RESULTCODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueCallback<Uri> getValueCallback() {
|
||||||
|
return this.mUploadMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
|
|
||||||
import android.location.Location;
|
import android.location.Location;
|
||||||
import android.location.LocationListener;
|
import android.location.LocationListener;
|
||||||
@@ -39,8 +40,8 @@ public class CordovaLocationListener implements LocationListener {
|
|||||||
private GeoBroker owner;
|
private GeoBroker owner;
|
||||||
protected boolean running = false;
|
protected boolean running = false;
|
||||||
|
|
||||||
public HashMap<String, String> watches = new HashMap<String, String>();
|
public HashMap<String, CallbackContext> watches = new HashMap<String, CallbackContext>();
|
||||||
private List<String> callbacks = new ArrayList<String>();
|
private List<CallbackContext> callbacks = new ArrayList<CallbackContext>();
|
||||||
|
|
||||||
private String TAG = "[Cordova Location Listener]";
|
private String TAG = "[Cordova Location Listener]";
|
||||||
|
|
||||||
@@ -51,30 +52,38 @@ public class CordovaLocationListener implements LocationListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void fail(int code, String message) {
|
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();
|
this.callbacks.clear();
|
||||||
|
|
||||||
Iterator it = this.watches.entrySet().iterator();
|
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Map.Entry pairs = (Map.Entry)it.next();
|
this.owner.fail(code, message, it.next());
|
||||||
this.owner.fail(code, message, (String)pairs.getValue());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void win(Location loc) {
|
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();
|
this.callbacks.clear();
|
||||||
|
|
||||||
Iterator it = this.watches.entrySet().iterator();
|
Iterator<CallbackContext> it = this.watches.values().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
Map.Entry pairs = (Map.Entry)it.next();
|
this.owner.win(loc, it.next());
|
||||||
this.owner.win(loc, (String)pairs.getValue());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,14 +149,14 @@ public class CordovaLocationListener implements LocationListener {
|
|||||||
return this.watches.size() + this.callbacks.size();
|
return this.watches.size() + this.callbacks.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addWatch(String timerId, String callbackId) {
|
public void addWatch(String timerId, CallbackContext callbackContext) {
|
||||||
this.watches.put(timerId, callbackId);
|
this.watches.put(timerId, callbackContext);
|
||||||
if (this.size() == 1) {
|
if (this.size() == 1) {
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void addCallback(String callbackId) {
|
public void addCallback(CallbackContext callbackContext) {
|
||||||
this.callbacks.add(callbackId);
|
this.callbacks.add(callbackContext);
|
||||||
if (this.size() == 1) {
|
if (this.size() == 1) {
|
||||||
this.start();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,10 +21,12 @@ package org.apache.cordova;
|
|||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
import org.apache.cordova.api.CordovaInterface;
|
||||||
|
|
||||||
import org.apache.cordova.api.LOG;
|
import org.apache.cordova.api.LOG;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@@ -32,6 +34,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.webkit.HttpAuthHandler;
|
import android.webkit.HttpAuthHandler;
|
||||||
import android.webkit.SslErrorHandler;
|
import android.webkit.SslErrorHandler;
|
||||||
@@ -43,8 +46,9 @@ import android.webkit.WebViewClient;
|
|||||||
*/
|
*/
|
||||||
public class CordovaWebViewClient extends WebViewClient {
|
public class CordovaWebViewClient extends WebViewClient {
|
||||||
|
|
||||||
private static final String TAG = "Cordova";
|
private static final String TAG = "Cordova";
|
||||||
CordovaInterface ctx;
|
private static final String CORDOVA_EXEC_URL_PREFIX = "http://cdv_exec/";
|
||||||
|
CordovaInterface cordova;
|
||||||
CordovaWebView appView;
|
CordovaWebView appView;
|
||||||
private boolean doClearHistory = false;
|
private boolean doClearHistory = false;
|
||||||
|
|
||||||
@@ -54,20 +58,20 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ctx
|
* @param cordova
|
||||||
*/
|
*/
|
||||||
public CordovaWebViewClient(CordovaInterface ctx) {
|
public CordovaWebViewClient(CordovaInterface cordova) {
|
||||||
this.ctx = ctx;
|
this.cordova = cordova;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
*
|
*
|
||||||
* @param ctx
|
* @param cordova
|
||||||
* @param view
|
* @param view
|
||||||
*/
|
*/
|
||||||
public CordovaWebViewClient(CordovaInterface ctx, CordovaWebView view) {
|
public CordovaWebViewClient(CordovaInterface cordova, CordovaWebView view) {
|
||||||
this.ctx = ctx;
|
this.cordova = cordova;
|
||||||
this.appView = view;
|
this.appView = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,6 +84,25 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
this.appView = view;
|
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
|
* Give the host application a chance to take over the control when a new url
|
||||||
* is about to be loaded in the current WebView.
|
* is about to be loaded in the current WebView.
|
||||||
@@ -88,11 +111,15 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
* @param url The url to be loaded.
|
* @param url The url to be loaded.
|
||||||
* @return true to override, false for default behavior
|
* @return true to override, false for default behavior
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
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
|
// Give plugins the chance to handle the url
|
||||||
if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
else if ((this.appView.pluginManager != null) && this.appView.pluginManager.onOverrideUrlLoading(url)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// If dialing phone (tel:5551212)
|
// If dialing phone (tel:5551212)
|
||||||
@@ -100,7 +127,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||||
intent.setData(Uri.parse(url));
|
intent.setData(Uri.parse(url));
|
||||||
this.ctx.getActivity().startActivity(intent);
|
this.cordova.getActivity().startActivity(intent);
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
LOG.e(TAG, "Error dialing " + url + ": " + e.toString());
|
LOG.e(TAG, "Error dialing " + url + ": " + e.toString());
|
||||||
}
|
}
|
||||||
@@ -111,7 +138,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(url));
|
intent.setData(Uri.parse(url));
|
||||||
this.ctx.getActivity().startActivity(intent);
|
this.cordova.getActivity().startActivity(intent);
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
LOG.e(TAG, "Error showing map " + url + ": " + e.toString());
|
LOG.e(TAG, "Error showing map " + url + ": " + e.toString());
|
||||||
}
|
}
|
||||||
@@ -122,7 +149,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(url));
|
intent.setData(Uri.parse(url));
|
||||||
this.ctx.getActivity().startActivity(intent);
|
this.cordova.getActivity().startActivity(intent);
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
LOG.e(TAG, "Error sending email " + url + ": " + e.toString());
|
LOG.e(TAG, "Error sending email " + url + ": " + e.toString());
|
||||||
}
|
}
|
||||||
@@ -154,7 +181,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
intent.setData(Uri.parse("sms:" + address));
|
intent.setData(Uri.parse("sms:" + address));
|
||||||
intent.putExtra("address", address);
|
intent.putExtra("address", address);
|
||||||
intent.setType("vnd.android-dir/mms-sms");
|
intent.setType("vnd.android-dir/mms-sms");
|
||||||
this.ctx.getActivity().startActivity(intent);
|
this.cordova.getActivity().startActivity(intent);
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
LOG.e(TAG, "Error sending sms " + url + ":" + e.toString());
|
||||||
}
|
}
|
||||||
@@ -165,9 +192,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.
|
// 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.
|
// 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 || Config.isUrlWhiteListed(url)) {
|
||||||
//This will fix iFrames
|
//This will fix iFrames
|
||||||
if (appView.useBrowserHistory)
|
if (appView.useBrowserHistory || url.startsWith("data:"))
|
||||||
return false;
|
return false;
|
||||||
else
|
else
|
||||||
this.appView.loadUrl(url);
|
this.appView.loadUrl(url);
|
||||||
@@ -178,7 +205,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
try {
|
try {
|
||||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||||
intent.setData(Uri.parse(url));
|
intent.setData(Uri.parse(url));
|
||||||
this.ctx.getActivity().startActivity(intent);
|
this.cordova.getActivity().startActivity(intent);
|
||||||
} catch (android.content.ActivityNotFoundException e) {
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
LOG.e(TAG, "Error loading url " + url, e);
|
LOG.e(TAG, "Error loading url " + url, e);
|
||||||
}
|
}
|
||||||
@@ -224,23 +251,23 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
this.doClearHistory = true;
|
this.doClearHistory = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create callback server and plugin manager
|
// Flush stale messages.
|
||||||
if (this.appView.callbackServer == null) {
|
this.appView.jsMessageQueue.reset();
|
||||||
this.appView.callbackServer = new CallbackServer();
|
|
||||||
this.appView.callbackServer.init(url);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.appView.callbackServer.reinit(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Broadcast message that page has loaded
|
// Broadcast message that page has loaded
|
||||||
this.appView.postMessage("onPageStarted", url);
|
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.
|
* 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.
|
* 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 view The webview initiating the callback.
|
||||||
* @param url The url of the page.
|
* @param url The url of the page.
|
||||||
*/
|
*/
|
||||||
@@ -267,6 +294,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
||||||
// from the JS side when the JS gets to that code.
|
// from the JS side when the JS gets to that code.
|
||||||
if (!url.equals("about:blank")) {
|
if (!url.equals("about:blank")) {
|
||||||
|
LOG.d(TAG, "Trying to fire onNativeReady");
|
||||||
this.appView.loadUrl("javascript:try{ cordova.require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
this.appView.loadUrl("javascript:try{ cordova.require('cordova/channel').onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||||
this.appView.postMessage("onNativeReady", null);
|
this.appView.postMessage("onNativeReady", null);
|
||||||
}
|
}
|
||||||
@@ -280,7 +308,7 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
Thread.sleep(2000);
|
Thread.sleep(2000);
|
||||||
ctx.getActivity().runOnUiThread(new Runnable() {
|
cordova.getActivity().runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
appView.postMessage("spinner", "stop");
|
appView.postMessage("spinner", "stop");
|
||||||
}
|
}
|
||||||
@@ -294,9 +322,6 @@ public class CordovaWebViewClient extends WebViewClient {
|
|||||||
|
|
||||||
// Shutdown if blank loaded
|
// Shutdown if blank loaded
|
||||||
if (url.equals("about:blank")) {
|
if (url.equals("about:blank")) {
|
||||||
if (this.appView.callbackServer != null) {
|
|
||||||
this.appView.callbackServer.destroy();
|
|
||||||
}
|
|
||||||
appView.postMessage("exit", null);
|
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 handler An SslErrorHandler object that will handle the user's response.
|
||||||
* @param error The SSL error object.
|
* @param error The SSL error object.
|
||||||
*/
|
*/
|
||||||
|
@TargetApi(8)
|
||||||
@Override
|
@Override
|
||||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||||
|
|
||||||
final String packageName = this.ctx.getActivity().getPackageName();
|
final String packageName = this.cordova.getActivity().getPackageName();
|
||||||
final PackageManager pm = this.ctx.getActivity().getPackageManager();
|
final PackageManager pm = this.cordova.getActivity().getPackageManager();
|
||||||
|
|
||||||
ApplicationInfo appInfo;
|
ApplicationInfo appInfo;
|
||||||
try {
|
try {
|
||||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||||
|
|||||||
@@ -20,9 +20,10 @@ package org.apache.cordova;
|
|||||||
|
|
||||||
import java.util.TimeZone;
|
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.LOG;
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
import org.apache.cordova.api.CordovaInterface;
|
||||||
import org.apache.cordova.api.Plugin;
|
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -35,10 +36,10 @@ import android.content.IntentFilter;
|
|||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
public class Device extends Plugin {
|
public class Device extends CordovaPlugin {
|
||||||
public static final String TAG = "Device";
|
public static final String TAG = "Device";
|
||||||
|
|
||||||
public static String cordovaVersion = "1.8.0"; // Cordova version
|
public static String cordovaVersion = "2.3.0"; // Cordova version
|
||||||
public static String platform = "Android"; // Device OS
|
public static String platform = "Android"; // Device OS
|
||||||
public static String uuid; // Device UUID
|
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
|
* Sets the context of the Command. This can then be used to do things like
|
||||||
* get file paths associated with the Activity.
|
* 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) {
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
super.setContext(ctx);
|
super.initialize(cordova, webView);
|
||||||
Device.uuid = getUuid();
|
Device.uuid = getUuid();
|
||||||
this.initTelephonyReceiver();
|
this.initTelephonyReceiver();
|
||||||
}
|
}
|
||||||
@@ -65,52 +67,33 @@ public class Device extends Plugin {
|
|||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArry of arguments for the plugin.
|
||||||
* @param callbackId The callback id used when calling back into JavaScript.
|
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||||
* @return A PluginResult object with a status and message.
|
* @return True if the action was valid, false if not.
|
||||||
*/
|
*/
|
||||||
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("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) {
|
|
||||||
if (action.equals("getDeviceInfo")) {
|
if (action.equals("getDeviceInfo")) {
|
||||||
return true;
|
JSONObject r = new JSONObject();
|
||||||
|
r.put("uuid", Device.uuid);
|
||||||
|
r.put("version", this.getOSVersion());
|
||||||
|
r.put("platform", Device.platform);
|
||||||
|
r.put("name", this.getProductName());
|
||||||
|
r.put("cordova", Device.cordovaVersion);
|
||||||
|
r.put("model", this.getModel());
|
||||||
|
callbackContext.success(r);
|
||||||
}
|
}
|
||||||
return false;
|
else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unregister receiver.
|
* Unregister receiver.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
this.ctx.getActivity().unregisterReceiver(this.telephonyReceiver);
|
this.cordova.getActivity().unregisterReceiver(this.telephonyReceiver);
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------------------------------------------------------------
|
//--------------------------------------------------------------------------
|
||||||
@@ -125,7 +108,7 @@ public class Device extends Plugin {
|
|||||||
private void initTelephonyReceiver() {
|
private void initTelephonyReceiver() {
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
intentFilter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
|
||||||
//final CordovaInterface myctx = this.ctx;
|
//final CordovaInterface mycordova = this.cordova;
|
||||||
this.telephonyReceiver = new BroadcastReceiver() {
|
this.telephonyReceiver = new BroadcastReceiver() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -153,7 +136,7 @@ public class Device extends Plugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Register the receiver
|
// Register the receiver
|
||||||
this.ctx.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
|
this.cordova.getActivity().registerReceiver(this.telephonyReceiver, intentFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,7 +154,7 @@ public class Device extends Plugin {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String getUuid() {
|
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;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,12 @@
|
|||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import java.util.HashMap;
|
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.CordovaInterface;
|
||||||
|
import org.apache.cordova.api.CordovaPlugin;
|
||||||
|
import org.apache.cordova.api.LOG;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@@ -36,8 +38,10 @@ import android.content.Intent;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.media.AudioManager;
|
import android.media.AudioManager;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -46,6 +50,8 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.webkit.ValueCallback;
|
||||||
|
import android.webkit.WebView;
|
||||||
import android.webkit.WebViewClient;
|
import android.webkit.WebViewClient;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
@@ -138,9 +144,10 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
protected CordovaWebViewClient webViewClient;
|
protected CordovaWebViewClient webViewClient;
|
||||||
|
|
||||||
protected LinearLayout root;
|
protected LinearLayout root;
|
||||||
public boolean bound = false;
|
|
||||||
protected boolean cancelLoadUrl = false;
|
protected boolean cancelLoadUrl = false;
|
||||||
protected ProgressDialog spinnerDialog = null;
|
protected ProgressDialog spinnerDialog = null;
|
||||||
|
private final ExecutorService threadPool = Executors.newCachedThreadPool();
|
||||||
|
|
||||||
|
|
||||||
// The initial URL for our app
|
// The initial URL for our app
|
||||||
// ie http://server/path/index.html#abc?query
|
// ie http://server/path/index.html#abc?query
|
||||||
@@ -151,13 +158,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
private static int ACTIVITY_EXITING = 2;
|
private static int ACTIVITY_EXITING = 2;
|
||||||
private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down
|
private int activityState = 0; // 0=starting, 1=running (after 1st resume), 2=shutting down
|
||||||
|
|
||||||
// The base of the initial URL for our app.
|
|
||||||
// Does not include file name. Ends with /
|
|
||||||
// ie http://server/path/
|
|
||||||
String baseUrl = null;
|
|
||||||
|
|
||||||
// Plugin to call when activity result is received
|
// Plugin to call when activity result is received
|
||||||
protected IPlugin activityResultCallback = null;
|
protected CordovaPlugin activityResultCallback = null;
|
||||||
protected boolean activityResultKeepRunning;
|
protected boolean activityResultKeepRunning;
|
||||||
|
|
||||||
// Default background color for activity
|
// Default background color for activity
|
||||||
@@ -171,7 +173,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
// Draw a splash screen using an image located in the drawable resource directory.
|
// Draw a splash screen using an image located in the drawable resource directory.
|
||||||
// This is not the same as calling super.loadSplashscreen(url)
|
// This is not the same as calling super.loadSplashscreen(url)
|
||||||
protected int splashscreen = 0;
|
protected int splashscreen = 0;
|
||||||
protected int splashscreenTime = 0;
|
protected int splashscreenTime = 3000;
|
||||||
|
|
||||||
// LoadUrl timeout value in msec (default of 20 sec)
|
// LoadUrl timeout value in msec (default of 20 sec)
|
||||||
protected int loadUrlTimeoutValue = 20000;
|
protected int loadUrlTimeoutValue = 20000;
|
||||||
@@ -181,9 +183,17 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
// when another application (activity) is started.
|
// when another application (activity) is started.
|
||||||
protected boolean keepRunning = true;
|
protected boolean keepRunning = true;
|
||||||
|
|
||||||
private boolean volumeupBound;
|
private int lastRequestCode;
|
||||||
|
|
||||||
private boolean volumedownBound;
|
private Object responseCode;
|
||||||
|
|
||||||
|
private Intent lastIntent;
|
||||||
|
|
||||||
|
private Object lastResponseCode;
|
||||||
|
|
||||||
|
private String initCallbackClass;
|
||||||
|
|
||||||
|
private Object LOG_TAG;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the authentication token.
|
* Sets the authentication token.
|
||||||
@@ -251,16 +261,20 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
//preferences = new PreferenceSet();
|
|
||||||
|
|
||||||
LOG.d(TAG, "DroidGap.onCreate()");
|
LOG.d(TAG, "DroidGap.onCreate()");
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
if(savedInstanceState != null)
|
||||||
|
{
|
||||||
|
initCallbackClass = savedInstanceState.getString("callbackClass");
|
||||||
|
}
|
||||||
|
|
||||||
if(!this.getBooleanProperty("showTitle", false))
|
if(!this.getBooleanProperty("showTitle", false))
|
||||||
{
|
{
|
||||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.getBooleanProperty("setFullscreen", false))
|
if(this.getBooleanProperty("setFullscreen", false))
|
||||||
{
|
{
|
||||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||||
@@ -300,7 +314,16 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
*/
|
*/
|
||||||
public void init() {
|
public void init() {
|
||||||
CordovaWebView webView = new CordovaWebView(DroidGap.this);
|
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -334,6 +357,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
|
|
||||||
// Clear cancel flag
|
// Clear cancel flag
|
||||||
this.cancelLoadUrl = false;
|
this.cancelLoadUrl = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -409,6 +433,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.splashscreenTime = time;
|
this.splashscreenTime = time;
|
||||||
|
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||||
|
this.showSplashScreen(this.splashscreenTime);
|
||||||
this.appView.loadUrl(url, time);
|
this.appView.loadUrl(url, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,6 +631,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
|
|
||||||
|
LOG.d(TAG, "Paused the application!");
|
||||||
|
|
||||||
// Don't process pause if shutting down, since onDestroy() will be called
|
// Don't process pause if shutting down, since onDestroy() will be called
|
||||||
if (this.activityState == ACTIVITY_EXITING) {
|
if (this.activityState == ACTIVITY_EXITING) {
|
||||||
return;
|
return;
|
||||||
@@ -613,21 +641,13 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
if (this.appView == null) {
|
if (this.appView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Send pause event to JavaScript
|
{
|
||||||
this.appView.loadUrl("javascript:try{cordova.fireDocumentEvent('pause');}catch(e){console.log('exception firing pause event from native');};");
|
this.appView.handlePause(this.keepRunning);
|
||||||
|
|
||||||
// Forward to plugins
|
|
||||||
if (this.appView.pluginManager != null) {
|
|
||||||
this.appView.pluginManager.onPause(this.keepRunning);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If app doesn't want to run in background
|
// hide the splash screen to avoid leaking a window
|
||||||
if (!this.keepRunning) {
|
this.removeSplashScreen();
|
||||||
|
|
||||||
// Pause JavaScript timers (including setInterval)
|
|
||||||
this.appView.pauseTimers();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -636,11 +656,9 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
**/
|
**/
|
||||||
protected void onNewIntent(Intent intent) {
|
protected void onNewIntent(Intent intent) {
|
||||||
super.onNewIntent(intent);
|
super.onNewIntent(intent);
|
||||||
|
|
||||||
//Forward to plugins
|
//Forward to plugins
|
||||||
if ((this.appView != null) && (this.appView.pluginManager != null)) {
|
if (this.appView != null)
|
||||||
this.appView.pluginManager.onNewIntent(intent);
|
this.appView.onNewIntent(intent);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -650,6 +668,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
|
||||||
|
LOG.d(TAG, "Resuming the App");
|
||||||
if (this.activityState == ACTIVITY_STARTING) {
|
if (this.activityState == ACTIVITY_STARTING) {
|
||||||
this.activityState = ACTIVITY_RUNNING;
|
this.activityState = ACTIVITY_RUNNING;
|
||||||
return;
|
return;
|
||||||
@@ -659,13 +678,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send resume event to JavaScript
|
this.appView.handleResume(this.keepRunning, this.activityResultKeepRunning);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If app doesn't want to run in background
|
// If app doesn't want to run in background
|
||||||
if (!this.keepRunning || this.activityResultKeepRunning) {
|
if (!this.keepRunning || this.activityResultKeepRunning) {
|
||||||
@@ -675,9 +688,6 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
this.keepRunning = this.activityResultKeepRunning;
|
this.keepRunning = this.activityResultKeepRunning;
|
||||||
this.activityResultKeepRunning = false;
|
this.activityResultKeepRunning = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resume JavaScript timers (including setInterval)
|
|
||||||
this.appView.resumeTimers();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -689,18 +699,11 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
LOG.d(TAG, "onDestroy()");
|
LOG.d(TAG, "onDestroy()");
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
|
|
||||||
|
// hide the splash screen to avoid leaking a window
|
||||||
|
this.removeSplashScreen();
|
||||||
|
|
||||||
if (this.appView != null) {
|
if (this.appView != null) {
|
||||||
|
appView.handleDestroy();
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.endActivity();
|
this.endActivity();
|
||||||
@@ -741,8 +744,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
* @param message
|
* @param message
|
||||||
*/
|
*/
|
||||||
public void sendJavascript(String statement) {
|
public void sendJavascript(String statement) {
|
||||||
if (this.appView != null && this.appView.callbackServer != null) {
|
if (this.appView != null) {
|
||||||
this.appView.callbackServer.sendJavascript(statement);
|
this.appView.jsMessageQueue.addJavaScript(statement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,10 +770,10 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop spinner.
|
* Stop spinner - Must be called from UI thread
|
||||||
*/
|
*/
|
||||||
public void spinnerStop() {
|
public void spinnerStop() {
|
||||||
if (this.spinnerDialog != null) {
|
if (this.spinnerDialog != null && this.spinnerDialog.isShowing()) {
|
||||||
this.spinnerDialog.dismiss();
|
this.spinnerDialog.dismiss();
|
||||||
this.spinnerDialog = null;
|
this.spinnerDialog = null;
|
||||||
}
|
}
|
||||||
@@ -784,16 +787,16 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
super.finish();
|
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.
|
* your onActivityResult() method will be called.
|
||||||
*
|
*
|
||||||
* @param command The command object
|
* @param command The command object
|
||||||
* @param intent The intent to start
|
* @param intent The intent to start
|
||||||
* @param requestCode The request code that is passed to callback to identify the activity
|
* @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.activityResultCallback = command;
|
||||||
this.activityResultKeepRunning = this.keepRunning;
|
this.activityResultKeepRunning = this.keepRunning;
|
||||||
|
|
||||||
@@ -817,14 +820,40 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||||
*/
|
*/
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
LOG.d(TAG, "Incoming Result");
|
||||||
super.onActivityResult(requestCode, resultCode, intent);
|
super.onActivityResult(requestCode, resultCode, intent);
|
||||||
IPlugin callback = this.activityResultCallback;
|
Log.d(TAG, "Request code = " + requestCode);
|
||||||
if (callback != null) {
|
ValueCallback<Uri> mUploadMessage = this.appView.getWebChromeClient().getValueCallback();
|
||||||
|
if (requestCode == CordovaChromeClient.FILECHOOSER_RESULTCODE) {
|
||||||
|
Log.d(TAG, "did we get here?");
|
||||||
|
if (null == mUploadMessage)
|
||||||
|
return;
|
||||||
|
Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData();
|
||||||
|
Log.d(TAG, "result = " + result);
|
||||||
|
// Uri filepath = Uri.parse("file://" + FileUtils.getRealPathFromURI(result, this));
|
||||||
|
// Log.d(TAG, "result = " + filepath);
|
||||||
|
mUploadMessage.onReceiveValue(result);
|
||||||
|
mUploadMessage = null;
|
||||||
|
}
|
||||||
|
CordovaPlugin callback = this.activityResultCallback;
|
||||||
|
if(callback == null)
|
||||||
|
{
|
||||||
|
if(initCallbackClass != null)
|
||||||
|
{
|
||||||
|
this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass);
|
||||||
|
callback = activityResultCallback;
|
||||||
|
LOG.d(TAG, "We have a callback to send this result to");
|
||||||
|
callback.onActivityResult(requestCode, resultCode, intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.d(TAG, "We have a callback to send this result to");
|
||||||
callback.onActivityResult(requestCode, resultCode, intent);
|
callback.onActivityResult(requestCode, resultCode, intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setActivityResultCallback(IPlugin plugin) {
|
public void setActivityResultCallback(CordovaPlugin plugin) {
|
||||||
this.activityResultCallback = plugin;
|
this.activityResultCallback = plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,16 +868,15 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
|
public void onReceivedError(final int errorCode, final String description, final String failingUrl) {
|
||||||
final DroidGap me = this;
|
final DroidGap me = this;
|
||||||
|
|
||||||
// Stop "app loading" spinner if showing
|
|
||||||
this.spinnerStop();
|
|
||||||
|
|
||||||
// If errorUrl specified, then load it
|
// If errorUrl specified, then load it
|
||||||
final String errorUrl = me.getStringProperty("errorUrl", null);
|
final String errorUrl = me.getStringProperty("errorUrl", null);
|
||||||
if ((errorUrl != null) && (errorUrl.startsWith("file://") || errorUrl.indexOf(me.baseUrl) == 0 || this.appView.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
if ((errorUrl != null) && (errorUrl.startsWith("file://") || Config.isUrlWhiteListed(errorUrl)) && (!failingUrl.equals(errorUrl))) {
|
||||||
|
|
||||||
// Load URL on UI thread
|
// Load URL on UI thread
|
||||||
me.runOnUiThread(new Runnable() {
|
me.runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
// Stop "app loading" spinner if showing
|
||||||
|
me.spinnerStop();
|
||||||
me.appView.showWebPage(errorUrl, false, true, null);
|
me.appView.showWebPage(errorUrl, false, true, null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -858,8 +886,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
|
final boolean exit = !(errorCode == WebViewClient.ERROR_HOST_LOOKUP);
|
||||||
me.runOnUiThread(new Runnable() {
|
me.runOnUiThread(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
if (exit)
|
if (exit) {
|
||||||
{
|
|
||||||
me.appView.setVisibility(View.GONE);
|
me.appView.setVisibility(View.GONE);
|
||||||
me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit);
|
me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit);
|
||||||
}
|
}
|
||||||
@@ -910,11 +937,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean isUrlWhiteListed(String url) {
|
public boolean isUrlWhiteListed(String url) {
|
||||||
// Check to see if we have matched url previously
|
return Config.isUrlWhiteListed(url);
|
||||||
if (this.appView != null) {
|
|
||||||
return this.appView.isUrlWhiteListed(url);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -930,7 +953,7 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||||
this.postMessage("onPrepareOptionsMenu", menu);
|
this.postMessage("onPrepareOptionsMenu", menu);
|
||||||
return super.onPrepareOptionsMenu(menu);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -945,25 +968,8 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return this.getContext();
|
LOG.d(TAG, "This will be deprecated December 2012");
|
||||||
}
|
return this;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -982,23 +988,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;
|
protected Dialog splashDialog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the Dialog that displays the splash screen
|
* Removes the Dialog that displays the splash screen
|
||||||
*/
|
*/
|
||||||
public void removeSplashScreen() {
|
public void removeSplashScreen() {
|
||||||
if (splashDialog != null) {
|
if (splashDialog != null && splashDialog.isShowing()) {
|
||||||
splashDialog.dismiss();
|
splashDialog.dismiss();
|
||||||
splashDialog = null;
|
splashDialog = null;
|
||||||
}
|
}
|
||||||
@@ -1008,42 +1004,81 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
* Shows the splash screen over the full Activity
|
* Shows the splash screen over the full Activity
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
protected void showSplashScreen(int time) {
|
protected void showSplashScreen(final int time) {
|
||||||
|
final DroidGap that = this;
|
||||||
|
|
||||||
// Get reference to display
|
Runnable runnable = new Runnable() {
|
||||||
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() {
|
|
||||||
public void run() {
|
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)
|
||||||
|
{
|
||||||
|
//Get whatever has focus!
|
||||||
|
View childView = appView.getFocusedChild();
|
||||||
|
if ((appView.isCustomViewShowing() || childView != null ) && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
return appView.onKeyUp(keyCode, event);
|
||||||
|
} else {
|
||||||
|
return super.onKeyUp(keyCode, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Android 2.x needs to be able to check where the cursor is. Android 4.x does not
|
||||||
|
*
|
||||||
|
* (non-Javadoc)
|
||||||
|
* @see android.app.Activity#onKeyDown(int, android.view.KeyEvent)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||||
|
{
|
||||||
|
//Get whatever has focus!
|
||||||
|
View childView = appView.getFocusedChild();
|
||||||
|
//Determine if the focus is on the current view or not
|
||||||
|
if (childView != null && keyCode == KeyEvent.KEYCODE_BACK) {
|
||||||
|
return appView.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return super.onKeyDown(keyCode, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a message is sent to plugin.
|
* Called when a message is sent to plugin.
|
||||||
*
|
*
|
||||||
@@ -1058,8 +1093,11 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
this.removeSplashScreen();
|
this.removeSplashScreen();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
// If the splash dialog is showing don't try to show it again
|
||||||
this.showSplashScreen(this.splashscreenTime);
|
if (this.splashDialog == null || !this.splashDialog.isShowing()) {
|
||||||
|
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||||
|
this.showSplashScreen(this.splashscreenTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ("spinner".equals(id)) {
|
else if ("spinner".equals(id)) {
|
||||||
@@ -1082,4 +1120,18 @@ public class DroidGap extends Activity implements CordovaInterface {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExecutorService getThreadPool() {
|
||||||
|
return threadPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void onSaveInstanceState(Bundle outState)
|
||||||
|
{
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
if(this.activityResultCallback != null)
|
||||||
|
{
|
||||||
|
String cClass = this.activityResultCallback.getClass().getName();
|
||||||
|
outState.putString("callbackClass", cClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
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
|
specific language governing permissions and limitations
|
||||||
under the License.
|
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.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) {
|
@Override
|
||||||
super(status);
|
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)) {
|
||||||
public PluginResult(Status status, String message) {
|
callbackContext.success(result);
|
||||||
super(status, message);
|
return true;
|
||||||
}
|
} else if ("echoAsync".equals(action)) {
|
||||||
|
cordova.getThreadPool().execute(new Runnable() {
|
||||||
public PluginResult(Status status, JSONArray message) {
|
public void run() {
|
||||||
super(status, message);
|
callbackContext.success(result);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
public PluginResult(Status status, JSONObject message) {
|
return true;
|
||||||
super(status, message);
|
}
|
||||||
}
|
return false;
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,7 @@ import java.io.IOException;
|
|||||||
import android.media.ExifInterface;
|
import android.media.ExifInterface;
|
||||||
|
|
||||||
public class ExifHelper {
|
public class ExifHelper {
|
||||||
private String aperature = null;
|
private String aperture = null;
|
||||||
private String datetime = null;
|
private String datetime = null;
|
||||||
private String exposureTime = null;
|
private String exposureTime = null;
|
||||||
private String flash = null;
|
private String flash = null;
|
||||||
@@ -70,7 +70,7 @@ public class ExifHelper {
|
|||||||
* Reads all the EXIF data from the input file.
|
* Reads all the EXIF data from the input file.
|
||||||
*/
|
*/
|
||||||
public void readExifData() {
|
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.datetime = inFile.getAttribute(ExifInterface.TAG_DATETIME);
|
||||||
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
|
this.exposureTime = inFile.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
|
||||||
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
|
this.flash = inFile.getAttribute(ExifInterface.TAG_FLASH);
|
||||||
@@ -102,8 +102,8 @@ public class ExifHelper {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.aperature != null) {
|
if (this.aperture != null) {
|
||||||
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperature);
|
this.outFile.setAttribute(ExifInterface.TAG_APERTURE, this.aperture);
|
||||||
}
|
}
|
||||||
if (this.datetime != null) {
|
if (this.datetime != null) {
|
||||||
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime);
|
this.outFile.setAttribute(ExifInterface.TAG_DATETIME, this.datetime);
|
||||||
@@ -162,4 +162,24 @@ public class ExifHelper {
|
|||||||
|
|
||||||
this.outFile.saveAttributes();
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
65
framework/src/org/apache/cordova/ExposedJsApi.java
Executable file
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import android.webkit.JavascriptInterface;
|
||||||
|
import org.apache.cordova.api.PluginManager;
|
||||||
|
import org.apache.cordova.api.PluginResult;
|
||||||
|
import org.json.JSONException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains APIs that the JS can call. All functions in here should also have
|
||||||
|
* an equivalent entry in CordovaChromeClient.java, and be added to
|
||||||
|
* cordova-js/lib/android/plugin/android/promptbasednativeapi.js
|
||||||
|
*/
|
||||||
|
/* package */ class ExposedJsApi {
|
||||||
|
|
||||||
|
private PluginManager pluginManager;
|
||||||
|
private NativeToJsMessageQueue jsMessageQueue;
|
||||||
|
|
||||||
|
public ExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) {
|
||||||
|
this.pluginManager = pluginManager;
|
||||||
|
this.jsMessageQueue = jsMessageQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String exec(String service, String action, String callbackId, String arguments) throws JSONException {
|
||||||
|
jsMessageQueue.setPaused(true);
|
||||||
|
try {
|
||||||
|
boolean wasSync = pluginManager.exec(service, action, callbackId, arguments);
|
||||||
|
String ret = "";
|
||||||
|
if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING || wasSync) {
|
||||||
|
ret = jsMessageQueue.popAndEncode();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
} finally {
|
||||||
|
jsMessageQueue.setPaused(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public void setNativeToJsBridgeMode(int value) {
|
||||||
|
jsMessageQueue.setBridgeMode(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String retrieveJsMessages() {
|
||||||
|
return jsMessageQueue.popAndEncode();
|
||||||
|
}
|
||||||
|
}
|
||||||
63
framework/src/org/apache/cordova/FileProgressResult.java
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates in-progress status of uploading or downloading a file to a remote server.
|
||||||
|
*/
|
||||||
|
public class FileProgressResult {
|
||||||
|
|
||||||
|
private boolean lengthComputable = false; // declares whether total is known
|
||||||
|
private long loaded = 0; // bytes sent so far
|
||||||
|
private long total = 0; // bytes total, if known
|
||||||
|
|
||||||
|
public boolean getLengthComputable() {
|
||||||
|
return lengthComputable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLengthComputable(boolean computable) {
|
||||||
|
this.lengthComputable = computable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getLoaded() {
|
||||||
|
return loaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLoaded(long bytes) {
|
||||||
|
this.loaded = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTotal() {
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotal(long bytes) {
|
||||||
|
this.total = bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSONObject toJSONObject() throws JSONException {
|
||||||
|
return new JSONObject(
|
||||||
|
"{loaded:" + loaded +
|
||||||
|
",total:" + total +
|
||||||
|
",lengthComputable:" + (lengthComputable ? "true" : "false") + "}");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,7 @@ public class FileUploadResult {
|
|||||||
private long bytesSent = 0; // bytes sent
|
private long bytesSent = 0; // bytes sent
|
||||||
private int responseCode = -1; // HTTP response code
|
private int responseCode = -1; // HTTP response code
|
||||||
private String response = null; // HTTP response
|
private String response = null; // HTTP response
|
||||||
|
private String objectId = null; // FileTransfer object id
|
||||||
|
|
||||||
public long getBytesSent() {
|
public long getBytesSent() {
|
||||||
return bytesSent;
|
return bytesSent;
|
||||||
@@ -54,10 +55,19 @@ public class FileUploadResult {
|
|||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getObjectId() {
|
||||||
|
return objectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setObjectId(String objectId) {
|
||||||
|
this.objectId = objectId;
|
||||||
|
}
|
||||||
|
|
||||||
public JSONObject toJSONObject() throws JSONException {
|
public JSONObject toJSONObject() throws JSONException {
|
||||||
return new JSONObject(
|
return new JSONObject(
|
||||||
"{bytesSent:" + bytesSent +
|
"{bytesSent:" + bytesSent +
|
||||||
",responseCode:" + responseCode +
|
",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 java.nio.channels.FileChannel;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
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.api.PluginResult;
|
||||||
import org.apache.cordova.file.EncodingException;
|
import org.apache.cordova.file.EncodingException;
|
||||||
import org.apache.cordova.file.FileExistsException;
|
import org.apache.cordova.file.FileExistsException;
|
||||||
@@ -42,6 +43,7 @@ import android.database.Cursor;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
//import android.app.Activity;
|
//import android.app.Activity;
|
||||||
@@ -50,7 +52,7 @@ import android.webkit.MimeTypeMap;
|
|||||||
* This class provides SD card file and directory services to JavaScript.
|
* This class provides SD card file and directory services to JavaScript.
|
||||||
* Only files on the SD card can be accessed.
|
* Only files on the SD card can be accessed.
|
||||||
*/
|
*/
|
||||||
public class FileUtils extends Plugin {
|
public class FileUtils extends CordovaPlugin {
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
private static final String LOG_TAG = "FileUtils";
|
private static final String LOG_TAG = "FileUtils";
|
||||||
private static final String _DATA = "_data"; // The column name where the file path is stored
|
private static final String _DATA = "_data"; // The column name where the file path is stored
|
||||||
@@ -84,83 +86,96 @@ 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 action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @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.
|
* @return True if the action was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
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 = "";
|
|
||||||
//System.out.println("FileUtils.execute("+action+")");
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (action.equals("testSaveLocationExists")) {
|
if (action.equals("testSaveLocationExists")) {
|
||||||
boolean b = DirectoryManager.testSaveLocationExists();
|
boolean b = DirectoryManager.testSaveLocationExists();
|
||||||
return new PluginResult(status, b);
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, b));
|
||||||
}
|
}
|
||||||
else if (action.equals("getFreeDiskSpace")) {
|
else if (action.equals("getFreeDiskSpace")) {
|
||||||
long l = DirectoryManager.getFreeDiskSpace(false);
|
long l = DirectoryManager.getFreeDiskSpace(false);
|
||||||
return new PluginResult(status, l);
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, l));
|
||||||
}
|
}
|
||||||
else if (action.equals("testFileExists")) {
|
else if (action.equals("testFileExists")) {
|
||||||
boolean b = DirectoryManager.testFileExists(args.getString(0));
|
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")) {
|
else if (action.equals("testDirectoryExists")) {
|
||||||
boolean b = DirectoryManager.testFileExists(args.getString(0));
|
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")) {
|
else if (action.equals("readAsText")) {
|
||||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
int start = 0;
|
||||||
return new PluginResult(status, s);
|
int end = Integer.MAX_VALUE;
|
||||||
|
if (args.length() >= 3) {
|
||||||
|
start = args.getInt(2);
|
||||||
|
}
|
||||||
|
if (args.length() >= 4) {
|
||||||
|
end = args.getInt(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = this.readAsText(args.getString(0), args.getString(1), start, end);
|
||||||
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, s));
|
||||||
}
|
}
|
||||||
else if (action.equals("readAsDataURL")) {
|
else if (action.equals("readAsDataURL")) {
|
||||||
String s = this.readAsDataURL(args.getString(0));
|
int start = 0;
|
||||||
return new PluginResult(status, s);
|
int end = Integer.MAX_VALUE;
|
||||||
|
if (args.length() >= 2) {
|
||||||
|
start = args.getInt(1);
|
||||||
|
}
|
||||||
|
if (args.length() >= 3) {
|
||||||
|
end = args.getInt(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
String s = this.readAsDataURL(args.getString(0), start, end);
|
||||||
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, s));
|
||||||
}
|
}
|
||||||
else if (action.equals("write")) {
|
else if (action.equals("write")) {
|
||||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
|
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")) {
|
else if (action.equals("truncate")) {
|
||||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
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")) {
|
else if (action.equals("requestFileSystem")) {
|
||||||
long size = args.optLong(1);
|
long size = args.optLong(1);
|
||||||
if (size != 0) {
|
if (size != 0 && size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) {
|
||||||
if (size > (DirectoryManager.getFreeDiskSpace(true) * 1024)) {
|
callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, FileUtils.QUOTA_EXCEEDED_ERR));
|
||||||
return 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")) {
|
else if (action.equals("resolveLocalFileSystemURI")) {
|
||||||
JSONObject obj = resolveLocalFileSystemURI(args.getString(0));
|
JSONObject obj = resolveLocalFileSystemURI(args.getString(0));
|
||||||
return new PluginResult(status, obj);
|
callbackContext.success(obj);
|
||||||
}
|
}
|
||||||
else if (action.equals("getMetadata")) {
|
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")) {
|
else if (action.equals("getFileMetadata")) {
|
||||||
JSONObject obj = getFileMetadata(args.getString(0));
|
JSONObject obj = getFileMetadata(args.getString(0));
|
||||||
return new PluginResult(status, obj);
|
callbackContext.success(obj);
|
||||||
}
|
}
|
||||||
else if (action.equals("getParent")) {
|
else if (action.equals("getParent")) {
|
||||||
JSONObject obj = getParent(args.getString(0));
|
JSONObject obj = getParent(args.getString(0));
|
||||||
return new PluginResult(status, obj);
|
callbackContext.success(obj);
|
||||||
}
|
}
|
||||||
else if (action.equals("getDirectory")) {
|
else if (action.equals("getDirectory")) {
|
||||||
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true);
|
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")) {
|
else if (action.equals("getFile")) {
|
||||||
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false);
|
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")) {
|
else if (action.equals("remove")) {
|
||||||
boolean success;
|
boolean success;
|
||||||
@@ -169,51 +184,52 @@ public class FileUtils extends Plugin {
|
|||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
notifyDelete(args.getString(0));
|
notifyDelete(args.getString(0));
|
||||||
return new PluginResult(status);
|
callbackContext.success();
|
||||||
} else {
|
} else {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action.equals("removeRecursively")) {
|
else if (action.equals("removeRecursively")) {
|
||||||
boolean success = removeRecursively(args.getString(0));
|
boolean success = removeRecursively(args.getString(0));
|
||||||
if (success) {
|
if (success) {
|
||||||
return new PluginResult(status);
|
callbackContext.success();
|
||||||
} else {
|
} else {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action.equals("moveTo")) {
|
else if (action.equals("moveTo")) {
|
||||||
JSONObject entry = transferTo(args.getString(0), args.getString(1), args.getString(2), true);
|
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")) {
|
else if (action.equals("copyTo")) {
|
||||||
JSONObject entry = transferTo(args.getString(0), args.getString(1), args.getString(2), false);
|
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")) {
|
else if (action.equals("readEntries")) {
|
||||||
JSONArray entries = readEntries(args.getString(0));
|
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) {
|
} catch (FileNotFoundException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
|
callbackContext.error(FileUtils.NOT_FOUND_ERR);
|
||||||
} catch (FileExistsException e) {
|
} catch (FileExistsException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.PATH_EXISTS_ERR);
|
callbackContext.error(FileUtils.PATH_EXISTS_ERR);
|
||||||
} catch (NoModificationAllowedException e) {
|
} catch (NoModificationAllowedException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
callbackContext.error(FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||||
} catch (JSONException e) {
|
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
|
||||||
} catch (InvalidModificationException e) {
|
} catch (InvalidModificationException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR);
|
callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR);
|
||||||
} catch (MalformedURLException e) {
|
} catch (MalformedURLException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR);
|
callbackContext.error(FileUtils.ENCODING_ERR);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.INVALID_MODIFICATION_ERR);
|
callbackContext.error(FileUtils.INVALID_MODIFICATION_ERR);
|
||||||
} catch (EncodingException e) {
|
} catch (EncodingException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.ENCODING_ERR);
|
callbackContext.error(FileUtils.ENCODING_ERR);
|
||||||
} catch (TypeMismatchException e) {
|
} catch (TypeMismatchException e) {
|
||||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.TYPE_MISMATCH_ERR);
|
callbackContext.error(FileUtils.TYPE_MISMATCH_ERR);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -223,9 +239,15 @@ public class FileUtils extends Plugin {
|
|||||||
*/
|
*/
|
||||||
private void notifyDelete(String filePath) {
|
private void notifyDelete(String filePath) {
|
||||||
String newFilePath = stripFileProtocol(filePath);
|
String newFilePath = stripFileProtocol(filePath);
|
||||||
int result = this.ctx.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
try {
|
||||||
MediaStore.Images.Media.DATA + " = ?",
|
this.cordova.getActivity().getContentResolver().delete(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
|
||||||
new String[] { filePath });
|
MediaStore.Images.Media.DATA + " = ?",
|
||||||
|
new String[] { newFilePath });
|
||||||
|
} catch (UnsupportedOperationException t) {
|
||||||
|
// Was seeing this on the File mobile-spec tests on 4.0.3 x86 emulator.
|
||||||
|
// The ContentResolver applies only when the file was registered in the
|
||||||
|
// first case, which is generally only the case with images.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -246,7 +268,7 @@ public class FileUtils extends Plugin {
|
|||||||
|
|
||||||
// Handle the special case where you get an Android content:// uri.
|
// Handle the special case where you get an Android content:// uri.
|
||||||
if (decoded.startsWith("content:")) {
|
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"
|
// Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data"
|
||||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||||
cursor.moveToFirst();
|
cursor.moveToFirst();
|
||||||
@@ -298,7 +320,9 @@ public class FileUtils extends Plugin {
|
|||||||
if (fp.isDirectory()) {
|
if (fp.isDirectory()) {
|
||||||
File[] files = fp.listFiles();
|
File[] files = fp.listFiles();
|
||||||
for (int i = 0; i < files.length; i++) {
|
for (int i = 0; i < files.length; i++) {
|
||||||
entries.put(getEntry(files[i]));
|
if (files[i].canRead()) {
|
||||||
|
entries.put(getEntry(files[i]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -318,8 +342,9 @@ public class FileUtils extends Plugin {
|
|||||||
* @throws InvalidModificationException
|
* @throws InvalidModificationException
|
||||||
* @throws EncodingException
|
* @throws EncodingException
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
|
* @throws FileExistsException
|
||||||
*/
|
*/
|
||||||
private JSONObject transferTo(String fileName, String newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException {
|
private JSONObject transferTo(String fileName, String newParent, String newName, boolean move) throws JSONException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException, FileExistsException {
|
||||||
fileName = stripFileProtocol(fileName);
|
fileName = stripFileProtocol(fileName);
|
||||||
newParent = stripFileProtocol(newParent);
|
newParent = stripFileProtocol(newParent);
|
||||||
|
|
||||||
@@ -407,23 +432,31 @@ public class FileUtils extends Plugin {
|
|||||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
FileChannel input = new FileInputStream(srcFile).getChannel();
|
copyAction(srcFile, destFile);
|
||||||
FileChannel output = new FileOutputStream(destFile).getChannel();
|
|
||||||
|
|
||||||
input.transferTo(0, input.size(), output);
|
|
||||||
|
|
||||||
input.close();
|
|
||||||
output.close();
|
|
||||||
|
|
||||||
/*
|
|
||||||
if (srcFile.length() != destFile.length()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return getEntry(destFile);
|
return getEntry(destFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moved this code into it's own method so moveTo could use it when the move is across file systems
|
||||||
|
*/
|
||||||
|
private void copyAction(File srcFile, File destFile)
|
||||||
|
throws FileNotFoundException, IOException {
|
||||||
|
FileInputStream istream = new FileInputStream(srcFile);
|
||||||
|
FileOutputStream ostream = new FileOutputStream(destFile);
|
||||||
|
FileChannel input = istream.getChannel();
|
||||||
|
FileChannel output = ostream.getChannel();
|
||||||
|
|
||||||
|
try {
|
||||||
|
input.transferTo(0, input.size(), output);
|
||||||
|
} finally {
|
||||||
|
istream.close();
|
||||||
|
ostream.close();
|
||||||
|
input.close();
|
||||||
|
output.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy a directory
|
* Copy a directory
|
||||||
*
|
*
|
||||||
@@ -478,7 +511,7 @@ public class FileUtils extends Plugin {
|
|||||||
|
|
||||||
// This weird test is to determine if we are copying or moving a directory into itself.
|
// 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 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) {
|
if (dest.startsWith(src) && dest.indexOf(File.separator, src.length() - 1) != -1) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -496,7 +529,7 @@ public class FileUtils extends Plugin {
|
|||||||
* @throws InvalidModificationException
|
* @throws InvalidModificationException
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
private JSONObject moveFile(File srcFile, File destFile) throws JSONException, InvalidModificationException {
|
private JSONObject moveFile(File srcFile, File destFile) throws IOException, JSONException, InvalidModificationException {
|
||||||
// Renaming a file to an existing directory should fail
|
// Renaming a file to an existing directory should fail
|
||||||
if (destFile.exists() && destFile.isDirectory()) {
|
if (destFile.exists() && destFile.isDirectory()) {
|
||||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||||
@@ -508,6 +541,12 @@ public class FileUtils extends Plugin {
|
|||||||
// Now we have to do things the hard way
|
// Now we have to do things the hard way
|
||||||
// 1) Copy all the old file
|
// 1) Copy all the old file
|
||||||
// 2) delete the src file
|
// 2) delete the src file
|
||||||
|
copyAction(srcFile, destFile);
|
||||||
|
if (destFile.exists()) {
|
||||||
|
srcFile.delete();
|
||||||
|
} else {
|
||||||
|
throw new IOException("moved failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getEntry(destFile);
|
return getEntry(destFile);
|
||||||
@@ -522,8 +561,10 @@ public class FileUtils extends Plugin {
|
|||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws InvalidModificationException
|
* @throws InvalidModificationException
|
||||||
|
* @throws NoModificationAllowedException
|
||||||
|
* @throws FileExistsException
|
||||||
*/
|
*/
|
||||||
private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, InvalidModificationException {
|
private JSONObject moveDirectory(File srcDir, File destinationDir) throws IOException, JSONException, InvalidModificationException, NoModificationAllowedException, FileExistsException {
|
||||||
// Renaming a file to an existing directory should fail
|
// Renaming a file to an existing directory should fail
|
||||||
if (destinationDir.exists() && destinationDir.isFile()) {
|
if (destinationDir.exists() && destinationDir.isFile()) {
|
||||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||||
@@ -547,6 +588,12 @@ public class FileUtils extends Plugin {
|
|||||||
// Now we have to do things the hard way
|
// Now we have to do things the hard way
|
||||||
// 1) Copy all the old files
|
// 1) Copy all the old files
|
||||||
// 2) delete the src directory
|
// 2) delete the src directory
|
||||||
|
copyDirectory(srcDir, destinationDir);
|
||||||
|
if (destinationDir.exists()) {
|
||||||
|
removeDirRecursively(srcDir);
|
||||||
|
} else {
|
||||||
|
throw new IOException("moved failed");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getEntry(destinationDir);
|
return getEntry(destinationDir);
|
||||||
@@ -728,9 +775,9 @@ public class FileUtils extends Plugin {
|
|||||||
private boolean atRootDirectory(String filePath) {
|
private boolean atRootDirectory(String filePath) {
|
||||||
filePath = stripFileProtocol(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(Environment.getExternalStorageDirectory().getAbsolutePath()) ||
|
||||||
filePath.equals("/data/data/" + ctx.getActivity().getPackageName())) {
|
filePath.equals("/data/data/" + cordova.getActivity().getPackageName())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -819,16 +866,16 @@ public class FileUtils extends Plugin {
|
|||||||
fs.put("name", "temporary");
|
fs.put("name", "temporary");
|
||||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
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.
|
// Create the cache dir if it doesn't exist.
|
||||||
fp.mkdirs();
|
fp.mkdirs();
|
||||||
fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||||
"/Android/data/" + ctx.getActivity().getPackageName() + "/cache/"));
|
"/Android/data/" + cordova.getActivity().getPackageName() + "/cache/"));
|
||||||
} else {
|
} 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.
|
// Create the cache dir if it doesn't exist.
|
||||||
fp.mkdirs();
|
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) {
|
else if (type == PERSISTENT) {
|
||||||
@@ -836,7 +883,7 @@ public class FileUtils extends Plugin {
|
|||||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||||
fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
|
fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
|
||||||
} else {
|
} else {
|
||||||
fs.put("root", getEntry("/data/data/" + ctx.getActivity().getPackageName()));
|
fs.put("root", getEntry("/data/data/" + cordova.getActivity().getPackageName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -909,17 +956,27 @@ public class FileUtils extends Plugin {
|
|||||||
* @param filename The name of the file.
|
* @param filename The name of the file.
|
||||||
* @param encoding The encoding to return contents as. Typical value is UTF-8.
|
* @param encoding The encoding to return contents as. Typical value is UTF-8.
|
||||||
* (see http://www.iana.org/assignments/character-sets)
|
* (see http://www.iana.org/assignments/character-sets)
|
||||||
|
* @param start Start position in the file.
|
||||||
|
* @param end End position to stop at (exclusive).
|
||||||
* @return Contents of file.
|
* @return Contents of file.
|
||||||
* @throws FileNotFoundException, IOException
|
* @throws FileNotFoundException, IOException
|
||||||
*/
|
*/
|
||||||
public String readAsText(String filename, String encoding) throws FileNotFoundException, IOException {
|
public String readAsText(String filename, String encoding, int start, int end) throws FileNotFoundException, IOException {
|
||||||
|
int diff = end - start;
|
||||||
byte[] bytes = new byte[1000];
|
byte[] bytes = new byte[1000];
|
||||||
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
|
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
int numRead = 0;
|
int numRead = 0;
|
||||||
while ((numRead = bis.read(bytes, 0, 1000)) >= 0) {
|
|
||||||
|
if (start > 0) {
|
||||||
|
bis.skip(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( diff > 0 && (numRead = bis.read(bytes, 0, Math.min(1000, diff))) >= 0) {
|
||||||
|
diff -= numRead;
|
||||||
bos.write(bytes, 0, numRead);
|
bos.write(bytes, 0, numRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String(bos.toByteArray(), encoding);
|
return new String(bos.toByteArray(), encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,12 +987,19 @@ public class FileUtils extends Plugin {
|
|||||||
* @return Contents of file = data:<media type>;base64,<data>
|
* @return Contents of file = data:<media type>;base64,<data>
|
||||||
* @throws FileNotFoundException, IOException
|
* @throws FileNotFoundException, IOException
|
||||||
*/
|
*/
|
||||||
public String readAsDataURL(String filename) throws FileNotFoundException, IOException {
|
public String readAsDataURL(String filename, int start, int end) throws FileNotFoundException, IOException {
|
||||||
|
int diff = end - start;
|
||||||
byte[] bytes = new byte[1000];
|
byte[] bytes = new byte[1000];
|
||||||
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
|
BufferedInputStream bis = new BufferedInputStream(getPathFromUri(filename), 1024);
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
int numRead = 0;
|
int numRead = 0;
|
||||||
while ((numRead = bis.read(bytes, 0, 1000)) >= 0) {
|
|
||||||
|
if (start > 0) {
|
||||||
|
bis.skip(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (diff > 0 && (numRead = bis.read(bytes, 0, Math.min(1000, diff))) >= 0) {
|
||||||
|
diff -= numRead;
|
||||||
bos.write(bytes, 0, numRead);
|
bos.write(bytes, 0, numRead);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -943,7 +1007,7 @@ public class FileUtils extends Plugin {
|
|||||||
String contentType = null;
|
String contentType = null;
|
||||||
if (filename.startsWith("content:")) {
|
if (filename.startsWith("content:")) {
|
||||||
Uri fileUri = Uri.parse(filename);
|
Uri fileUri = Uri.parse(filename);
|
||||||
contentType = this.ctx.getActivity().getContentResolver().getType(fileUri);
|
contentType = this.cordova.getActivity().getContentResolver().getType(fileUri);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
contentType = getMimeType(filename);
|
contentType = getMimeType(filename);
|
||||||
@@ -961,8 +1025,22 @@ public class FileUtils extends Plugin {
|
|||||||
* @return a mime type
|
* @return a mime type
|
||||||
*/
|
*/
|
||||||
public static String getMimeType(String filename) {
|
public static String getMimeType(String filename) {
|
||||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
if (filename != null) {
|
||||||
return map.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(filename));
|
// Stupid bug in getFileExtensionFromUrl when the file name has a space
|
||||||
|
// So we need to replace the space with a url encoded %20
|
||||||
|
|
||||||
|
// CB-2185: Stupid bug not putting JPG extension in the mime-type map
|
||||||
|
String url = filename.replace(" ", "%20").toLowerCase();
|
||||||
|
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||||
|
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
|
||||||
|
if (extension.toLowerCase().equals("3ga")) {
|
||||||
|
return "audio/3gpp";
|
||||||
|
} else {
|
||||||
|
return map.getMimeTypeFromExtension(extension);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1006,14 +1084,17 @@ public class FileUtils extends Plugin {
|
|||||||
filename = stripFileProtocol(filename);
|
filename = stripFileProtocol(filename);
|
||||||
|
|
||||||
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
|
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
|
||||||
|
try {
|
||||||
if (raf.length() >= size) {
|
if (raf.length() >= size) {
|
||||||
FileChannel channel = raf.getChannel();
|
FileChannel channel = raf.getChannel();
|
||||||
channel.truncate(size);
|
channel.truncate(size);
|
||||||
return size;
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return raf.length();
|
||||||
|
} finally {
|
||||||
|
raf.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
return raf.length();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1026,7 +1107,7 @@ public class FileUtils extends Plugin {
|
|||||||
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
||||||
if (path.startsWith("content")) {
|
if (path.startsWith("content")) {
|
||||||
Uri uri = Uri.parse(path);
|
Uri uri = Uri.parse(path);
|
||||||
return ctx.getActivity().getContentResolver().openInputStream(uri);
|
return cordova.getActivity().getContentResolver().openInputStream(uri);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
path = stripFileProtocol(path);
|
path = stripFileProtocol(path);
|
||||||
@@ -1038,15 +1119,23 @@ public class FileUtils extends Plugin {
|
|||||||
* Queries the media store to find out what the file path is for the Uri we supply
|
* 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 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
|
* @return the full path to the file
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface ctx) {
|
protected static String getRealPathFromURI(Uri contentUri, CordovaInterface cordova) {
|
||||||
String[] proj = { _DATA };
|
final String scheme = contentUri.getScheme();
|
||||||
Cursor cursor = ctx.getActivity().managedQuery(contentUri, proj, null, null, null);
|
|
||||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
if (scheme.compareTo("content") == 0) {
|
||||||
cursor.moveToFirst();
|
String[] proj = { _DATA };
|
||||||
return cursor.getString(column_index);
|
Cursor cursor = cordova.getActivity().managedQuery(contentUri, proj, null, null, null);
|
||||||
|
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
return cursor.getString(column_index);
|
||||||
|
} else if (scheme.compareTo("file") == 0) {
|
||||||
|
return contentUri.getPath();
|
||||||
|
} else {
|
||||||
|
return contentUri.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
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.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
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
|
* 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 GPSListener gpsListener;
|
||||||
private NetworkListener networkListener;
|
private NetworkListener networkListener;
|
||||||
private LocationManager locationManager;
|
private LocationManager locationManager;
|
||||||
@@ -49,46 +50,51 @@ public class GeoBroker extends Plugin {
|
|||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArry of arguments for the plugin.
|
||||||
* @param callbackId The callback id used when calling back into JavaScript.
|
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||||
* @return A PluginResult object with a status and message.
|
* @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) {
|
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.networkListener = new NetworkListener(this.locationManager, this);
|
||||||
this.gpsListener = new GPSListener(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")) {
|
if (action.equals("getLocation")) {
|
||||||
boolean enableHighAccuracy = args.getBoolean(0);
|
boolean enableHighAccuracy = args.getBoolean(0);
|
||||||
int maximumAge = args.getInt(1);
|
int maximumAge = args.getInt(1);
|
||||||
Location last = this.locationManager.getLastKnownLocation((enableHighAccuracy ? LocationManager.GPS_PROVIDER : LocationManager.NETWORK_PROVIDER));
|
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
|
// Check if we can use lastKnownLocation to get a quick reading and use less battery
|
||||||
if ((System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
if (last != null && (System.currentTimeMillis() - last.getTime()) <= maximumAge) {
|
||||||
result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
PluginResult result = new PluginResult(PluginResult.Status.OK, this.returnLocationJSON(last));
|
||||||
|
callbackContext.sendPluginResult(result);
|
||||||
} else {
|
} else {
|
||||||
this.getCurrentLocation(callbackId, enableHighAccuracy);
|
this.getCurrentLocation(callbackContext, enableHighAccuracy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (action.equals("addWatch")) {
|
else if (action.equals("addWatch")) {
|
||||||
String id = args.getString(0);
|
String id = args.getString(0);
|
||||||
boolean enableHighAccuracy = args.getBoolean(1);
|
boolean enableHighAccuracy = args.getBoolean(1);
|
||||||
this.addWatch(id, callbackId, enableHighAccuracy);
|
this.addWatch(id, callbackContext, enableHighAccuracy);
|
||||||
}
|
}
|
||||||
else if (action.equals("clearWatch")) {
|
else if (action.equals("clearWatch")) {
|
||||||
String id = args.getString(0);
|
String id = args.getString(0);
|
||||||
this.clearWatch(id);
|
this.clearWatch(id);
|
||||||
}
|
}
|
||||||
} catch (JSONException e) {
|
else {
|
||||||
result = new PluginResult(PluginResult.Status.JSON_EXCEPTION, e.getMessage());
|
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) {
|
private void clearWatch(String id) {
|
||||||
@@ -96,42 +102,43 @@ public class GeoBroker extends Plugin {
|
|||||||
this.networkListener.clearWatch(id);
|
this.networkListener.clearWatch(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void getCurrentLocation(String callbackId, boolean enableHighAccuracy) {
|
private void getCurrentLocation(CallbackContext callbackContext, boolean enableHighAccuracy) {
|
||||||
if (enableHighAccuracy) {
|
if (enableHighAccuracy) {
|
||||||
this.gpsListener.addCallback(callbackId);
|
this.gpsListener.addCallback(callbackContext);
|
||||||
} else {
|
} 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) {
|
if (enableHighAccuracy) {
|
||||||
this.gpsListener.addWatch(timerId, callbackId);
|
this.gpsListener.addWatch(timerId, callbackContext);
|
||||||
} else {
|
} 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.
|
* Called when the activity is to be shut down.
|
||||||
* Stop listener.
|
* Stop listener.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
this.networkListener.destroy();
|
if (this.networkListener != null) {
|
||||||
this.gpsListener.destroy();
|
this.networkListener.destroy();
|
||||||
this.networkListener = null;
|
this.networkListener = null;
|
||||||
this.gpsListener = 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) {
|
public JSONObject returnLocationJSON(Location loc) {
|
||||||
@@ -153,9 +160,9 @@ public class GeoBroker extends Plugin {
|
|||||||
return o;
|
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));
|
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
|
* @param msg The error message
|
||||||
* @throws JSONException
|
* @throws JSONException
|
||||||
*/
|
*/
|
||||||
public void fail(int code, String msg, String callbackId) {
|
public void fail(int code, String msg, CallbackContext callbackContext) {
|
||||||
JSONObject obj = new JSONObject();
|
JSONObject obj = new JSONObject();
|
||||||
String backup = null;
|
String backup = null;
|
||||||
try {
|
try {
|
||||||
@@ -182,6 +189,16 @@ public class GeoBroker extends Plugin {
|
|||||||
result = new PluginResult(PluginResult.Status.ERROR, backup);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
534
framework/src/org/apache/cordova/InAppBrowser.java
Normal file
@@ -0,0 +1,534 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
|
import org.apache.cordova.api.CordovaPlugin;
|
||||||
|
import org.apache.cordova.api.PluginResult;
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.WindowManager.LayoutParams;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebSettings;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
|
public class InAppBrowser extends CordovaPlugin {
|
||||||
|
|
||||||
|
private static final String NULL = "null";
|
||||||
|
protected static final String LOG_TAG = "InAppBrowser";
|
||||||
|
private static final String SELF = "_self";
|
||||||
|
private static final String SYSTEM = "_system";
|
||||||
|
// private static final String BLANK = "_blank";
|
||||||
|
private static final String LOCATION = "location";
|
||||||
|
private static final String EXIT_EVENT = "exit";
|
||||||
|
private static final String LOAD_START_EVENT = "loadstart";
|
||||||
|
private static final String LOAD_STOP_EVENT = "loadstop";
|
||||||
|
|
||||||
|
private Dialog dialog;
|
||||||
|
private WebView inAppWebView;
|
||||||
|
private EditText edittext;
|
||||||
|
private boolean showLocationBar = true;
|
||||||
|
private CallbackContext callbackContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the request and returns PluginResult.
|
||||||
|
*
|
||||||
|
* @param action The action to execute.
|
||||||
|
* @param args JSONArry of arguments for the plugin.
|
||||||
|
* @param callbackId The callback id used when calling back into JavaScript.
|
||||||
|
* @return A PluginResult object with a status and message.
|
||||||
|
*/
|
||||||
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
|
PluginResult.Status status = PluginResult.Status.OK;
|
||||||
|
String result = "";
|
||||||
|
this.callbackContext = callbackContext;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (action.equals("open")) {
|
||||||
|
String url = args.getString(0);
|
||||||
|
String target = args.optString(1);
|
||||||
|
if (target == null || target.equals("") || target.equals(NULL)) {
|
||||||
|
target = SELF;
|
||||||
|
}
|
||||||
|
HashMap<String, Boolean> features = parseFeature(args.optString(2));
|
||||||
|
|
||||||
|
Log.d(LOG_TAG, "target = " + target);
|
||||||
|
|
||||||
|
url = updateUrl(url);
|
||||||
|
|
||||||
|
// SELF
|
||||||
|
if (SELF.equals(target)) {
|
||||||
|
Log.d(LOG_TAG, "in self");
|
||||||
|
// load in webview
|
||||||
|
if (url.startsWith("file://") || url.startsWith("javascript:")
|
||||||
|
|| Config.isUrlWhiteListed(url)) {
|
||||||
|
this.webView.loadUrl(url);
|
||||||
|
}
|
||||||
|
// load in InAppBrowser
|
||||||
|
else {
|
||||||
|
result = this.showWebPage(url, features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// SYSTEM
|
||||||
|
else if (SYSTEM.equals(target)) {
|
||||||
|
Log.d(LOG_TAG, "in system");
|
||||||
|
result = this.openExternal(url);
|
||||||
|
}
|
||||||
|
// BLANK - or anything else
|
||||||
|
else {
|
||||||
|
Log.d(LOG_TAG, "in blank");
|
||||||
|
result = this.showWebPage(url, features);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (action.equals("close")) {
|
||||||
|
closeDialog();
|
||||||
|
|
||||||
|
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK);
|
||||||
|
pluginResult.setKeepCallback(false);
|
||||||
|
this.callbackContext.sendPluginResult(pluginResult);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
status = PluginResult.Status.INVALID_ACTION;
|
||||||
|
}
|
||||||
|
PluginResult pluginResult = new PluginResult(status, result);
|
||||||
|
pluginResult.setKeepCallback(true);
|
||||||
|
this.callbackContext.sendPluginResult(pluginResult);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Put the list of features into a hash map
|
||||||
|
*
|
||||||
|
* @param optString
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private HashMap<String, Boolean> parseFeature(String optString) {
|
||||||
|
if (optString.equals(NULL)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
HashMap<String, Boolean> map = new HashMap<String, Boolean>();
|
||||||
|
StringTokenizer features = new StringTokenizer(optString, ",");
|
||||||
|
StringTokenizer option;
|
||||||
|
while(features.hasMoreElements()) {
|
||||||
|
option = new StringTokenizer(features.nextToken(), "=");
|
||||||
|
if (option.hasMoreElements()) {
|
||||||
|
String key = option.nextToken();
|
||||||
|
Boolean value = option.nextToken().equals("no") ? Boolean.FALSE : Boolean.TRUE;
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert relative URL to full path
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private String updateUrl(String url) {
|
||||||
|
Uri newUrl = Uri.parse(url);
|
||||||
|
if (newUrl.isRelative()) {
|
||||||
|
url = this.webView.getUrl().substring(0, this.webView.getUrl().lastIndexOf("/")+1) + url;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a new browser with the specified URL.
|
||||||
|
*
|
||||||
|
* @param url The url to load.
|
||||||
|
* @param usePhoneGap Load url in PhoneGap webview
|
||||||
|
* @return "" if ok, or error message.
|
||||||
|
*/
|
||||||
|
public String openExternal(String url) {
|
||||||
|
try {
|
||||||
|
Intent intent = null;
|
||||||
|
intent = new Intent(Intent.ACTION_VIEW);
|
||||||
|
intent.setData(Uri.parse(url));
|
||||||
|
this.cordova.getActivity().startActivity(intent);
|
||||||
|
return "";
|
||||||
|
} catch (android.content.ActivityNotFoundException e) {
|
||||||
|
Log.d(LOG_TAG, "InAppBrowser: Error loading url "+url+":"+ e.toString());
|
||||||
|
return e.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the dialog
|
||||||
|
*/
|
||||||
|
private void closeDialog() {
|
||||||
|
try {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("type", EXIT_EVENT);
|
||||||
|
|
||||||
|
sendUpdate(obj, false);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.d(LOG_TAG, "Should never happen");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dialog != null) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if it is possible to go back one page in history, then does so.
|
||||||
|
*/
|
||||||
|
private void goBack() {
|
||||||
|
if (this.inAppWebView.canGoBack()) {
|
||||||
|
this.inAppWebView.goBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if it is possible to go forward one page in history, then does so.
|
||||||
|
*/
|
||||||
|
private void goForward() {
|
||||||
|
if (this.inAppWebView.canGoForward()) {
|
||||||
|
this.inAppWebView.goForward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to the new page
|
||||||
|
*
|
||||||
|
* @param url to load
|
||||||
|
*/
|
||||||
|
private void navigate(String url) {
|
||||||
|
InputMethodManager imm = (InputMethodManager)this.cordova.getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||||
|
imm.hideSoftInputFromWindow(edittext.getWindowToken(), 0);
|
||||||
|
|
||||||
|
if (!url.startsWith("http") && !url.startsWith("file:")) {
|
||||||
|
this.inAppWebView.loadUrl("http://" + url);
|
||||||
|
} else {
|
||||||
|
this.inAppWebView.loadUrl(url);
|
||||||
|
}
|
||||||
|
this.inAppWebView.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should we show the location bar?
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
private boolean getShowLocationBar() {
|
||||||
|
return this.showLocationBar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a new browser with the specified URL.
|
||||||
|
*
|
||||||
|
* @param url The url to load.
|
||||||
|
* @param jsonObject
|
||||||
|
*/
|
||||||
|
public String showWebPage(final String url, HashMap<String, Boolean> features) {
|
||||||
|
// Determine if we should hide the location bar.
|
||||||
|
showLocationBar = true;
|
||||||
|
if (features != null) {
|
||||||
|
showLocationBar = features.get(LOCATION).booleanValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
final CordovaWebView thatWebView = this.webView;
|
||||||
|
|
||||||
|
// Create dialog in new thread
|
||||||
|
Runnable runnable = new Runnable() {
|
||||||
|
/**
|
||||||
|
* Convert our DIP units to Pixels
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private int dpToPixels(int dipValue) {
|
||||||
|
int value = (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
(float) dipValue,
|
||||||
|
cordova.getActivity().getResources().getDisplayMetrics()
|
||||||
|
);
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void run() {
|
||||||
|
// Let's create the main dialog
|
||||||
|
dialog = new Dialog(cordova.getActivity(), android.R.style.Theme_NoTitleBar);
|
||||||
|
dialog.getWindow().getAttributes().windowAnimations = android.R.style.Animation_Dialog;
|
||||||
|
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
dialog.setCancelable(true);
|
||||||
|
dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
try {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("type", EXIT_EVENT);
|
||||||
|
|
||||||
|
sendUpdate(obj, false);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
Log.d(LOG_TAG, "Should never happen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Main container layout
|
||||||
|
LinearLayout main = new LinearLayout(cordova.getActivity());
|
||||||
|
main.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
// Toolbar layout
|
||||||
|
RelativeLayout toolbar = new RelativeLayout(cordova.getActivity());
|
||||||
|
toolbar.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, this.dpToPixels(44)));
|
||||||
|
toolbar.setPadding(this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2), this.dpToPixels(2));
|
||||||
|
toolbar.setHorizontalGravity(Gravity.LEFT);
|
||||||
|
toolbar.setVerticalGravity(Gravity.TOP);
|
||||||
|
|
||||||
|
// Action Button Container layout
|
||||||
|
RelativeLayout actionButtonContainer = new RelativeLayout(cordova.getActivity());
|
||||||
|
actionButtonContainer.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
|
||||||
|
actionButtonContainer.setHorizontalGravity(Gravity.LEFT);
|
||||||
|
actionButtonContainer.setVerticalGravity(Gravity.CENTER_VERTICAL);
|
||||||
|
actionButtonContainer.setId(1);
|
||||||
|
|
||||||
|
// Back button
|
||||||
|
Button back = new Button(cordova.getActivity());
|
||||||
|
RelativeLayout.LayoutParams backLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||||
|
backLayoutParams.addRule(RelativeLayout.ALIGN_LEFT);
|
||||||
|
back.setLayoutParams(backLayoutParams);
|
||||||
|
back.setContentDescription("Back Button");
|
||||||
|
back.setId(2);
|
||||||
|
back.setText("<");
|
||||||
|
back.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
goBack();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Forward button
|
||||||
|
Button forward = new Button(cordova.getActivity());
|
||||||
|
RelativeLayout.LayoutParams forwardLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||||
|
forwardLayoutParams.addRule(RelativeLayout.RIGHT_OF, 2);
|
||||||
|
forward.setLayoutParams(forwardLayoutParams);
|
||||||
|
forward.setContentDescription("Forward Button");
|
||||||
|
forward.setId(3);
|
||||||
|
forward.setText(">");
|
||||||
|
forward.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
goForward();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Edit Text Box
|
||||||
|
edittext = new EditText(cordova.getActivity());
|
||||||
|
RelativeLayout.LayoutParams textLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
|
||||||
|
textLayoutParams.addRule(RelativeLayout.RIGHT_OF, 1);
|
||||||
|
textLayoutParams.addRule(RelativeLayout.LEFT_OF, 5);
|
||||||
|
edittext.setLayoutParams(textLayoutParams);
|
||||||
|
edittext.setId(4);
|
||||||
|
edittext.setSingleLine(true);
|
||||||
|
edittext.setText(url);
|
||||||
|
edittext.setInputType(InputType.TYPE_TEXT_VARIATION_URI);
|
||||||
|
edittext.setImeOptions(EditorInfo.IME_ACTION_GO);
|
||||||
|
edittext.setInputType(InputType.TYPE_NULL); // Will not except input... Makes the text NON-EDITABLE
|
||||||
|
edittext.setOnKeyListener(new View.OnKeyListener() {
|
||||||
|
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||||
|
// If the event is a key-down event on the "enter" button
|
||||||
|
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
|
||||||
|
navigate(edittext.getText().toString());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Close button
|
||||||
|
Button close = new Button(cordova.getActivity());
|
||||||
|
RelativeLayout.LayoutParams closeLayoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
|
||||||
|
closeLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||||
|
close.setLayoutParams(closeLayoutParams);
|
||||||
|
forward.setContentDescription("Close Button");
|
||||||
|
close.setId(5);
|
||||||
|
close.setText("Done");
|
||||||
|
close.setOnClickListener(new View.OnClickListener() {
|
||||||
|
public void onClick(View v) {
|
||||||
|
closeDialog();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// WebView
|
||||||
|
inAppWebView = new WebView(cordova.getActivity());
|
||||||
|
inAppWebView.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
|
||||||
|
inAppWebView.setWebChromeClient(new WebChromeClient());
|
||||||
|
WebViewClient client = new InAppBrowserClient(thatWebView, edittext);
|
||||||
|
inAppWebView.setWebViewClient(client);
|
||||||
|
WebSettings settings = inAppWebView.getSettings();
|
||||||
|
settings.setJavaScriptEnabled(true);
|
||||||
|
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||||
|
settings.setBuiltInZoomControls(true);
|
||||||
|
/**
|
||||||
|
* We need to be careful of this line as a future Android release may deprecate it out of existence.
|
||||||
|
* Can't replace it with the API 8 level call right now as our minimum SDK is 7 until May 2013
|
||||||
|
*/
|
||||||
|
// @TODO: replace with settings.setPluginState(android.webkit.WebSettings.PluginState.ON)
|
||||||
|
settings.setPluginsEnabled(true);
|
||||||
|
settings.setDatabaseEnabled(true);
|
||||||
|
String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath();
|
||||||
|
settings.setDatabasePath(databasePath);
|
||||||
|
settings.setDomStorageEnabled(true);
|
||||||
|
inAppWebView.loadUrl(url);
|
||||||
|
inAppWebView.setId(6);
|
||||||
|
inAppWebView.getSettings().setLoadWithOverviewMode(true);
|
||||||
|
inAppWebView.getSettings().setUseWideViewPort(true);
|
||||||
|
inAppWebView.requestFocus();
|
||||||
|
inAppWebView.requestFocusFromTouch();
|
||||||
|
|
||||||
|
// Add the back and forward buttons to our action button container layout
|
||||||
|
actionButtonContainer.addView(back);
|
||||||
|
actionButtonContainer.addView(forward);
|
||||||
|
|
||||||
|
// Add the views to our toolbar
|
||||||
|
toolbar.addView(actionButtonContainer);
|
||||||
|
toolbar.addView(edittext);
|
||||||
|
toolbar.addView(close);
|
||||||
|
|
||||||
|
// Don't add the toolbar if its been disabled
|
||||||
|
if (getShowLocationBar()) {
|
||||||
|
// Add our toolbar to our main view/layout
|
||||||
|
main.addView(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our webview to our main view/layout
|
||||||
|
main.addView(inAppWebView);
|
||||||
|
|
||||||
|
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
|
||||||
|
lp.copyFrom(dialog.getWindow().getAttributes());
|
||||||
|
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||||
|
lp.height = WindowManager.LayoutParams.MATCH_PARENT;
|
||||||
|
|
||||||
|
dialog.setContentView(main);
|
||||||
|
dialog.show();
|
||||||
|
dialog.getWindow().setAttributes(lp);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.cordova.getActivity().runOnUiThread(runnable);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new plugin result and send it back to JavaScript
|
||||||
|
*
|
||||||
|
* @param obj a JSONObject contain event payload information
|
||||||
|
*/
|
||||||
|
private void sendUpdate(JSONObject obj, boolean keepCallback) {
|
||||||
|
PluginResult result = new PluginResult(PluginResult.Status.OK, obj);
|
||||||
|
result.setKeepCallback(keepCallback);
|
||||||
|
this.callbackContext.sendPluginResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The webview client receives notifications about appView
|
||||||
|
*/
|
||||||
|
public class InAppBrowserClient extends WebViewClient {
|
||||||
|
EditText edittext;
|
||||||
|
CordovaWebView webView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param mContext
|
||||||
|
* @param edittext
|
||||||
|
*/
|
||||||
|
public InAppBrowserClient(CordovaWebView webView, EditText mEditText) {
|
||||||
|
this.webView = webView;
|
||||||
|
this.edittext = mEditText;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the host application that a page has started loading.
|
||||||
|
*
|
||||||
|
* @param view The webview initiating the callback.
|
||||||
|
* @param url The url of the page.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
|
super.onPageStarted(view, url, favicon);
|
||||||
|
String newloc;
|
||||||
|
if (url.startsWith("http:") || url.startsWith("https:") || url.startsWith("file:")) {
|
||||||
|
newloc = url;
|
||||||
|
} else {
|
||||||
|
newloc = "http://" + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newloc.equals(edittext.getText().toString())) {
|
||||||
|
edittext.setText(newloc);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("type", LOAD_START_EVENT);
|
||||||
|
obj.put("url", newloc);
|
||||||
|
|
||||||
|
sendUpdate(obj, true);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.d(LOG_TAG, "Should never happen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
super.onPageFinished(view, url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
JSONObject obj = new JSONObject();
|
||||||
|
obj.put("type", LOAD_STOP_EVENT);
|
||||||
|
obj.put("url", url);
|
||||||
|
|
||||||
|
sendUpdate(obj, true);
|
||||||
|
} catch (JSONException ex) {
|
||||||
|
Log.d(LOG_TAG, "Should never happen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
474
framework/src/org/apache/cordova/NativeToJsMessageQueue.java
Executable file
@@ -0,0 +1,474 @@
|
|||||||
|
/*
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CordovaInterface;
|
||||||
|
import org.apache.cordova.api.PluginResult;
|
||||||
|
|
||||||
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds the list of messages to be sent to the WebView.
|
||||||
|
*/
|
||||||
|
public class NativeToJsMessageQueue {
|
||||||
|
private static final String LOG_TAG = "JsMessageQueue";
|
||||||
|
|
||||||
|
// This must match the default value in incubator-cordova-js/lib/android/exec.js
|
||||||
|
private static final int DEFAULT_BRIDGE_MODE = 2;
|
||||||
|
|
||||||
|
// Set this to true to force plugin results to be encoding as
|
||||||
|
// JS instead of the custom format (useful for benchmarking).
|
||||||
|
private static final boolean FORCE_ENCODE_USING_EVAL = false;
|
||||||
|
|
||||||
|
// Disable URL-based exec() bridge by default since it's a bit of a
|
||||||
|
// security concern.
|
||||||
|
static final boolean ENABLE_LOCATION_CHANGE_EXEC_MODE = false;
|
||||||
|
|
||||||
|
// Disable sending back native->JS messages during an exec() when the active
|
||||||
|
// exec() is asynchronous. Set this to true when running bridge benchmarks.
|
||||||
|
static final boolean DISABLE_EXEC_CHAINING = false;
|
||||||
|
|
||||||
|
// Upper limit for how much data to send to JS in one shot.
|
||||||
|
// TODO(agrieve): This is currently disable. It should be re-enabled once we
|
||||||
|
// remove support for returning values from exec() calls. This was
|
||||||
|
// deprecated in 2.2.0.
|
||||||
|
// Also, this currently only chops up on message boundaries. It may be useful
|
||||||
|
// to allow it to break up messages.
|
||||||
|
private static int MAX_PAYLOAD_SIZE = -1; //50 * 1024 * 10240;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The index into registeredListeners to treat as active.
|
||||||
|
*/
|
||||||
|
private int activeListenerIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When true, the active listener is not fired upon enqueue. When set to false,
|
||||||
|
* the active listener will be fired if the queue is non-empty.
|
||||||
|
*/
|
||||||
|
private boolean paused;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of JavaScript statements to be sent to JavaScript.
|
||||||
|
*/
|
||||||
|
private final LinkedList<JsMessage> queue = new LinkedList<JsMessage>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The array of listeners that can be used to send messages to JS.
|
||||||
|
*/
|
||||||
|
private final BridgeMode[] registeredListeners;
|
||||||
|
|
||||||
|
private final CordovaInterface cordova;
|
||||||
|
private final CordovaWebView webView;
|
||||||
|
|
||||||
|
public NativeToJsMessageQueue(CordovaWebView webView, CordovaInterface cordova) {
|
||||||
|
this.cordova = cordova;
|
||||||
|
this.webView = webView;
|
||||||
|
registeredListeners = new BridgeMode[4];
|
||||||
|
registeredListeners[0] = null; // Polling. Requires no logic.
|
||||||
|
registeredListeners[1] = new LoadUrlBridgeMode();
|
||||||
|
registeredListeners[2] = new OnlineEventsBridgeMode();
|
||||||
|
registeredListeners[3] = new PrivateApiBridgeMode();
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Changes the bridge mode.
|
||||||
|
*/
|
||||||
|
public void setBridgeMode(int value) {
|
||||||
|
if (value < 0 || value >= registeredListeners.length) {
|
||||||
|
Log.d(LOG_TAG, "Invalid NativeToJsBridgeMode: " + value);
|
||||||
|
} else {
|
||||||
|
if (value != activeListenerIndex) {
|
||||||
|
Log.d(LOG_TAG, "Set native->JS mode to " + value);
|
||||||
|
synchronized (this) {
|
||||||
|
activeListenerIndex = value;
|
||||||
|
BridgeMode activeListener = registeredListeners[value];
|
||||||
|
if (!paused && !queue.isEmpty() && activeListener != null) {
|
||||||
|
activeListener.onNativeToJsMessageAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all messages and resets to the default bridge mode.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
synchronized (this) {
|
||||||
|
queue.clear();
|
||||||
|
setBridgeMode(DEFAULT_BRIDGE_MODE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int calculatePackedMessageLength(JsMessage message) {
|
||||||
|
int messageLen = message.calculateEncodedLength();
|
||||||
|
String messageLenStr = String.valueOf(messageLen);
|
||||||
|
return messageLenStr.length() + messageLen + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void packMessage(JsMessage message, StringBuilder sb) {
|
||||||
|
sb.append(message.calculateEncodedLength())
|
||||||
|
.append(' ');
|
||||||
|
message.encodeAsMessage(sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines and returns queued messages combined into a single string.
|
||||||
|
* Combines as many messages as possible, while staying under MAX_PAYLOAD_SIZE.
|
||||||
|
* Returns null if the queue is empty.
|
||||||
|
*/
|
||||||
|
public String popAndEncode() {
|
||||||
|
synchronized (this) {
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int totalPayloadLen = 0;
|
||||||
|
int numMessagesToSend = 0;
|
||||||
|
for (JsMessage message : queue) {
|
||||||
|
int messageSize = calculatePackedMessageLength(message);
|
||||||
|
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
totalPayloadLen += messageSize;
|
||||||
|
numMessagesToSend += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder(totalPayloadLen);
|
||||||
|
for (int i = 0; i < numMessagesToSend; ++i) {
|
||||||
|
JsMessage message = queue.removeFirst();
|
||||||
|
packMessage(message, sb);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
// Attach a char to indicate that there are more messages pending.
|
||||||
|
sb.append('*');
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as popAndEncode(), except encodes in a form that can be executed as JS.
|
||||||
|
*/
|
||||||
|
private String popAndEncodeAsJs() {
|
||||||
|
synchronized (this) {
|
||||||
|
int length = queue.size();
|
||||||
|
if (length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int totalPayloadLen = 0;
|
||||||
|
int numMessagesToSend = 0;
|
||||||
|
for (JsMessage message : queue) {
|
||||||
|
int messageSize = message.calculateEncodedLength() + 50; // overestimate.
|
||||||
|
if (numMessagesToSend > 0 && totalPayloadLen + messageSize > MAX_PAYLOAD_SIZE && MAX_PAYLOAD_SIZE > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
totalPayloadLen += messageSize;
|
||||||
|
numMessagesToSend += 1;
|
||||||
|
}
|
||||||
|
boolean willSendAllMessages = numMessagesToSend == queue.size();
|
||||||
|
StringBuilder sb = new StringBuilder(totalPayloadLen + (willSendAllMessages ? 0 : 100));
|
||||||
|
// Wrap each statement in a try/finally so that if one throws it does
|
||||||
|
// not affect the next.
|
||||||
|
for (int i = 0; i < numMessagesToSend; ++i) {
|
||||||
|
JsMessage message = queue.removeFirst();
|
||||||
|
if (willSendAllMessages && (i + 1 == numMessagesToSend)) {
|
||||||
|
message.encodeAsJsMessage(sb);
|
||||||
|
} else {
|
||||||
|
sb.append("try{");
|
||||||
|
message.encodeAsJsMessage(sb);
|
||||||
|
sb.append("}finally{");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!willSendAllMessages) {
|
||||||
|
sb.append("window.setTimeout(function(){cordova.require('cordova/plugin/android/polling').pollOnce();},0);");
|
||||||
|
}
|
||||||
|
for (int i = willSendAllMessages ? 1 : 0; i < numMessagesToSend; ++i) {
|
||||||
|
sb.append('}');
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a JavaScript statement to the list.
|
||||||
|
*/
|
||||||
|
public void addJavaScript(String statement) {
|
||||||
|
enqueueMessage(new JsMessage(statement));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a JavaScript statement to the list.
|
||||||
|
*/
|
||||||
|
public void addPluginResult(PluginResult result, String callbackId) {
|
||||||
|
if (callbackId == null) {
|
||||||
|
Log.e(LOG_TAG, "Got plugin result with no callbackId", new Throwable());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Don't send anything if there is no result and there is no need to
|
||||||
|
// clear the callbacks.
|
||||||
|
boolean noResult = result.getStatus() == PluginResult.Status.NO_RESULT.ordinal();
|
||||||
|
boolean keepCallback = result.getKeepCallback();
|
||||||
|
if (noResult && keepCallback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JsMessage message = new JsMessage(result, callbackId);
|
||||||
|
if (FORCE_ENCODE_USING_EVAL) {
|
||||||
|
StringBuilder sb = new StringBuilder(message.calculateEncodedLength() + 50);
|
||||||
|
message.encodeAsJsMessage(sb);
|
||||||
|
message = new JsMessage(sb.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
enqueueMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enqueueMessage(JsMessage message) {
|
||||||
|
synchronized (this) {
|
||||||
|
queue.add(message);
|
||||||
|
if (!paused && registeredListeners[activeListenerIndex] != null) {
|
||||||
|
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPaused(boolean value) {
|
||||||
|
if (paused && value) {
|
||||||
|
// This should never happen. If a use-case for it comes up, we should
|
||||||
|
// change pause to be a counter.
|
||||||
|
Log.e(LOG_TAG, "nested call to setPaused detected.", new Throwable());
|
||||||
|
}
|
||||||
|
paused = value;
|
||||||
|
if (!value) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (!queue.isEmpty() && registeredListeners[activeListenerIndex] != null) {
|
||||||
|
registeredListeners[activeListenerIndex].onNativeToJsMessageAvailable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getPaused() {
|
||||||
|
return paused;
|
||||||
|
}
|
||||||
|
|
||||||
|
private interface BridgeMode {
|
||||||
|
void onNativeToJsMessageAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Uses webView.loadUrl("javascript:") to execute messages. */
|
||||||
|
private class LoadUrlBridgeMode implements BridgeMode {
|
||||||
|
final Runnable runnable = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
String js = popAndEncodeAsJs();
|
||||||
|
if (js != null) {
|
||||||
|
webView.loadUrlNow("javascript:" + js);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public void onNativeToJsMessageAvailable() {
|
||||||
|
cordova.getActivity().runOnUiThread(runnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Uses online/offline events to tell the JS when to poll for messages. */
|
||||||
|
private class OnlineEventsBridgeMode implements BridgeMode {
|
||||||
|
boolean online = true;
|
||||||
|
final Runnable runnable = new Runnable() {
|
||||||
|
public void run() {
|
||||||
|
if (!queue.isEmpty()) {
|
||||||
|
online = !online;
|
||||||
|
webView.setNetworkAvailable(online);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
OnlineEventsBridgeMode() {
|
||||||
|
webView.setNetworkAvailable(true);
|
||||||
|
}
|
||||||
|
public void onNativeToJsMessageAvailable() {
|
||||||
|
cordova.getActivity().runOnUiThread(runnable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses Java reflection to access an API that lets us eval JS.
|
||||||
|
* Requires Android 3.2.4 or above.
|
||||||
|
*/
|
||||||
|
private class PrivateApiBridgeMode implements BridgeMode {
|
||||||
|
// Message added in commit:
|
||||||
|
// http://omapzoom.org/?p=platform/frameworks/base.git;a=commitdiff;h=9497c5f8c4bc7c47789e5ccde01179abc31ffeb2
|
||||||
|
// Which first appeared in 3.2.4ish.
|
||||||
|
private static final int EXECUTE_JS = 194;
|
||||||
|
|
||||||
|
Method sendMessageMethod;
|
||||||
|
Object webViewCore;
|
||||||
|
boolean initFailed;
|
||||||
|
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
private void initReflection() {
|
||||||
|
Object webViewObject = webView;
|
||||||
|
Class webViewClass = WebView.class;
|
||||||
|
try {
|
||||||
|
Field f = webViewClass.getDeclaredField("mProvider");
|
||||||
|
f.setAccessible(true);
|
||||||
|
webViewObject = f.get(webView);
|
||||||
|
webViewClass = webViewObject.getClass();
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// mProvider is only required on newer Android releases.
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field f = webViewClass.getDeclaredField("mWebViewCore");
|
||||||
|
f.setAccessible(true);
|
||||||
|
webViewCore = f.get(webViewObject);
|
||||||
|
|
||||||
|
if (webViewCore != null) {
|
||||||
|
sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class);
|
||||||
|
sendMessageMethod.setAccessible(true);
|
||||||
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
initFailed = true;
|
||||||
|
Log.e(LOG_TAG, "PrivateApiBridgeMode failed to find the expected APIs.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onNativeToJsMessageAvailable() {
|
||||||
|
if (sendMessageMethod == null && !initFailed) {
|
||||||
|
initReflection();
|
||||||
|
}
|
||||||
|
// webViewCore is lazily initialized, and so may not be available right away.
|
||||||
|
if (sendMessageMethod != null) {
|
||||||
|
String js = popAndEncodeAsJs();
|
||||||
|
Message execJsMessage = Message.obtain(null, EXECUTE_JS, js);
|
||||||
|
try {
|
||||||
|
sendMessageMethod.invoke(webViewCore, execJsMessage);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Log.e(LOG_TAG, "Reflection message bridge failed.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static class JsMessage {
|
||||||
|
final String jsPayloadOrCallbackId;
|
||||||
|
final PluginResult pluginResult;
|
||||||
|
JsMessage(String js) {
|
||||||
|
if (js == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
jsPayloadOrCallbackId = js;
|
||||||
|
pluginResult = null;
|
||||||
|
}
|
||||||
|
JsMessage(PluginResult pluginResult, String callbackId) {
|
||||||
|
if (callbackId == null || pluginResult == null) {
|
||||||
|
throw new NullPointerException();
|
||||||
|
}
|
||||||
|
jsPayloadOrCallbackId = callbackId;
|
||||||
|
this.pluginResult = pluginResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
int calculateEncodedLength() {
|
||||||
|
if (pluginResult == null) {
|
||||||
|
return jsPayloadOrCallbackId.length() + 1;
|
||||||
|
}
|
||||||
|
int statusLen = String.valueOf(pluginResult.getStatus()).length();
|
||||||
|
int ret = 2 + statusLen + 1 + jsPayloadOrCallbackId.length() + 1;
|
||||||
|
switch (pluginResult.getMessageType()) {
|
||||||
|
case PluginResult.MESSAGE_TYPE_BOOLEAN: // f or t
|
||||||
|
case PluginResult.MESSAGE_TYPE_NULL: // N
|
||||||
|
ret += 1;
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_NUMBER: // n
|
||||||
|
ret += 1 + pluginResult.getMessage().length();
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||||
|
ret += 1 + pluginResult.getStrMessage().length();
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_JSON:
|
||||||
|
default:
|
||||||
|
ret += pluginResult.getMessage().length();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodeAsMessage(StringBuilder sb) {
|
||||||
|
if (pluginResult == null) {
|
||||||
|
sb.append('J')
|
||||||
|
.append(jsPayloadOrCallbackId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int status = pluginResult.getStatus();
|
||||||
|
boolean noResult = status == PluginResult.Status.NO_RESULT.ordinal();
|
||||||
|
boolean resultOk = status == PluginResult.Status.OK.ordinal();
|
||||||
|
boolean keepCallback = pluginResult.getKeepCallback();
|
||||||
|
|
||||||
|
sb.append((noResult || resultOk) ? 'S' : 'F')
|
||||||
|
.append(keepCallback ? '1' : '0')
|
||||||
|
.append(status)
|
||||||
|
.append(' ')
|
||||||
|
.append(jsPayloadOrCallbackId)
|
||||||
|
.append(' ');
|
||||||
|
switch (pluginResult.getMessageType()) {
|
||||||
|
case PluginResult.MESSAGE_TYPE_BOOLEAN:
|
||||||
|
sb.append(pluginResult.getMessage().charAt(0)); // t or f.
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_NULL: // N
|
||||||
|
sb.append('N');
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_NUMBER: // n
|
||||||
|
sb.append('n')
|
||||||
|
.append(pluginResult.getMessage());
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_STRING: // s
|
||||||
|
sb.append('s');
|
||||||
|
sb.append(pluginResult.getStrMessage());
|
||||||
|
break;
|
||||||
|
case PluginResult.MESSAGE_TYPE_JSON:
|
||||||
|
default:
|
||||||
|
sb.append(pluginResult.getMessage()); // [ or {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void encodeAsJsMessage(StringBuilder sb) {
|
||||||
|
if (pluginResult == null) {
|
||||||
|
sb.append(jsPayloadOrCallbackId);
|
||||||
|
} else {
|
||||||
|
int status = pluginResult.getStatus();
|
||||||
|
boolean success = (status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal());
|
||||||
|
sb.append("cordova.callbackFromNative('")
|
||||||
|
.append(jsPayloadOrCallbackId)
|
||||||
|
.append("',")
|
||||||
|
.append(success)
|
||||||
|
.append(",")
|
||||||
|
.append(status)
|
||||||
|
.append(",")
|
||||||
|
.append(pluginResult.getMessage())
|
||||||
|
.append(",")
|
||||||
|
.append(pluginResult.getKeepCallback())
|
||||||
|
.append(");");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,8 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
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.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ import android.net.ConnectivityManager;
|
|||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class NetworkManager extends Plugin {
|
public class NetworkManager extends CordovaPlugin {
|
||||||
|
|
||||||
public static int NOT_REACHABLE = 0;
|
public static int NOT_REACHABLE = 0;
|
||||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||||
@@ -68,10 +69,12 @@ public class NetworkManager extends Plugin {
|
|||||||
|
|
||||||
private static final String LOG_TAG = "NetworkManager";
|
private static final String LOG_TAG = "NetworkManager";
|
||||||
|
|
||||||
private String connectionCallbackId;
|
private CallbackContext connectionCallbackContext;
|
||||||
|
private boolean registered = false;
|
||||||
|
|
||||||
ConnectivityManager sockMan;
|
ConnectivityManager sockMan;
|
||||||
BroadcastReceiver receiver;
|
BroadcastReceiver receiver;
|
||||||
|
private String lastStatus = "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
@@ -84,25 +87,28 @@ public class NetworkManager extends Plugin {
|
|||||||
* Sets the context of the Command. This can then be used to do things like
|
* Sets the context of the Command. This can then be used to do things like
|
||||||
* get file paths associated with the Activity.
|
* 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) {
|
public void initialize(CordovaInterface cordova, CordovaWebView webView) {
|
||||||
super.setContext(ctx);
|
super.initialize(cordova, webView);
|
||||||
this.sockMan = (ConnectivityManager) ctx.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
|
this.sockMan = (ConnectivityManager) cordova.getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||||
this.connectionCallbackId = null;
|
this.connectionCallbackContext = null;
|
||||||
|
|
||||||
// We need to listen to connectivity events to update navigator.connection
|
// We need to listen to connectivity events to update navigator.connection
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
IntentFilter intentFilter = new IntentFilter();
|
||||||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||||
if (this.receiver == null) {
|
if (this.receiver == null) {
|
||||||
this.receiver = new BroadcastReceiver() {
|
this.receiver = new BroadcastReceiver() {
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
// (The null check is for the ARM Emulator, please use Intel Emulator for better results)
|
||||||
|
if(NetworkManager.this.webView != null)
|
||||||
|
updateConnectionInfo(sockMan.getActiveNetworkInfo());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ctx.getActivity().registerReceiver(this.receiver, intentFilter);
|
cordova.getActivity().registerReceiver(this.receiver, intentFilter);
|
||||||
|
this.registered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -110,43 +116,31 @@ public class NetworkManager extends Plugin {
|
|||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArry of arguments for the plugin.
|
||||||
* @param callbackId The callback id used when calling back into JavaScript.
|
* @param callbackContext The callback id used when calling back into JavaScript.
|
||||||
* @return A PluginResult object with a status and message.
|
* @return True if the action was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
|
|
||||||
String result = "Unsupported Operation: " + action;
|
|
||||||
|
|
||||||
if (action.equals("getConnectionInfo")) {
|
if (action.equals("getConnectionInfo")) {
|
||||||
this.connectionCallbackId = callbackId;
|
this.connectionCallbackContext = callbackContext;
|
||||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
|
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
|
||||||
pluginResult.setKeepCallback(true);
|
pluginResult.setKeepCallback(true);
|
||||||
return pluginResult;
|
callbackContext.sendPluginResult(pluginResult);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop network receiver.
|
* Stop network receiver.
|
||||||
*/
|
*/
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
if (this.receiver != null) {
|
if (this.receiver != null && this.registered) {
|
||||||
try {
|
try {
|
||||||
this.ctx.getActivity().unregisterReceiver(this.receiver);
|
this.cordova.getActivity().unregisterReceiver(this.receiver);
|
||||||
|
this.registered = false;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -165,7 +159,14 @@ public class NetworkManager extends Plugin {
|
|||||||
*/
|
*/
|
||||||
private void updateConnectionInfo(NetworkInfo info) {
|
private void updateConnectionInfo(NetworkInfo info) {
|
||||||
// send update to javascript "navigator.network.connection"
|
// send update to javascript "navigator.network.connection"
|
||||||
sendUpdate(this.getConnectionInfo(info));
|
// Jellybean sends its own info
|
||||||
|
String thisStatus = this.getConnectionInfo(info);
|
||||||
|
if(!thisStatus.equals(lastStatus))
|
||||||
|
{
|
||||||
|
sendUpdate(thisStatus);
|
||||||
|
lastStatus = thisStatus;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -185,6 +186,7 @@ public class NetworkManager extends Plugin {
|
|||||||
type = getType(info);
|
type = getType(info);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log.d("CordovaNetworkManager", "Connection Type: " + type);
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,11 +196,11 @@ public class NetworkManager extends Plugin {
|
|||||||
* @param connection the network info to set as navigator.connection
|
* @param connection the network info to set as navigator.connection
|
||||||
*/
|
*/
|
||||||
private void sendUpdate(String type) {
|
private void sendUpdate(String type) {
|
||||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
if (connectionCallbackContext != null) {
|
||||||
result.setKeepCallback(true);
|
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||||
this.success(result, this.connectionCallbackId);
|
result.setKeepCallback(true);
|
||||||
|
connectionCallbackContext.sendPluginResult(result);
|
||||||
// Send to all plugins
|
}
|
||||||
webView.postMessage("networkconnection", type);
|
webView.postMessage("networkconnection", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
|
import org.apache.cordova.api.CallbackContext;
|
||||||
import org.apache.cordova.api.CordovaInterface;
|
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.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -35,7 +36,7 @@ import android.os.Vibrator;
|
|||||||
/**
|
/**
|
||||||
* This class provides access to notifications on the device.
|
* This class provides access to notifications on the device.
|
||||||
*/
|
*/
|
||||||
public class Notification extends Plugin {
|
public class Notification extends CordovaPlugin {
|
||||||
|
|
||||||
public int confirmResult = -1;
|
public int confirmResult = -1;
|
||||||
public ProgressDialog spinnerDialog = null;
|
public ProgressDialog spinnerDialog = null;
|
||||||
@@ -49,87 +50,49 @@ public class Notification extends Plugin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
*
|
*
|
||||||
* @param action The action to execute.
|
* @param action The action to execute.
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* @param args JSONArray 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.
|
* @return True when the action was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
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;
|
if (action.equals("beep")) {
|
||||||
String result = "";
|
this.beep(args.getLong(0));
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
else if (action.equals("vibrate")) {
|
||||||
|
this.vibrate(args.getLong(0));
|
||||||
/**
|
}
|
||||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
else if (action.equals("alert")) {
|
||||||
*
|
this.alert(args.getString(0), args.getString(1), args.getString(2), callbackContext);
|
||||||
* @param action The action to execute
|
|
||||||
* @return T=returns value
|
|
||||||
*/
|
|
||||||
public boolean isSynch(String action) {
|
|
||||||
if (action.equals("alert")) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (action.equals("confirm")) {
|
else if (action.equals("confirm")) {
|
||||||
|
this.confirm(args.getString(0), args.getString(1), args.getString(2), callbackContext);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (action.equals("activityStart")) {
|
else if (action.equals("activityStart")) {
|
||||||
return true;
|
this.activityStart(args.getString(0), args.getString(1));
|
||||||
}
|
}
|
||||||
else if (action.equals("activityStop")) {
|
else if (action.equals("activityStop")) {
|
||||||
return true;
|
this.activityStop();
|
||||||
}
|
}
|
||||||
else if (action.equals("progressStart")) {
|
else if (action.equals("progressStart")) {
|
||||||
return true;
|
this.progressStart(args.getString(0), args.getString(1));
|
||||||
}
|
}
|
||||||
else if (action.equals("progressValue")) {
|
else if (action.equals("progressValue")) {
|
||||||
return true;
|
this.progressValue(args.getInt(0));
|
||||||
}
|
}
|
||||||
else if (action.equals("progressStop")) {
|
else if (action.equals("progressStop")) {
|
||||||
return true;
|
this.progressStop();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return false;
|
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.
|
* Beep plays the default notification ringtone.
|
||||||
*
|
*
|
||||||
* @param count Number of times to play notification
|
* @param count Number of times to play notification
|
||||||
*/
|
*/
|
||||||
public void beep(long count) {
|
public void beep(long count) {
|
||||||
Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
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 phone is not set to silent mode
|
||||||
if (notification != null) {
|
if (notification != null) {
|
||||||
@@ -163,7 +126,7 @@ public class Notification extends Plugin {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Vibrates the device for the specified amount of time.
|
* Vibrates the device for the specified amount of time.
|
||||||
*
|
*
|
||||||
* @param time Time to vibrate in ms.
|
* @param time Time to vibrate in ms.
|
||||||
*/
|
*/
|
||||||
public void vibrate(long time) {
|
public void vibrate(long time) {
|
||||||
@@ -171,65 +134,71 @@ public class Notification extends Plugin {
|
|||||||
if (time == 0) {
|
if (time == 0) {
|
||||||
time = 500;
|
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);
|
vibrator.vibrate(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and shows a native Android alert with given Strings
|
* Builds and shows a native Android alert with given Strings
|
||||||
* @param message The message the alert should display
|
* @param message The message the alert should display
|
||||||
* @param title The title of the alert
|
* @param title The title of the alert
|
||||||
* @param buttonLabel The label of the button
|
* @param buttonLabel The label of the button
|
||||||
* @param callbackId The callback id
|
* @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 CordovaInterface cordova = this.cordova;
|
||||||
final Notification notification = this;
|
|
||||||
|
|
||||||
Runnable runnable = new Runnable() {
|
Runnable runnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
AlertDialog.Builder dlg = new AlertDialog.Builder(ctx.getActivity());
|
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
|
||||||
dlg.setMessage(message);
|
dlg.setMessage(message);
|
||||||
dlg.setTitle(title);
|
dlg.setTitle(title);
|
||||||
dlg.setCancelable(false);
|
dlg.setCancelable(true);
|
||||||
dlg.setPositiveButton(buttonLabel,
|
dlg.setPositiveButton(buttonLabel,
|
||||||
new AlertDialog.OnClickListener() {
|
new AlertDialog.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
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.create();
|
||||||
dlg.show();
|
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.
|
* 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.
|
* 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.
|
* 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 message The message the dialog should display
|
||||||
* @param title The title of the dialog
|
* @param title The title of the dialog
|
||||||
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
||||||
* @param callbackId The callback id
|
* @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 CordovaInterface cordova = this.cordova;
|
||||||
final Notification notification = this;
|
|
||||||
final String[] fButtons = buttonLabels.split(",");
|
final String[] fButtons = buttonLabels.split(",");
|
||||||
|
|
||||||
Runnable runnable = new Runnable() {
|
Runnable runnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
AlertDialog.Builder dlg = new AlertDialog.Builder(ctx.getActivity());
|
AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
|
||||||
dlg.setMessage(message);
|
dlg.setMessage(message);
|
||||||
dlg.setTitle(title);
|
dlg.setTitle(title);
|
||||||
dlg.setCancelable(false);
|
dlg.setCancelable(true);
|
||||||
|
|
||||||
// First button
|
// First button
|
||||||
if (fButtons.length > 0) {
|
if (fButtons.length > 0) {
|
||||||
@@ -237,7 +206,7 @@ public class Notification extends Plugin {
|
|||||||
new AlertDialog.OnClickListener() {
|
new AlertDialog.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
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() {
|
new AlertDialog.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
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() {
|
new AlertDialog.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
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.create();
|
||||||
dlg.show();
|
dlg.show();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
this.ctx.getActivity().runOnUiThread(runnable);
|
this.cordova.getActivity().runOnUiThread(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the spinner.
|
* Show the spinner.
|
||||||
*
|
*
|
||||||
* @param title Title of the dialog
|
* @param title Title of the dialog
|
||||||
* @param message The message of the dialog
|
* @param message The message of the dialog
|
||||||
*/
|
*/
|
||||||
@@ -283,19 +259,18 @@ public class Notification extends Plugin {
|
|||||||
this.spinnerDialog.dismiss();
|
this.spinnerDialog.dismiss();
|
||||||
this.spinnerDialog = null;
|
this.spinnerDialog = null;
|
||||||
}
|
}
|
||||||
final Notification notification = this;
|
final CordovaInterface cordova = this.cordova;
|
||||||
final CordovaInterface ctx = this.ctx;
|
|
||||||
Runnable runnable = new Runnable() {
|
Runnable runnable = new Runnable() {
|
||||||
public void run() {
|
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() {
|
new DialogInterface.OnCancelListener() {
|
||||||
public void onCancel(DialogInterface dialog) {
|
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.
|
* Show the progress dialog.
|
||||||
*
|
*
|
||||||
* @param title Title of the dialog
|
* @param title Title of the dialog
|
||||||
* @param message The message of the dialog
|
* @param message The message of the dialog
|
||||||
*/
|
*/
|
||||||
@@ -320,10 +295,10 @@ public class Notification extends Plugin {
|
|||||||
this.progressDialog = null;
|
this.progressDialog = null;
|
||||||
}
|
}
|
||||||
final Notification notification = this;
|
final Notification notification = this;
|
||||||
final CordovaInterface ctx = this.ctx;
|
final CordovaInterface cordova = this.cordova;
|
||||||
Runnable runnable = new Runnable() {
|
Runnable runnable = new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
notification.progressDialog = new ProgressDialog(ctx.getActivity());
|
notification.progressDialog = new ProgressDialog(cordova.getActivity());
|
||||||
notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||||
notification.progressDialog.setTitle(title);
|
notification.progressDialog.setTitle(title);
|
||||||
notification.progressDialog.setMessage(message);
|
notification.progressDialog.setMessage(message);
|
||||||
@@ -339,12 +314,12 @@ public class Notification extends Plugin {
|
|||||||
notification.progressDialog.show();
|
notification.progressDialog.show();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.ctx.getActivity().runOnUiThread(runnable);
|
this.cordova.getActivity().runOnUiThread(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set value of progress bar.
|
* Set value of progress bar.
|
||||||
*
|
*
|
||||||
* @param value 0-100
|
* @param value 0-100
|
||||||
*/
|
*/
|
||||||
public synchronized void progressValue(int value) {
|
public synchronized void progressValue(int value) {
|
||||||
|
|||||||
@@ -19,25 +19,25 @@
|
|||||||
|
|
||||||
package org.apache.cordova;
|
package org.apache.cordova;
|
||||||
|
|
||||||
import org.apache.cordova.api.Plugin;
|
import org.apache.cordova.api.CallbackContext;
|
||||||
import org.apache.cordova.api.PluginResult;
|
import org.apache.cordova.api.CordovaPlugin;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
|
||||||
public class SplashScreen extends Plugin {
|
public class SplashScreen extends CordovaPlugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) {
|
||||||
PluginResult.Status status = PluginResult.Status.OK;
|
|
||||||
String result = "";
|
|
||||||
|
|
||||||
if (action.equals("hide")) {
|
if (action.equals("hide")) {
|
||||||
//((DroidGap)this.ctx).removeSplashScreen();
|
|
||||||
this.webView.postMessage("splashscreen", "hide");
|
this.webView.postMessage("splashscreen", "hide");
|
||||||
|
} else if (action.equals("show")){
|
||||||
|
this.webView.postMessage("splashscreen", "show");
|
||||||
}
|
}
|
||||||
else {
|
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 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.apache.cordova.api.PluginResult;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
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
|
* Android 3.0 devices. It is not used for other versions of Android, since
|
||||||
* HTML5 database is built in to the browser.
|
* HTML5 database is built in to the browser.
|
||||||
*/
|
*/
|
||||||
public class Storage extends Plugin {
|
public class Storage extends CordovaPlugin {
|
||||||
|
|
||||||
// Data Definition Language
|
// Data Definition Language
|
||||||
private static final String ALTER = "alter";
|
private static final String ALTER = "alter";
|
||||||
@@ -60,47 +61,32 @@ public class Storage extends Plugin {
|
|||||||
* The action to execute.
|
* The action to execute.
|
||||||
* @param args
|
* @param args
|
||||||
* JSONArry of arguments for the plugin.
|
* JSONArry of arguments for the plugin.
|
||||||
* @param callbackId
|
* @param callbackContext
|
||||||
* The callback id used when calling back into JavaScript.
|
* The callback context used when calling back into JavaScript.
|
||||||
* @return A PluginResult object with a status and message.
|
* @return True if the action was valid, false otherwise.
|
||||||
*/
|
*/
|
||||||
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;
|
if (action.equals("openDatabase")) {
|
||||||
String result = "";
|
this.openDatabase(args.getString(0), args.getString(1),
|
||||||
|
args.getString(2), args.getLong(3));
|
||||||
try {
|
} else if (action.equals("executeSql")) {
|
||||||
if (action.equals("openDatabase")) {
|
String[] s = null;
|
||||||
this.openDatabase(args.getString(0), args.getString(1),
|
if (args.isNull(1)) {
|
||||||
args.getString(2), args.getLong(3));
|
s = new String[0];
|
||||||
} else if (action.equals("executeSql")) {
|
} else {
|
||||||
String[] s = null;
|
JSONArray a = args.getJSONArray(1);
|
||||||
if (args.isNull(1)) {
|
int len = a.length();
|
||||||
s = new String[0];
|
s = new String[len];
|
||||||
} else {
|
for (int i = 0; i < len; i++) {
|
||||||
JSONArray a = args.getJSONArray(1);
|
s[i] = a.getString(i);
|
||||||
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);
|
this.executeSql(args.getString(0), s, args.getString(2));
|
||||||
} catch (JSONException e) {
|
|
||||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
|
return false;
|
||||||
/**
|
}
|
||||||
* Identifies if action to be executed returns a value and should be run
|
callbackContext.success();
|
||||||
* synchronously.
|
|
||||||
*
|
|
||||||
* @param action
|
|
||||||
* The action to execute
|
|
||||||
* @return T=returns value
|
|
||||||
*/
|
|
||||||
public boolean isSynch(String action) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +101,13 @@ public class Storage extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up on navigation/refresh.
|
||||||
|
*/
|
||||||
|
public void onReset() {
|
||||||
|
this.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
// LOCAL METHODS
|
// LOCAL METHODS
|
||||||
// --------------------------------------------------------------------------
|
// --------------------------------------------------------------------------
|
||||||
@@ -141,10 +134,23 @@ public class Storage extends Plugin {
|
|||||||
|
|
||||||
// If no database path, generate from application package
|
// If no database path, generate from application package
|
||||||
if (this.path == null) {
|
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);
|
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,7 +168,7 @@ public class Storage extends Plugin {
|
|||||||
try {
|
try {
|
||||||
if (isDDL(query)) {
|
if (isDDL(query)) {
|
||||||
this.myDb.execSQL(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 {
|
else {
|
||||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||||
@@ -175,12 +181,12 @@ public class Storage extends Plugin {
|
|||||||
System.out.println("Storage.executeSql(): Error=" + ex.getMessage());
|
System.out.println("Storage.executeSql(): Error=" + ex.getMessage());
|
||||||
|
|
||||||
// Send error message back to JavaScript
|
// 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
|
* @param query to be executed
|
||||||
* @return true if it is a DDL command, false otherwise
|
* @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
|
// 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;
|
package org.apache.cordova.api;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Cordova activity abstract class that is extended by DroidGap.
|
* The Cordova activity abstract class that is extended by DroidGap.
|
||||||
* It is used to isolate plugin development, and remove dependency on entire Cordova library.
|
* 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 intent The intent to start
|
||||||
* @param requestCode The request code that is passed to callback to identify the activity
|
* @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.
|
* Set the plugin to be called when a sub-activity exits.
|
||||||
*
|
*
|
||||||
* @param plugin The plugin on which onActivityResult is to be called
|
* @param plugin The plugin on which onActivityResult is to be called
|
||||||
*/
|
*/
|
||||||
abstract public void setActivityResultCallback(IPlugin plugin);
|
abstract public void setActivityResultCallback(CordovaPlugin 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();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Android activity.
|
* Get the Android activity.
|
||||||
@@ -64,9 +53,7 @@ public interface CordovaInterface {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public abstract Activity getActivity();
|
public abstract Activity getActivity();
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public abstract void cancelLoadUrl();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a message is sent to plugin.
|
* Called when a message is sent to plugin.
|
||||||
@@ -76,4 +63,9 @@ public interface CordovaInterface {
|
|||||||
* @return Object or null
|
* @return Object or null
|
||||||
*/
|
*/
|
||||||
public Object onMessage(String id, Object data);
|
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.apache.cordova.CordovaWebView;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
//import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin interface must be implemented by any plugin classes.
|
* Plugins must extend this class and override one of the execute methods.
|
||||||
*
|
|
||||||
* The execute method is called by the PluginManager.
|
|
||||||
*/
|
*/
|
||||||
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.
|
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||||
* @param args JSONArry of arguments for the plugin.
|
* cordova.getThreadPool().execute(runnable);
|
||||||
* @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.
|
|
||||||
*
|
*
|
||||||
* @param action The action to execute
|
* To run on the UI thread, use:
|
||||||
* @return T=returns value
|
* 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
|
* Executes the request.
|
||||||
* get file paths associated with the Activity.
|
*
|
||||||
*
|
* This method is called from the WebView thread. To do a non-trivial amount of work, use:
|
||||||
* @param ctx The context of the main Activity.
|
* 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);
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
|
return false;
|
||||||
/**
|
}
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the system is about to start resuming a previous activity.
|
* Called when the system is about to start resuming a previous activity.
|
||||||
*
|
*
|
||||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
* @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.
|
* Called when the activity will start interacting with the user.
|
||||||
*
|
*
|
||||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
* @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.
|
* 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.
|
* The final call you receive before your activity is destroyed.
|
||||||
*/
|
*/
|
||||||
void onDestroy();
|
public void onDestroy() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a message is sent to plugin.
|
* Called when a message is sent to plugin.
|
||||||
@@ -96,7 +114,9 @@ public interface IPlugin {
|
|||||||
* @param data The message data
|
* @param data The message data
|
||||||
* @return Object to stop propagation or null
|
* @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,
|
* 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 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").
|
* @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.
|
* 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.
|
* @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.
|
* @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() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||