mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
580 Commits
accel_hack
...
1.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7bb34dea14 | ||
|
|
004453b03f | ||
|
|
a1edf92fa4 | ||
|
|
e28458869f | ||
|
|
229e15f685 | ||
|
|
39d6952494 | ||
|
|
b864a8a6c6 | ||
|
|
f5dac1451d | ||
|
|
2bf765897b | ||
|
|
411288b051 | ||
|
|
1c97467e39 | ||
|
|
7c39edfcd0 | ||
|
|
9f673db79f | ||
|
|
8c807315e9 | ||
|
|
be48b576d9 | ||
|
|
648e56cb67 | ||
|
|
b37defd3cf | ||
|
|
c093881f54 | ||
|
|
e09c728fd0 | ||
|
|
d098b6133e | ||
|
|
029ddeade0 | ||
|
|
17ccb2db4d | ||
|
|
6b84ead393 | ||
|
|
9bf3a82964 | ||
|
|
7afa2d3840 | ||
|
|
e789349c23 | ||
|
|
f7ebc98375 | ||
|
|
2787a960d8 | ||
|
|
4af8952dc3 | ||
|
|
701717fd55 | ||
|
|
3de2084af2 | ||
|
|
6d532c9fe5 | ||
|
|
eb0e0d9d11 | ||
|
|
53410781e4 | ||
|
|
2bee9a8442 | ||
|
|
7c9eca1fab | ||
|
|
017fa1b917 | ||
|
|
24bb836221 | ||
|
|
6993c7edd1 | ||
|
|
4cc3772e84 | ||
|
|
2a3c387ce3 | ||
|
|
c80ddc1b22 | ||
|
|
e1092590d6 | ||
|
|
24f979394f | ||
|
|
9971d61601 | ||
|
|
6c65a6a016 | ||
|
|
37b9cc47bd | ||
|
|
66f7afbed2 | ||
|
|
b1c0be3dd6 | ||
|
|
60be45b9d9 | ||
|
|
393bd5a1f5 | ||
|
|
1de036a744 | ||
|
|
a235513991 | ||
|
|
22a9cabeb9 | ||
|
|
5de4ae7554 | ||
|
|
f19d8f9bba | ||
|
|
d4ccc702b2 | ||
|
|
a0c748620a | ||
|
|
b85a769372 | ||
|
|
dd52081deb | ||
|
|
ebd92a4b12 | ||
|
|
0a7a77e77b | ||
|
|
9d1e73656f | ||
|
|
6e62a76564 | ||
|
|
3895570edd | ||
|
|
28b01fe494 | ||
|
|
709eacd9dc | ||
|
|
090890b22a | ||
|
|
ce9d577415 | ||
|
|
cfc9631873 | ||
|
|
3bf48f82af | ||
|
|
f9bcf71a7a | ||
|
|
9d5aa9406c | ||
|
|
0b1e760fc1 | ||
|
|
941b64f6a2 | ||
|
|
7cc1cc4a3f | ||
|
|
c98b758e94 | ||
|
|
102d37d48a | ||
|
|
4b647fc58d | ||
|
|
b7156c6803 | ||
|
|
b8cc36e805 | ||
|
|
76b2df208e | ||
|
|
9643314553 | ||
|
|
1e3422ae70 | ||
|
|
fc1bea4947 | ||
|
|
afb48e52b6 | ||
|
|
b5dc62d9ef | ||
|
|
2a786044de | ||
|
|
f3d7ce8fc3 | ||
|
|
85dab52cf7 | ||
|
|
c96c9b00b9 | ||
|
|
b059a31787 | ||
|
|
8cb71673c2 | ||
|
|
5c8913351c | ||
|
|
8ef93ff0e5 | ||
|
|
042c34192b | ||
|
|
ac2e92321f | ||
|
|
05eacf4792 | ||
|
|
435c903baf | ||
|
|
53de070a41 | ||
|
|
54fdcbfd46 | ||
|
|
44aa0aeb0f | ||
|
|
7d53eb8e3e | ||
|
|
7bc0d624ac | ||
|
|
a5039f021d | ||
|
|
80e66d87a9 | ||
|
|
d35e8cd44b | ||
|
|
eb3b1f91d4 | ||
|
|
8a1ab69235 | ||
|
|
66f3018767 | ||
|
|
ff7de25b62 | ||
|
|
85eb6e4997 | ||
|
|
0280d5dd82 | ||
|
|
3c90a9a23c | ||
|
|
088c342198 | ||
|
|
af18a8e1aa | ||
|
|
11a29e11e1 | ||
|
|
226e4dddcd | ||
|
|
08c44f5c5d | ||
|
|
66dbd9ef8b | ||
|
|
1c3ea54dcb | ||
|
|
a65638ab59 | ||
|
|
1a9471a2e0 | ||
|
|
c15971253f | ||
|
|
a67dfdb75e | ||
|
|
9b52827744 | ||
|
|
c5b268756b | ||
|
|
afa85a74b3 | ||
|
|
517b5e0db9 | ||
|
|
327bda49a0 | ||
|
|
c978341d83 | ||
|
|
939754e1c9 | ||
|
|
5e733ede9f | ||
|
|
d1448e9073 | ||
|
|
e5fb0c0e71 | ||
|
|
33bfb44f67 | ||
|
|
a89c8bf482 | ||
|
|
b46cbfd673 | ||
|
|
a67aeed571 | ||
|
|
fbb6b22de6 | ||
|
|
315b5a59b3 | ||
|
|
8da5ad8eca | ||
|
|
740e50ce61 | ||
|
|
3ce0fc4897 | ||
|
|
8d1722ad67 | ||
|
|
7d41646a35 | ||
|
|
992ee0bca4 | ||
|
|
d00a9f33cd | ||
|
|
431c80782e | ||
|
|
39ec9c095d | ||
|
|
10e1808c56 | ||
|
|
9036eb8fcc | ||
|
|
4280fdf252 | ||
|
|
ca5e141b5b | ||
|
|
8f7a5decb6 | ||
|
|
1d79b6617b | ||
|
|
6c3eefe6f9 | ||
|
|
2177cd0a39 | ||
|
|
6618015151 | ||
|
|
e99f75d59b | ||
|
|
ab8cfe01d0 | ||
|
|
e84c59d23c | ||
|
|
e81fc239a7 | ||
|
|
839c577243 | ||
|
|
116169a4c5 | ||
|
|
346ed60f0d | ||
|
|
bde59adc04 | ||
|
|
ffbc010d7b | ||
|
|
bdadbbc339 | ||
|
|
b94eedaf07 | ||
|
|
58ecac335b | ||
|
|
fd8bb2f671 | ||
|
|
0aacfbdd50 | ||
|
|
2cd116e4e7 | ||
|
|
673a8871df | ||
|
|
44945f9d5e | ||
|
|
887f754014 | ||
|
|
674015460f | ||
|
|
40084293c3 | ||
|
|
ed4c57e791 | ||
|
|
bf164f4161 | ||
|
|
626119ae3b | ||
|
|
307f9d1871 | ||
|
|
e766188689 | ||
|
|
d74569ffa7 | ||
|
|
d424af03e4 | ||
|
|
f6f80537c3 | ||
|
|
908485751b | ||
|
|
b850d225f4 | ||
|
|
010c774988 | ||
|
|
969f0c87d7 | ||
|
|
b3e9794189 | ||
|
|
935295c9b8 | ||
|
|
04de2052fd | ||
|
|
60eb60b4f5 | ||
|
|
ec307fdda8 | ||
|
|
7344964c05 | ||
|
|
1fc56921aa | ||
|
|
21a34a8980 | ||
|
|
8d73b364f2 | ||
|
|
fb2c25c6c6 | ||
|
|
47ca081f36 | ||
|
|
939b70243d | ||
|
|
d44bb7a9d8 | ||
|
|
dccc29daf2 | ||
|
|
b402efd1f7 | ||
|
|
0c3a8fb9f7 | ||
|
|
64d4337d5f | ||
|
|
a9e1751812 | ||
|
|
2bc7bd6768 | ||
|
|
1711fb07d7 | ||
|
|
6f4673f590 | ||
|
|
5e858f8bc3 | ||
|
|
691b093ccd | ||
|
|
99002f9dce | ||
|
|
b07072c12b | ||
|
|
36dd964ba4 | ||
|
|
f848527c28 | ||
|
|
6aa055f46e | ||
|
|
7952668cf7 | ||
|
|
a0c761664d | ||
|
|
9fd9cf55cf | ||
|
|
3c9089b9c7 | ||
|
|
f220489543 | ||
|
|
b65f9517db | ||
|
|
1a0de5f626 | ||
|
|
3c0bef6cc1 | ||
|
|
040194157f | ||
|
|
7ebf8130e4 | ||
|
|
64310dc85c | ||
|
|
cd2e86af2f | ||
|
|
b353f3608d | ||
|
|
cda154209d | ||
|
|
e3c72fa915 | ||
|
|
9354b429f3 | ||
|
|
b1f0c037bd | ||
|
|
726f1094d9 | ||
|
|
1b8ab156df | ||
|
|
ee01b5058f | ||
|
|
03ea8a0b5a | ||
|
|
f090f9a70c | ||
|
|
b7abc2c344 | ||
|
|
53bdf2dd6b | ||
|
|
b9e1b1d280 | ||
|
|
9051b157f8 | ||
|
|
f16d9b01b7 | ||
|
|
2a9bc2ddf8 | ||
|
|
6e39c46b07 | ||
|
|
567ca94245 | ||
|
|
812a4b32b4 | ||
|
|
023df10f31 | ||
|
|
8d513e2765 | ||
|
|
1eae6786c4 | ||
|
|
73f278963b | ||
|
|
54eff557d9 | ||
|
|
a7415bcfc9 | ||
|
|
b6bd9ad5b8 | ||
|
|
f71d9deb5e | ||
|
|
115b428a9d | ||
|
|
92a1e4a2d9 | ||
|
|
2504db13d7 | ||
|
|
43c72e684c | ||
|
|
46f0bf60c4 | ||
|
|
8bad4eb7eb | ||
|
|
fbe96f891b | ||
|
|
ab8950a5af | ||
|
|
26adfb6346 | ||
|
|
43b6b6d34e | ||
|
|
04ddc68dfd | ||
|
|
42cd10cf56 | ||
|
|
5f3bc33f8e | ||
|
|
2131070ee9 | ||
|
|
b2a82975e5 | ||
|
|
ddeba91faf | ||
|
|
6e572f05e4 | ||
|
|
50b435c4d1 | ||
|
|
af5c5dc021 | ||
|
|
87fd9665fe | ||
|
|
5e9ca84b40 | ||
|
|
090ad56d0b | ||
|
|
e3ebfea064 | ||
|
|
44761f87d2 | ||
|
|
04e3ceac96 | ||
|
|
afc7e605ff | ||
|
|
1c5aa6cd00 | ||
|
|
c1a87ebaaa | ||
|
|
46babe7a48 | ||
|
|
0dc64d2aa7 | ||
|
|
1d9e522bd9 | ||
|
|
5dcac6d7fe | ||
|
|
07418a3606 | ||
|
|
0e08af98ca | ||
|
|
b8b1ad8421 | ||
|
|
4fa1f40b44 | ||
|
|
5f55ebf1d9 | ||
|
|
9798de7efa | ||
|
|
102745875c | ||
|
|
dce0d93df8 | ||
|
|
1428ac5ed5 | ||
|
|
4f1bc1401f | ||
|
|
28ff6e1150 | ||
|
|
5ffe5fa3c5 | ||
|
|
49341356d7 | ||
|
|
e8b85f6cf7 | ||
|
|
4b2398b487 | ||
|
|
10f3313ed5 | ||
|
|
46664c6494 | ||
|
|
8ce7e61ed7 | ||
|
|
912458c679 | ||
|
|
e117b95057 | ||
|
|
3a0101261d | ||
|
|
48d3bc09f3 | ||
|
|
0b3e27b3fa | ||
|
|
b66535a17d | ||
|
|
fdc78e1b08 | ||
|
|
36064c564e | ||
|
|
7102810283 | ||
|
|
ab4d4e22da | ||
|
|
80c15de606 | ||
|
|
be5cac6d0b | ||
|
|
2bb67ee4b0 | ||
|
|
f7254044ee | ||
|
|
2e5d6f5b74 | ||
|
|
b7024ad1f5 | ||
|
|
577284b960 | ||
|
|
35b3808701 | ||
|
|
45c9a88fd7 | ||
|
|
831670e4ae | ||
|
|
37a9307681 | ||
|
|
34f6e878d5 | ||
|
|
bc1e039ea1 | ||
|
|
8c624c7f22 | ||
|
|
f63b8140af | ||
|
|
153d42f693 | ||
|
|
5647e54399 | ||
|
|
8663ed412f | ||
|
|
072613be99 | ||
|
|
6b7fc8119f | ||
|
|
29549b835a | ||
|
|
60fc61065e | ||
|
|
de23753204 | ||
|
|
9cd4d4c603 | ||
|
|
bc086cb93d | ||
|
|
f15bdf9445 | ||
|
|
9358e23726 | ||
|
|
6d605c1cbf | ||
|
|
be16eebf55 | ||
|
|
1f3bd9f51c | ||
|
|
5bdc81e84d | ||
|
|
08963b5e5f | ||
|
|
522a7225db | ||
|
|
032db387f8 | ||
|
|
5164464d28 | ||
|
|
a66ef4641f | ||
|
|
3fd372f9d1 | ||
|
|
b125f4e74b | ||
|
|
3a9c106aba | ||
|
|
a31ce5ef2a | ||
|
|
bf3b38036b | ||
|
|
34859ec479 | ||
|
|
4a6105de6b | ||
|
|
50ab0e0834 | ||
|
|
72b2ec804c | ||
|
|
086e6ea56b | ||
|
|
122395e0d0 | ||
|
|
98206852de | ||
|
|
dc960b9835 | ||
|
|
9adb85a64b | ||
|
|
23b02e7267 | ||
|
|
e3cef16629 | ||
|
|
5a2398dbea | ||
|
|
f1421bc724 | ||
|
|
7d6ffc676d | ||
|
|
385be26046 | ||
|
|
68146329b9 | ||
|
|
1231268a6b | ||
|
|
cbff3812e8 | ||
|
|
f8ae11993f | ||
|
|
3138178fea | ||
|
|
f20e5cf943 | ||
|
|
a1b35b7636 | ||
|
|
8eaaa04746 | ||
|
|
2bbf62c489 | ||
|
|
c80397ad68 | ||
|
|
c91ea37438 | ||
|
|
9671083bed | ||
|
|
6071b9c75a | ||
|
|
668bc9e0ca | ||
|
|
297ddb99fe | ||
|
|
cbd5686e7d | ||
|
|
a772acbd89 | ||
|
|
c2240966ef | ||
|
|
36984f4697 | ||
|
|
ac92498594 | ||
|
|
7bfe94ffc7 | ||
|
|
f85f4e6b69 | ||
|
|
c13c0c37e3 | ||
|
|
2098436a2c | ||
|
|
a427b8cead | ||
|
|
bcf920669b | ||
|
|
a9f057c278 | ||
|
|
4e4207f294 | ||
|
|
c86f7d053c | ||
|
|
7f7cc1db2a | ||
|
|
edfa41c9f9 | ||
|
|
1768b507f8 | ||
|
|
36949f4af1 | ||
|
|
328bc106e5 | ||
|
|
063e189bb7 | ||
|
|
1a9173d2c3 | ||
|
|
fdca4c5ecb | ||
|
|
b079a24373 | ||
|
|
ace84227cc | ||
|
|
f68b75c1cf | ||
|
|
f272748c5b | ||
|
|
a59cad68e2 | ||
|
|
eff7c92dae | ||
|
|
ca4d7f7fd2 | ||
|
|
0ed522481f | ||
|
|
2d4a321cc1 | ||
|
|
00dc18a488 | ||
|
|
b95ad44c18 | ||
|
|
9d3306ccc5 | ||
|
|
c2bcc29cfb | ||
|
|
0a2d7bf536 | ||
|
|
0895083f1f | ||
|
|
8de6d9ce15 | ||
|
|
c483ebd1d7 | ||
|
|
92d2b5812c | ||
|
|
9c2e4cfd9c | ||
|
|
8da131cc45 | ||
|
|
5cd25316fa | ||
|
|
705b8f6874 | ||
|
|
c050e00b8f | ||
|
|
03f6267c82 | ||
|
|
d955502ca2 | ||
|
|
f606012c87 | ||
|
|
3f24c63fc5 | ||
|
|
b78896e5f0 | ||
|
|
0efe871efe | ||
|
|
633100a3ce | ||
|
|
1c0de5ad8d | ||
|
|
e4d1087b72 | ||
|
|
040a3d7d43 | ||
|
|
fb281ddc9f | ||
|
|
afd278cf80 | ||
|
|
53fca124ab | ||
|
|
d72c77d6f3 | ||
|
|
0a7762743e | ||
|
|
863807a7a5 | ||
|
|
5c20ba57e9 | ||
|
|
a13b8fc124 | ||
|
|
2b015164f4 | ||
|
|
a21080fb76 | ||
|
|
cda0319be3 | ||
|
|
7108076260 | ||
|
|
7f3cf4a884 | ||
|
|
2d2adf29fd | ||
|
|
b02f376826 | ||
|
|
1fa41df3f1 | ||
|
|
77801de1ae | ||
|
|
3c9bae3402 | ||
|
|
d208f7bea7 | ||
|
|
4f360c2680 | ||
|
|
9e931cc3f6 | ||
|
|
5c24abcafd | ||
|
|
6d403c5864 | ||
|
|
0417a9873b | ||
|
|
22e9530c66 | ||
|
|
40997b4cb8 | ||
|
|
5d83a44ec3 | ||
|
|
d39781f3fb | ||
|
|
75636f7f00 | ||
|
|
f77e51290b | ||
|
|
8163416e27 | ||
|
|
9185848cbc | ||
|
|
d2e19d8818 | ||
|
|
5c481ebe40 | ||
|
|
1febba48ff | ||
|
|
a6faa68a5c | ||
|
|
48ace80183 | ||
|
|
b06d02028a | ||
|
|
c53427d7ec | ||
|
|
f818f9bda9 | ||
|
|
83070fc4ea | ||
|
|
6cd1d8113e | ||
|
|
5a90792e0d | ||
|
|
90b708fe83 | ||
|
|
f48a19554c | ||
|
|
cf7a4f0fb6 | ||
|
|
86a7a0d9e3 | ||
|
|
a5323d77f5 | ||
|
|
190cec7730 | ||
|
|
be7f0a2496 | ||
|
|
06779773dd | ||
|
|
2384714138 | ||
|
|
91f1f475e9 | ||
|
|
b1af7d8739 | ||
|
|
86d6053d2d | ||
|
|
c6280edbed | ||
|
|
4fd8a4dad8 | ||
|
|
fb2b8345d1 | ||
|
|
c9a579af8c | ||
|
|
92b838e07a | ||
|
|
f0cd97de72 | ||
|
|
764c9191b7 | ||
|
|
fbcaf15353 | ||
|
|
4eec4c0801 | ||
|
|
b01668d262 | ||
|
|
ae800455b1 | ||
|
|
56f0e8e087 | ||
|
|
8ca1804de9 | ||
|
|
0c52ed44a0 | ||
|
|
bb5a628a28 | ||
|
|
49de553ee8 | ||
|
|
1af469c8b1 | ||
|
|
2b2b4f5dd4 | ||
|
|
742910f369 | ||
|
|
3365dba870 | ||
|
|
ac0ba41ccf | ||
|
|
745aa7201c | ||
|
|
ea8dc73525 | ||
|
|
8683667041 | ||
|
|
24869b637d | ||
|
|
13679fe00d | ||
|
|
9f1730ae47 | ||
|
|
5e46fad0e9 | ||
|
|
e0d1414c4a | ||
|
|
8e5de2cb7b | ||
|
|
ca01781766 | ||
|
|
f7a4a0aef8 | ||
|
|
27ff3068f5 | ||
|
|
27c4de6aa0 | ||
|
|
e5bbbbe35c | ||
|
|
b4d3a10773 | ||
|
|
1850d2c04e | ||
|
|
5bdad8c0ca | ||
|
|
4b3255e4fd | ||
|
|
d5646584ee | ||
|
|
f15555ee34 | ||
|
|
b18657cf72 | ||
|
|
32b68f568b | ||
|
|
4e02179bf8 | ||
|
|
853dc0d8aa | ||
|
|
4d5d8d253b | ||
|
|
ab4ca5ec8b | ||
|
|
9ff45f70e2 | ||
|
|
3cba58ce57 | ||
|
|
8e7ce8780e | ||
|
|
707738c3ee | ||
|
|
e3b32e7679 | ||
|
|
ea1184cdd3 | ||
|
|
89dd0396fe | ||
|
|
8c0cb5716d | ||
|
|
6d60a2b012 | ||
|
|
7304741164 | ||
|
|
d2048a7c5a | ||
|
|
ac952b25bb | ||
|
|
d6e79f158f | ||
|
|
9bf83337c9 | ||
|
|
36bd034ee5 | ||
|
|
dc58c67883 | ||
|
|
88eef5df66 | ||
|
|
c75e66a2fc | ||
|
|
4e31d4af63 | ||
|
|
5972147e36 | ||
|
|
8ec0de457f | ||
|
|
310766180f | ||
|
|
db9d603881 | ||
|
|
e5f03b6aed | ||
|
|
bde54704d3 | ||
|
|
d20abc9aad | ||
|
|
945c0d95e6 | ||
|
|
bb501de22c | ||
|
|
bab03882e1 | ||
|
|
7ad30dfc63 | ||
|
|
4c5f820316 | ||
|
|
b65a0b1bd6 | ||
|
|
8f18dae2c1 | ||
|
|
653190c6a1 |
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,11 @@
|
||||
.DS_Store
|
||||
default.properties
|
||||
bin
|
||||
gen
|
||||
assets/www/phonegap.js
|
||||
local.properties
|
||||
framework/phonegap.jar
|
||||
framework/phonegap.jar
|
||||
framework/phonegap-*.jar
|
||||
framework/bin
|
||||
framework/assets/www/.DS_Store
|
||||
framework/assets/www/phonegap-*.js
|
||||
.DS_Store
|
||||
|
||||
81
LICENSE
81
LICENSE
@@ -1,21 +1,64 @@
|
||||
Copyright (c) 2010 Nitobi
|
||||
website: http://phonegap.com
|
||||
PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
MIT License (2008). As a recipient of PhonegGap, you may choose which
|
||||
license to receive this code under (except as noted in per-module LICENSE
|
||||
files). Some modules may not be the copyright of Nitobi. These
|
||||
modules contain explicit declarations of copyright in both the LICENSE files in
|
||||
the directories in which they reside and in the code itself. No external
|
||||
contributions are allowed under licenses which are fundamentally incompatible
|
||||
with the MIT or BSD licenses that PhoneGap is distributed under.
|
||||
|
||||
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 text of the MIT and BSD licenses is reproduced below.
|
||||
|
||||
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.
|
||||
-------------------------------------------------------------------------------
|
||||
The "New" BSD License:
|
||||
**********************
|
||||
|
||||
Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Phonegap/Nitobi nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
The MIT License
|
||||
*****************
|
||||
|
||||
Copyright (c) <2010> <Nitobi Software Inc., et. al., >
|
||||
|
||||
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.
|
||||
124
README.md
124
README.md
@@ -2,45 +2,127 @@ PhoneGap/Android
|
||||
================
|
||||
PhoneGap/Android is an Android application library that allows for PhoneGap based projects to be built for the Android Platform. PhoneGap based applications are, at the core, an application written with web technology: HTML, CSS and JavaScript.
|
||||
|
||||
Pre-requisites
|
||||
Pre Requisites
|
||||
--------------
|
||||
- Java JDK 1.5
|
||||
- Android SDK Package [http://developer.android.com](http://developer.android.com)
|
||||
- Android SDK [http://developer.android.com](http://developer.android.com)
|
||||
- Apache ANT
|
||||
- Ruby
|
||||
- Ruby (Optional, see section: DroidGap with JRuby)
|
||||
|
||||
Recommended
|
||||
-----------
|
||||
- Eclipse (Great for debugging and extending PhoneGap/Android with your own Java plugins.)
|
||||
Install
|
||||
-------
|
||||
|
||||
Getting Started with PhoneGap/Android
|
||||
--------------------------------------
|
||||
On any POSIX machine add PhoneGap/Android to your PATH variable like so:
|
||||
|
||||
1. From the root of this repo run the following command to generate a new PhoneGap/Android app:
|
||||
export PATH=$PATH:~/phonegap-android/bin
|
||||
|
||||
<pre>
|
||||
./droidgap [android_sdk_path] [name] [package_name] [www] [path]
|
||||
On Windows add the phonegap-android/bin to your PATH as normal.
|
||||
|
||||
android_sdk_path ... The path to your Android SDK install.
|
||||
name ............... The name of your application.
|
||||
package_name ....... The name of your package (For example: com.nitobi.demo)
|
||||
www ................ The path to your www folder. (Wherein your HTML, CSS and JS app is.)
|
||||
path ............... The path to generate the application.
|
||||
DroidGap: PhoneGap/Android Dev Script
|
||||
-------------------------------------
|
||||
|
||||
Tools for developers building mobile apps using PhoneGap for Android.
|
||||
|
||||
Usage:
|
||||
|
||||
<pre>droidgap [command] [parameters]</pre>
|
||||
|
||||
Commands:
|
||||
|
||||
<pre>
|
||||
help ...... See this message. Type help [command name] to see specific help topics.
|
||||
gen ....... Generate the example PhoneGap application to current directory (or optionally provide an output directory as parameter).
|
||||
create .... Creates an Android compatible project from a WWW folder.
|
||||
classic ... Backwards support for droidgap script. Run "droidgap help classic" for more info.
|
||||
update .... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project.
|
||||
test ...... Gets edge copy of mobile-spec and runs in first device or emulator attached.
|
||||
</pre>
|
||||
|
||||
Thats it!
|
||||
Quickstart:
|
||||
|
||||
<pre>
|
||||
$ droidgap gen exampleapp
|
||||
$ cd exampleapp
|
||||
$ ant debug install && adb logcat
|
||||
</pre>
|
||||
|
||||
DroidGap with JRuby
|
||||
-------------------
|
||||
|
||||
If you want to use the droidgap command but do not want to install Ruby then you can call it using jruby jar included in the lib folder. All the options are the same and a call looks like this:
|
||||
|
||||
java -jar jruby-complete-1.4.0RC1.jar ../bin/droidgap help run
|
||||
|
||||
Keep in mind this will be slower due to JVM warmup.
|
||||
|
||||
Importing a PhoneGap/Android app into Eclipse
|
||||
---------------------------------------------
|
||||
|
||||
1. File > New > Project...
|
||||
2. Android > Android Project
|
||||
3. Create project from existing source (point to the generated app). This should import the project into Eclipse. Almost done!
|
||||
4. Right click on libs/phonegap.jar and add to build path.
|
||||
3. Create project from existing source (point to the generated app found in tmp/android)
|
||||
4. Right click on libs/phonegap.jar and add to build path
|
||||
5. Right click on the project root: Run as > Run Configurations
|
||||
6. Click on the Target tab and select Manual (this way you can choose the emulator or device to build to).
|
||||
6. Click on the Target tab and select Manual (this way you can choose the emulator or device to build to)
|
||||
|
||||
|
||||
Common Command Line Tasks
|
||||
=========================
|
||||
|
||||
Running Mobile Spec
|
||||
---
|
||||
|
||||
droidgap test
|
||||
|
||||
Compile an APK
|
||||
---
|
||||
|
||||
Make sure you have a device plugged in (with debugging enabled) or a running emulator. Then:
|
||||
|
||||
ant debug install
|
||||
|
||||
or
|
||||
|
||||
droidgap run
|
||||
|
||||
Converting a W3C Widget into a an APK
|
||||
---
|
||||
|
||||
Given a Widget called FooBar with an index.html file in it. You navigate to its folder and run:
|
||||
|
||||
droidgap create
|
||||
cd ../FooBar_android
|
||||
ant debug install
|
||||
|
||||
List devices attached
|
||||
---
|
||||
|
||||
adb devices
|
||||
List of devices attached
|
||||
0123456789012 device
|
||||
|
||||
Install APK onto device
|
||||
---
|
||||
|
||||
apk -s 0123456789012 install phonegap.apk
|
||||
|
||||
Logging
|
||||
---
|
||||
|
||||
Via console.log calls from your apps javascript.
|
||||
|
||||
adb logcat
|
||||
|
||||
Debugging
|
||||
---
|
||||
|
||||
Attach it to a process on the device
|
||||
|
||||
$ adb jdwp
|
||||
adb forward tcp:8000 jdwp: jdb -attach localhost:8000
|
||||
|
||||
|
||||
For more info see
|
||||
-----------------
|
||||
- [http://docs.phonegap.com](http://docs.phonegap.com)
|
||||
- [http://wiki.phonegap.com](http://wiki.phonegap.com)
|
||||
- [http://wiki.phonegap.com](http://wiki.phonegap.com)
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=320; user-scalable=no" />
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
var deviceInfo = function(){
|
||||
document.getElementById("platform").innerHTML = device.platform;
|
||||
document.getElementById("version").innerHTML = device.version;
|
||||
document.getElementById("uuid").innerHTML = device.uuid;
|
||||
console.log("Height:" + window.innerHeight);
|
||||
console.log("Width:" + window.innerWidth);
|
||||
}
|
||||
|
||||
var getLocation = function() {
|
||||
var suc = function(p){
|
||||
alert(p.coords.latitude + " " + p.coords.longitude);
|
||||
};
|
||||
var fail = function(){};
|
||||
navigator.geolocation.getCurrentPosition(suc,fail);
|
||||
}
|
||||
|
||||
var beep = function(){
|
||||
navigator.notification.beep(2);
|
||||
}
|
||||
|
||||
var vibrate = function(){
|
||||
navigator.notification.vibrate(0);
|
||||
}
|
||||
|
||||
var getContact = function(){
|
||||
var suc = function(c){ alert("Contact 4: " + c.contacts[3].name); };
|
||||
var fail = function(){};
|
||||
navigator.ContactManager.get(suc, fail);
|
||||
}
|
||||
|
||||
var watchAccel = function() {
|
||||
var suc = function(a){
|
||||
document.getElementById('x').innerHTML = roundNumber(a.x);
|
||||
document.getElementById('y').innerHTML = roundNumber(a.y);
|
||||
document.getElementById('z').innerHTML = roundNumber(a.z);
|
||||
};
|
||||
var fail = function(){};
|
||||
var opt = {};
|
||||
opt.frequency = 100;
|
||||
timer = navigator.accelerometer.watchAcceleration(suc,fail,opt);
|
||||
}
|
||||
|
||||
function roundNumber(num) {
|
||||
var dec = 3;
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
return result;
|
||||
}
|
||||
|
||||
var preventBehavior = function(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
function show_pic()
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.display = "";
|
||||
navigator.camera.getPicture(dump_pic, fail, { quality: 50 });
|
||||
}
|
||||
|
||||
function dump_pic(data)
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
console.log(data);
|
||||
viewport.style.display = "";
|
||||
viewport.style.position = "absolute";
|
||||
viewport.style.top = "10px";
|
||||
viewport.style.left = "10px";
|
||||
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
|
||||
}
|
||||
|
||||
function close()
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.position = "relative";
|
||||
viewport.style.display = "none";
|
||||
}
|
||||
|
||||
function fail(fail)
|
||||
{
|
||||
alert(fail);
|
||||
}
|
||||
|
||||
// This is just to do this.
|
||||
function readFile()
|
||||
{
|
||||
navigator.file.read('/sdcard/phonegap.txt', fail , fail);
|
||||
}
|
||||
|
||||
function writeFile()
|
||||
{
|
||||
navigator.file.write('foo.txt', "This is a test of writing to a file", fail, fail);
|
||||
}
|
||||
|
||||
function get_contacts()
|
||||
{
|
||||
navigator.ContactManager.getAllContacts(count_contacts, fail, null);
|
||||
}
|
||||
|
||||
function count_contacts(contacts)
|
||||
{
|
||||
alert(contacts.length);
|
||||
}
|
||||
|
||||
function init(){
|
||||
document.addEventListener("touchmove", preventBehavior, false);
|
||||
document.addEventListener("deviceready", deviceInfo, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
<h1>Welcome to PhoneGap!</h1>
|
||||
<h2>this file is located at assets/index.html</h2>
|
||||
<div id="info">
|
||||
<h4>Platform: <span id="platform"> </span></h4>
|
||||
<h4>Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span></h4>
|
||||
</div>
|
||||
<dl id="accel-data">
|
||||
<dt>X:</dt><dd id="x"> </dd>
|
||||
<dt>Y:</dt><dd id="y"> </dd>
|
||||
<dt>Z:</dt><dd id="z"> </dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="watchAccel();">Watch 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();">Get phone's contacts</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
2
bin/android-sdk-path.bat
Executable file
2
bin/android-sdk-path.bat
Executable file
@@ -0,0 +1,2 @@
|
||||
@ECHO OFF
|
||||
echo %~dp$PATH:1
|
||||
173
bin/droidgap
Executable file
173
bin/droidgap
Executable file
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env ruby
|
||||
ROOT = File.expand_path(File.dirname(__FILE__).gsub(/bin$/,''))
|
||||
require 'fileutils'
|
||||
require File.join(ROOT, "lib", "generate.rb")
|
||||
require File.join(ROOT, "lib", "classic.rb")
|
||||
require File.join(ROOT, "lib", "create.rb")
|
||||
require File.join(ROOT, "lib", "run.rb")
|
||||
require File.join(ROOT, "lib", "update.rb")
|
||||
require File.join(ROOT, "lib", "test.rb")
|
||||
|
||||
# ---------------------------------------------------------- #
|
||||
# #
|
||||
# command line interface #
|
||||
# #
|
||||
# ---------------------------------------------------------- #
|
||||
|
||||
# droidgap gen [app name]
|
||||
Generate.new(ARGV[1]) if ARGV.first == 'gen'
|
||||
|
||||
# droidgap classic (for windows users mostly)
|
||||
Classic.new(ARGV[1..-1]) if ARGV.first == 'classic'
|
||||
|
||||
# droidgap create [path to phonegap project]
|
||||
Create.new(ARGV[1]) if ARGV.first == 'create'
|
||||
|
||||
# droidgap run [optional directory]
|
||||
Run.new if ARGV.first == 'run'
|
||||
|
||||
# droidgap update [params]
|
||||
Update.new if ARGV.first == 'update'
|
||||
|
||||
# droidgap log
|
||||
if ARGV.first == 'log'
|
||||
$stdout.sync = true
|
||||
IO.popen('adb logcat') do |f|
|
||||
until f.eof?
|
||||
puts f.gets
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# droidgap test
|
||||
Test.new if ARGV.first == 'test'
|
||||
|
||||
# TODO implement these!
|
||||
puts "droidgap ship not implemented" if ARGV.first == 'ship'
|
||||
|
||||
if ARGV.first.nil? || ARGV.first == 'help'
|
||||
help = <<-EOF
|
||||
|
||||
DroidGap: PhoneGap/Android Dev Script
|
||||
-------------------------------------
|
||||
|
||||
Useful utilities for devlopers building mobile apps using PhoneGap for Android.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap <command> <parameters>
|
||||
|
||||
Commands:
|
||||
|
||||
help ...... See this message. Type help [command name] to see specific help topics.
|
||||
gen ....... Generate the example PhoneGap application to current directory (or optionally provide an output directory as parameter).
|
||||
create .... Creates an Android compatible project from a WWW folder.
|
||||
classic ... Backwards support for droidgap script. Run "droidgap help classic" for more info.
|
||||
update .... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project.
|
||||
ship ...... Build and sign an APK suitable for submission to an Android Marketplace.
|
||||
|
||||
Quickstart:
|
||||
|
||||
$ droidgap gen exampleapp
|
||||
$ cd exampleapp
|
||||
$ ant debug install && adb logcat
|
||||
|
||||
EOF
|
||||
|
||||
gen = <<-EOF
|
||||
|
||||
DroidGap Generate
|
||||
-----------------
|
||||
|
||||
Generate the example PhoneGap application to path supplied or current working directory if none is supplied.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap gen [path]
|
||||
|
||||
NOTE: Do *not* run "droidgap gen example" - you will end up with a recursive directory problem.
|
||||
|
||||
EOF
|
||||
|
||||
run = <<-EOF
|
||||
|
||||
DroidGap Run
|
||||
------------
|
||||
|
||||
Launches PhoneGap project to first device found and attaches a logger that listens for console.log statements.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap run <path>
|
||||
|
||||
EOF
|
||||
|
||||
ship = <<-EOF
|
||||
|
||||
DroidGap Ship
|
||||
-------------
|
||||
|
||||
Build and sign an APK suitable for submission to an Android Marketplace.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap ship <path>
|
||||
|
||||
EOF
|
||||
|
||||
log = <<-EOF
|
||||
|
||||
DroidGap Log
|
||||
-------------
|
||||
|
||||
Launches LogCat
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap log
|
||||
|
||||
EOF
|
||||
|
||||
create = <<-EOF
|
||||
|
||||
DroidGap Create
|
||||
----------------
|
||||
|
||||
Creates an Android compatable project from a PhoneGap project. For example, if you have MyProject with index.html this command will create MyProject_android.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap create <path>
|
||||
|
||||
EOF
|
||||
|
||||
update = <<-EOF
|
||||
|
||||
DroidGap Update
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Builds the JS and PhoneGap Android jar file and copies them to your project.
|
||||
|
||||
EOF
|
||||
|
||||
classic = <<-EOF
|
||||
|
||||
DroidGap Classic
|
||||
~~~~~~~~~~~~-~~~
|
||||
|
||||
Compatability for older droidgap scripts.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap classic [android_sdk_path] [name] [package_name] [www] [path]
|
||||
|
||||
android_sdk_path ... The path to your Android SDK install.
|
||||
name ............... The name of your application.
|
||||
package_name ....... The name of your package (For example: com.nitobi.demo)
|
||||
www ................ The path to your www folder. (Wherein your HTML, CSS and JS app is.)
|
||||
path ............... The path to generate the application.
|
||||
|
||||
EOF
|
||||
|
||||
puts ARGV[1].nil? ? help : eval(ARGV[1])
|
||||
end
|
||||
1
bin/droidgap.bat
Executable file
1
bin/droidgap.bat
Executable file
@@ -0,0 +1 @@
|
||||
ruby %~dp0droidgap %1 %2
|
||||
179
droidgap
179
droidgap
@@ -1,179 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
require 'fileutils'
|
||||
|
||||
# ./droidgap /Users/brianleroux/Code/android-sdk-mac MyApp com.westcoastlogic example /Users/brianleroux/Desktop/MyApp
|
||||
class Build
|
||||
attr_reader :android_sdk_path, :name, :pkg, :www, :path
|
||||
|
||||
def initialize(*a)
|
||||
@android_sdk_path, @name, @pkg, @www, @path = a
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__))
|
||||
@framework_dir = File.join(@android_dir, "framework")
|
||||
end
|
||||
|
||||
# runs the build script
|
||||
def run
|
||||
build_jar
|
||||
create_android
|
||||
include_www
|
||||
generate_manifest
|
||||
copy_libs
|
||||
add_name_to_strings
|
||||
write_java
|
||||
puts "Complete!"
|
||||
end
|
||||
|
||||
# removes local.properties and recreates based on android_sdk_path
|
||||
# then generates framework/phonegap.jar
|
||||
def build_jar
|
||||
puts "Building the JAR..."
|
||||
%w(local.properties phonegap.js phonegap.jar).each do |f|
|
||||
FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f)
|
||||
end
|
||||
open(File.join(@framework_dir, "local.properties"), 'w') do |f|
|
||||
f.puts "sdk.dir=#{ @android_sdk_path }"
|
||||
end
|
||||
Dir.chdir(@framework_dir)
|
||||
`ant jar`
|
||||
Dir.chdir(@android_dir)
|
||||
end
|
||||
|
||||
# runs android create project
|
||||
# TODO need to allow more flexible SDK targetting
|
||||
# TODO validate Android SDK
|
||||
# TODO fix 'android' shell call so that it works on Windows. Can't prepend android command with path to it.
|
||||
def create_android
|
||||
android_exec = File.join(@android_sdk_path, "tools", "android");
|
||||
target_id = 5
|
||||
puts "Creating Android project for target level #{ target_id }"
|
||||
`android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }`
|
||||
end
|
||||
|
||||
def include_www
|
||||
puts "Adding www folder to project..."
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "assets", "www")
|
||||
FileUtils.cp_r File.join(@www, "."), File.join(@path, "assets", "www")
|
||||
end
|
||||
|
||||
# creates an AndroidManifest.xml for the project
|
||||
def generate_manifest
|
||||
puts "Generating manifest..."
|
||||
manifest = ""
|
||||
open(File.join(@framework_dir, "AndroidManifest.xml"), 'r') do |old|
|
||||
manifest = old.read
|
||||
manifest.gsub! 'android:versionCode="5"', 'android:versionCode="1"'
|
||||
manifest.gsub! 'package="com.phonegap"', "package=\"#{ @pkg }\""
|
||||
manifest.gsub! 'android:name=".StandAlone"', "android:name=\".#{ @name }\""
|
||||
manifest.gsub! 'android:minSdkVersion="5"', 'android:minSdkVersion="3"'
|
||||
end
|
||||
open(File.join(@path, "AndroidManifest.xml"), 'w') { |x| x.puts manifest }
|
||||
end
|
||||
|
||||
# copies stuff from framework into the project
|
||||
# TODO need to allow for www import inc icon
|
||||
def copy_libs
|
||||
puts "Copying over libraries and assets and creating phonegap.js..."
|
||||
|
||||
framework_res_dir = File.join(@framework_dir, "res")
|
||||
app_res_dir = File.join(@path, "res")
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs")
|
||||
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "values")
|
||||
FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml")
|
||||
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "layout")
|
||||
%w(main.xml preview.xml).each do |f|
|
||||
FileUtils.cp File.join(framework_res_dir, "layout", f), File.join(app_res_dir, "layout", f)
|
||||
end
|
||||
|
||||
%w(drawable-hdpi drawable-ldpi drawable-mdpi).each do |e|
|
||||
FileUtils.mkdir_p File.join(app_res_dir, e)
|
||||
FileUtils.cp File.join(framework_res_dir, "drawable", "icon.png"), File.join(app_res_dir, e, "icon.png")
|
||||
end
|
||||
|
||||
# concat JS and put into www folder.
|
||||
js_dir = File.join(@framework_dir, "assets", "js")
|
||||
|
||||
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
|
||||
|
||||
Dir.new(js_dir).entries.each do |script|
|
||||
next if script[0].chr == "." or script == "phonegap.js.base"
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
|
||||
File.open(File.join(@path, "assets", "www", "phonegap.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
|
||||
# puts app name in strings
|
||||
def add_name_to_strings
|
||||
puts "Adding some application name to strings.xml..."
|
||||
x = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
<resources>
|
||||
<string name=\"app_name\">#{ @name }</string>
|
||||
<string name=\"go\">Snap</string>
|
||||
</resources>
|
||||
"
|
||||
open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f|
|
||||
f.puts x.gsub(' ','')
|
||||
end
|
||||
end
|
||||
|
||||
# this is so fucking unholy yet oddly beautiful
|
||||
# not sure if I should thank Ruby or apologize for this abusive use of string interpolation
|
||||
def write_java
|
||||
puts "Writing application Java code..."
|
||||
j = "
|
||||
package #{ @pkg };
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import com.phonegap.*;
|
||||
|
||||
public class #{ @name } extends DroidGap
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
super.loadUrl(\"file:///android_asset/www/index.html\");
|
||||
}
|
||||
}
|
||||
"
|
||||
|
||||
code_dir = File.join(@path, "src", @pkg.gsub('.', File::SEPARATOR))
|
||||
|
||||
FileUtils.mkdir_p(code_dir)
|
||||
open(File.join(code_dir, "#{@name}.java"),'w') { |f| f.puts j.gsub(' ','') }
|
||||
end
|
||||
#
|
||||
end
|
||||
|
||||
|
||||
if ARGV.length == 5
|
||||
Build.new(*ARGV).run
|
||||
else
|
||||
puts <<-EOF
|
||||
|
||||
DroidGap: PhoneGap/Android Project Generator
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Creates a fresh app for hybrid mobile web hacking. Delicious robot!
|
||||
|
||||
Usage:
|
||||
|
||||
./droidgap <android_sdk_path> <name> <package_name> <www> <path>
|
||||
|
||||
Params:
|
||||
|
||||
android_sdk_path ... The path to your Android SDK install.
|
||||
name ............... The name of your application.
|
||||
package_name ....... The name of your package (For example: com.nitobi.demo)
|
||||
www ................ The path to your www folder. (Wherein your HTML, CSS and JS app is.)
|
||||
path ............... The path to generate the Android application.
|
||||
|
||||
EOF
|
||||
end
|
||||
@@ -5,143 +5,34 @@
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>PhoneGap</title>
|
||||
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
|
||||
var deviceInfo = function(){
|
||||
document.getElementById("platform").innerHTML = device.platform;
|
||||
document.getElementById("version").innerHTML = device.version;
|
||||
document.getElementById("uuid").innerHTML = device.uuid;
|
||||
console.log("Height:" + window.innerHeight);
|
||||
console.log("Width:" + window.innerWidth);
|
||||
}
|
||||
|
||||
var getLocation = function() {
|
||||
var suc = function(p){
|
||||
alert(p.coords.latitude + " " + p.coords.longitude);
|
||||
};
|
||||
var fail = function(){};
|
||||
navigator.geolocation.getCurrentPosition(suc,fail);
|
||||
}
|
||||
|
||||
var beep = function(){
|
||||
navigator.notification.beep(2);
|
||||
}
|
||||
|
||||
var vibrate = function(){
|
||||
navigator.notification.vibrate(0);
|
||||
}
|
||||
|
||||
var getContact = function(){
|
||||
var suc = function(c){ alert("Contact 4: " + c.contacts[3].name); };
|
||||
var fail = function(){};
|
||||
navigator.ContactManager.get(suc, fail);
|
||||
}
|
||||
|
||||
var watchAccel = function() {
|
||||
var suc = function(a){
|
||||
document.getElementById('x').innerHTML = roundNumber(a.x);
|
||||
document.getElementById('y').innerHTML = roundNumber(a.y);
|
||||
document.getElementById('z').innerHTML = roundNumber(a.z);
|
||||
};
|
||||
var fail = function(){};
|
||||
var opt = {};
|
||||
opt.frequency = 100;
|
||||
timer = navigator.accelerometer.watchAcceleration(suc,fail,opt);
|
||||
}
|
||||
|
||||
function roundNumber(num) {
|
||||
var dec = 3;
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
return result;
|
||||
}
|
||||
|
||||
var preventBehavior = function(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
function show_pic()
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.display = "";
|
||||
navigator.camera.getPicture(dump_pic, fail, { quality: 50 });
|
||||
}
|
||||
|
||||
function dump_pic(data)
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
console.log(data);
|
||||
viewport.style.display = "";
|
||||
viewport.style.position = "absolute";
|
||||
viewport.style.top = "10px";
|
||||
viewport.style.left = "10px";
|
||||
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
|
||||
}
|
||||
|
||||
function close()
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.position = "relative";
|
||||
viewport.style.display = "none";
|
||||
}
|
||||
|
||||
function fail(fail)
|
||||
{
|
||||
alert(fail);
|
||||
}
|
||||
|
||||
// This is just to do this.
|
||||
function readFile()
|
||||
{
|
||||
navigator.file.read('/sdcard/phonegap.txt', fail , fail);
|
||||
}
|
||||
|
||||
function writeFile()
|
||||
{
|
||||
navigator.file.write('foo.txt', "This is a test of writing to a file", fail, fail);
|
||||
}
|
||||
|
||||
function get_contacts()
|
||||
{
|
||||
navigator.ContactManager.getAllContacts(count_contacts, fail, null);
|
||||
}
|
||||
|
||||
function count_contacts(contacts)
|
||||
{
|
||||
alert(contacts.length);
|
||||
}
|
||||
|
||||
function init(){
|
||||
document.addEventListener("touchmove", preventBehavior, false);
|
||||
document.addEventListener("deviceReady", deviceInfo, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
<h1>Welcome to PhoneGap!</h1>
|
||||
<h2>this file is located at assets/index.html</h2>
|
||||
<h2>this file is located at assets/www/index.html</h2>
|
||||
<div id="info">
|
||||
<h4>Platform: <span id="platform"> </span></h4>
|
||||
<h4>Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span></h4>
|
||||
</div>
|
||||
<h4>Platform: <span id="platform"> </span>, Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span>, Name: <span id="name"> </span></h4>
|
||||
<h4>Width: <span id="width"> </span>, Height: <span id="height">
|
||||
</span>, Color Depth: <span id="colorDepth"></span></h4>
|
||||
</div>
|
||||
<dl id="accel-data">
|
||||
<dt>X:</dt><dd id="x"> </dd>
|
||||
<dt>Y:</dt><dd id="y"> </dd>
|
||||
<dt>Z:</dt><dd id="z"> </dd>
|
||||
</dl>
|
||||
<a href="#" class="btn large" onclick="watchAccel();">Watch Accelerometer</a>
|
||||
<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();">Get phone's contacts</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<a href="#" class="btn large" onclick="get_contacts();">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();">Check Network</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
127
example/main.js
Normal file
127
example/main.js
Normal file
@@ -0,0 +1,127 @@
|
||||
var deviceInfo = function() {
|
||||
document.getElementById("platform").innerHTML = device.platform;
|
||||
document.getElementById("version").innerHTML = device.version;
|
||||
document.getElementById("uuid").innerHTML = device.uuid;
|
||||
document.getElementById("name").innerHTML = device.name;
|
||||
document.getElementById("width").innerHTML = screen.width;
|
||||
document.getElementById("height").innerHTML = screen.height;
|
||||
document.getElementById("colorDepth").innerHTML = screen.colorDepth;
|
||||
};
|
||||
|
||||
var getLocation = function() {
|
||||
var suc = function(p) {
|
||||
alert(p.coords.latitude + " " + p.coords.longitude);
|
||||
};
|
||||
var locFail = function() {
|
||||
};
|
||||
navigator.geolocation.getCurrentPosition(suc, locFail);
|
||||
};
|
||||
|
||||
var beep = function() {
|
||||
navigator.notification.beep(2);
|
||||
};
|
||||
|
||||
var vibrate = function() {
|
||||
navigator.notification.vibrate(0);
|
||||
};
|
||||
|
||||
function roundNumber(num) {
|
||||
var dec = 3;
|
||||
var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
|
||||
return result;
|
||||
}
|
||||
|
||||
var accelerationWatch = null;
|
||||
|
||||
function updateAcceleration(a) {
|
||||
document.getElementById('x').innerHTML = roundNumber(a.x);
|
||||
document.getElementById('y').innerHTML = roundNumber(a.y);
|
||||
document.getElementById('z').innerHTML = roundNumber(a.z);
|
||||
}
|
||||
|
||||
var toggleAccel = function() {
|
||||
if (accelerationWatch !== null) {
|
||||
navigator.accelerometer.clearWatch(accelerationWatch);
|
||||
updateAcceleration({
|
||||
x : "",
|
||||
y : "",
|
||||
z : ""
|
||||
});
|
||||
accelerationWatch = null;
|
||||
} else {
|
||||
var options = {};
|
||||
options.frequency = 1000;
|
||||
accelerationWatch = navigator.accelerometer.watchAcceleration(
|
||||
updateAcceleration, function(ex) {
|
||||
alert("accel fail (" + ex.name + ": " + ex.message + ")");
|
||||
}, options);
|
||||
}
|
||||
};
|
||||
|
||||
var preventBehavior = function(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
function dump_pic(data) {
|
||||
var viewport = document.getElementById('viewport');
|
||||
console.log(data);
|
||||
viewport.style.display = "";
|
||||
viewport.style.position = "absolute";
|
||||
viewport.style.top = "10px";
|
||||
viewport.style.left = "10px";
|
||||
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
|
||||
}
|
||||
|
||||
function fail(msg) {
|
||||
alert(msg);
|
||||
}
|
||||
|
||||
function show_pic() {
|
||||
navigator.camera.getPicture(dump_pic, fail, {
|
||||
quality : 50
|
||||
});
|
||||
}
|
||||
|
||||
function close() {
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.position = "relative";
|
||||
viewport.style.display = "none";
|
||||
}
|
||||
|
||||
function contacts_success(contacts) {
|
||||
alert(contacts.length
|
||||
+ ' contacts returned.'
|
||||
+ (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted)
|
||||
: ''));
|
||||
}
|
||||
|
||||
function get_contacts() {
|
||||
var obj = new ContactFindOptions();
|
||||
obj.filter = "";
|
||||
obj.multiple = true;
|
||||
navigator.contacts.find(
|
||||
[ "displayName", "name" ], contacts_success,
|
||||
fail, obj);
|
||||
}
|
||||
|
||||
function check_network() {
|
||||
var networkState = navigator.network.connection.type;
|
||||
|
||||
var states = {};
|
||||
states[Connection.UNKNOWN] = 'Unknown connection';
|
||||
states[Connection.ETHERNET] = 'Ethernet connection';
|
||||
states[Connection.WIFI] = 'WiFi connection';
|
||||
states[Connection.CELL_2G] = 'Cell 2G connection';
|
||||
states[Connection.CELL_3G] = 'Cell 3G connection';
|
||||
states[Connection.CELL_4G] = 'Cell 4G connection';
|
||||
states[Connection.NONE] = 'No network connection';
|
||||
|
||||
confirm('Connection type:\n ' + states[networkState]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
// the next line makes it impossible to see Contacts on the HTC Evo since it
|
||||
// doesn't have a scroll button
|
||||
// document.addEventListener("touchmove", preventBehavior, false);
|
||||
document.addEventListener("deviceready", deviceInfo, true);
|
||||
}
|
||||
1188
example/phonegap.js
1188
example/phonegap.js
File diff suppressed because it is too large
Load Diff
@@ -1,43 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
||||
package="com.phonegap" android:versionName="1.1" android:versionCode="5">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true"
|
||||
android:resizeable="true"
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:debuggable="true">
|
||||
<activity android:name=".StandAlone"
|
||||
<activity android:name=".StandAlone" android:windowSoftInputMode="adjustPan"
|
||||
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.phonegap.CameraPreview"
|
||||
android:label="@string/app_name" android:screenOrientation="landscape"
|
||||
<activity android:name="com.phonegap.DroidGap" android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
<intent-filter>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
</manifest>
|
||||
|
||||
168
framework/assets/js/accelerometer.js
Normal file → Executable file
168
framework/assets/js/accelerometer.js
Normal file → Executable file
@@ -1,102 +1,126 @@
|
||||
function Acceleration(x, y, z)
|
||||
{
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("accelerometer")) {
|
||||
PhoneGap.addResource("accelerometer");
|
||||
|
||||
/** @constructor */
|
||||
var Acceleration = function(x, y, z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.timestamp = new Date().getTime();
|
||||
this.win = null;
|
||||
this.fail = null;
|
||||
}
|
||||
|
||||
var accelListeners = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* This class provides access to device accelerometer data.
|
||||
* @constructor
|
||||
*/
|
||||
function Accelerometer() {
|
||||
/**
|
||||
* The last known acceleration.
|
||||
*/
|
||||
this.lastAcceleration = null;
|
||||
}
|
||||
var Accelerometer = function() {
|
||||
|
||||
/**
|
||||
* The last known acceleration. type=Acceleration()
|
||||
*/
|
||||
this.lastAcceleration = null;
|
||||
|
||||
/**
|
||||
* List of accelerometer watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
};
|
||||
|
||||
Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
/**
|
||||
* Asynchronously aquires the current acceleration.
|
||||
* @param {Function} successCallback The function to call when the acceleration
|
||||
* data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error
|
||||
* getting the acceleration data.
|
||||
* @param {AccelerationOptions} options The options for getting the accelerometer data
|
||||
* such as timeout.
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the acceleration data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
|
||||
* @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
|
||||
*/
|
||||
Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
|
||||
// If the acceleration is available then call success
|
||||
// If the acceleration is not available then call error
|
||||
|
||||
// Created for iPhone, Iphone passes back _accel obj litteral
|
||||
if (typeof successCallback == "function") {
|
||||
if(this.lastAcceleration)
|
||||
successCallback(accel);
|
||||
else
|
||||
{
|
||||
watchAcceleration(this.gotCurrentAcceleration, this.fail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Accelerometer.prototype.gotAccel = function(key, x, y, z)
|
||||
{
|
||||
console.log('we won');
|
||||
var a = new Acceleration(x,y,z);
|
||||
a.x = x;
|
||||
a.y = y;
|
||||
a.x = z;
|
||||
a.win = accelListeners[key].win;
|
||||
a.fail = accelListeners[key].fail;
|
||||
this.timestamp = new Date().getTime();
|
||||
this.lastAcceleration = a;
|
||||
accelListeners[key] = a;
|
||||
if (typeof a.win == "function") {
|
||||
a.win(a);
|
||||
// successCallback required
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Accelerometer Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Accelerometer Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get acceleration
|
||||
PhoneGap.exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously aquires the acceleration repeatedly at a given interval.
|
||||
* @param {Function} successCallback The function to call each time the acceleration
|
||||
* data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error
|
||||
* getting the acceleration data.
|
||||
* @param {AccelerationOptions} options The options for getting the accelerometer data
|
||||
* such as timeout.
|
||||
*
|
||||
* @param {Function} successCallback The function to call each time the acceleration data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the acceleration data. (OPTIONAL)
|
||||
* @param {AccelerationOptions} options The options for getting the accelerometer data such as timeout. (OPTIONAL)
|
||||
* @return String The watch id that must be passed to #clearWatch to stop watching.
|
||||
*/
|
||||
|
||||
Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
|
||||
// TODO: add the interval id to a list so we can clear all watches
|
||||
var frequency = (options != undefined)? options.frequency : 10000;
|
||||
var accel = Acceleration(0,0,0);
|
||||
accel.win = successCallback;
|
||||
accel.fail = errorCallback;
|
||||
accel.opts = options;
|
||||
var key = accelListeners.push( accel ) - 1;
|
||||
Accel.start(frequency, key);
|
||||
}
|
||||
|
||||
// Default interval (10 sec)
|
||||
var frequency = (options !== undefined)? options.frequency : 10000;
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Accelerometer Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Accelerometer Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure accelerometer timeout > frequency + 10 sec
|
||||
PhoneGap.exec(
|
||||
function(timeout) {
|
||||
if (timeout < (frequency + 10000)) {
|
||||
PhoneGap.exec(null, null, "Accelerometer", "setTimeout", [frequency + 10000]);
|
||||
}
|
||||
},
|
||||
function(e) { }, "Accelerometer", "getTimeout", []);
|
||||
|
||||
// Start watch timer
|
||||
var id = PhoneGap.createUUID();
|
||||
navigator.accelerometer.timers[id] = setInterval(function() {
|
||||
PhoneGap.exec(successCallback, errorCallback, "Accelerometer", "getAcceleration", []);
|
||||
}, (frequency ? frequency : 1));
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the specified accelerometer watch.
|
||||
* @param {String} watchId The ID of the watch returned from #watchAcceleration.
|
||||
*
|
||||
* @param {String} id The id of the watch returned from #watchAcceleration.
|
||||
*/
|
||||
Accelerometer.prototype.clearWatch = function(watchId) {
|
||||
Accel.stop(watchId);
|
||||
}
|
||||
Accelerometer.prototype.clearWatch = function(id) {
|
||||
|
||||
Accelerometer.prototype.epicFail = function(watchId, message) {
|
||||
accelWatcher[key].fail();
|
||||
}
|
||||
// Stop javascript timer & remove from timer list
|
||||
if (id && navigator.accelerometer.timers[id] !== undefined) {
|
||||
clearInterval(navigator.accelerometer.timers[id]);
|
||||
delete navigator.accelerometer.timers[id];
|
||||
}
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
|
||||
if (typeof navigator.accelerometer === "undefined") {
|
||||
navigator.accelerometer = new Accelerometer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
86
framework/assets/js/app.js
Executable file
86
framework/assets/js/app.js
Executable file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("app")) {
|
||||
PhoneGap.addResource("app");
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @constructor
|
||||
*/
|
||||
var App = function() {};
|
||||
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
App.prototype.clearCache = function() {
|
||||
PhoneGap.exec(null, null, "App", "clearCache", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs.
|
||||
* loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html");
|
||||
* keepRunning: boolean => enable app to keep running in background
|
||||
*
|
||||
* Example:
|
||||
* App app = new App();
|
||||
* app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
*/
|
||||
App.prototype.loadUrl = function(url, props) {
|
||||
PhoneGap.exec(null, null, "App", "loadUrl", [url, props]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cancel loadUrl that is waiting to be loaded.
|
||||
*/
|
||||
App.prototype.cancelLoadUrl = function() {
|
||||
PhoneGap.exec(null, null, "App", "cancelLoadUrl", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear web history in this web view.
|
||||
* Instead of BACK button loading the previous web page, it will exit the app.
|
||||
*/
|
||||
App.prototype.clearHistory = function() {
|
||||
PhoneGap.exec(null, null, "App", "clearHistory", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
* Note: The user should not have to call this method. Instead, when the user
|
||||
* registers for the "backbutton" event, this is automatically done.
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
App.prototype.overrideBackbutton = function(override) {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [override]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Exit and terminate the application.
|
||||
*/
|
||||
App.prototype.exitApp = function() {
|
||||
return PhoneGap.exec(null, null, "App", "exitApp", []);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.app = new App();
|
||||
});
|
||||
}());
|
||||
}
|
||||
156
framework/assets/js/camera.js
Normal file → Executable file
156
framework/assets/js/camera.js
Normal file → Executable file
@@ -1,41 +1,143 @@
|
||||
/**
|
||||
* This class provides access to the device camera.
|
||||
* @constructor
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
function Camera() {
|
||||
|
||||
}
|
||||
|
||||
if (!PhoneGap.hasResource("camera")) {
|
||||
PhoneGap.addResource("camera");
|
||||
|
||||
/**
|
||||
*
|
||||
* This class provides access to the device camera.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var Camera = function() {
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
this.options = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format of image that returned from getPicture.
|
||||
*
|
||||
* Example: navigator.camera.getPicture(success, fail,
|
||||
* { quality: 80,
|
||||
* destinationType: Camera.DestinationType.DATA_URL,
|
||||
* sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
|
||||
*/
|
||||
Camera.DestinationType = {
|
||||
DATA_URL: 0, // Return base64 encoded string
|
||||
FILE_URI: 1 // Return file uri (content://media/external/images/media/2 for Android)
|
||||
};
|
||||
Camera.prototype.DestinationType = Camera.DestinationType;
|
||||
|
||||
/**
|
||||
* Encoding of image returned from getPicture.
|
||||
*
|
||||
* Example: navigator.camera.getPicture(success, fail,
|
||||
* { quality: 80,
|
||||
* destinationType: Camera.DestinationType.DATA_URL,
|
||||
* sourceType: Camera.PictureSourceType.CAMERA,
|
||||
* encodingType: Camera.EncodingType.PNG})
|
||||
*/
|
||||
Camera.EncodingType = {
|
||||
JPEG: 0, // Return JPEG encoded image
|
||||
PNG: 1 // Return PNG encoded image
|
||||
};
|
||||
Camera.prototype.EncodingType = Camera.EncodingType;
|
||||
|
||||
/**
|
||||
* Source to getPicture from.
|
||||
*
|
||||
* Example: navigator.camera.getPicture(success, fail,
|
||||
* { quality: 80,
|
||||
* destinationType: Camera.DestinationType.DATA_URL,
|
||||
* sourceType: Camera.PictureSourceType.PHOTOLIBRARY})
|
||||
*/
|
||||
Camera.PictureSourceType = {
|
||||
PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
|
||||
CAMERA : 1, // Take picture from camera
|
||||
SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android)
|
||||
};
|
||||
Camera.prototype.PictureSourceType = Camera.PictureSourceType;
|
||||
|
||||
/**
|
||||
* Gets a picture from source defined by "options.sourceType", and returns the
|
||||
* image as defined by the "options.destinationType" option.
|
||||
|
||||
* The defaults are sourceType=CAMERA and destinationType=DATA_URL.
|
||||
*
|
||||
* @param {Function} successCallback
|
||||
* @param {Function} errorCallback
|
||||
* @param {Object} options
|
||||
*/
|
||||
Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
|
||||
|
||||
this.winCallback = successCallback;
|
||||
this.failCallback = errorCallback;
|
||||
if (options.quality)
|
||||
{
|
||||
GapCam.takePicture(options.quality);
|
||||
}
|
||||
else
|
||||
{
|
||||
GapCam.takePicture(80);
|
||||
}
|
||||
}
|
||||
// successCallback required
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Camera Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
Camera.prototype.win = function(picture)
|
||||
{
|
||||
this.winCallback(picture);
|
||||
}
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Camera Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
Camera.prototype.fail = function(err)
|
||||
{
|
||||
this.failCallback(err);
|
||||
}
|
||||
this.options = options;
|
||||
var quality = 80;
|
||||
if (options.quality) {
|
||||
quality = this.options.quality;
|
||||
}
|
||||
|
||||
var maxResolution = 0;
|
||||
if (options.maxResolution) {
|
||||
maxResolution = this.options.maxResolution;
|
||||
}
|
||||
|
||||
var destinationType = Camera.DestinationType.DATA_URL;
|
||||
if (this.options.destinationType) {
|
||||
destinationType = this.options.destinationType;
|
||||
}
|
||||
var sourceType = Camera.PictureSourceType.CAMERA;
|
||||
if (typeof this.options.sourceType === "number") {
|
||||
sourceType = this.options.sourceType;
|
||||
}
|
||||
var encodingType = Camera.EncodingType.JPEG;
|
||||
if (typeof options.encodingType == "number") {
|
||||
encodingType = this.options.encodingType;
|
||||
}
|
||||
|
||||
var targetWidth = -1;
|
||||
if (typeof options.targetWidth == "number") {
|
||||
targetWidth = options.targetWidth;
|
||||
} else if (typeof options.targetWidth == "string") {
|
||||
var width = new Number(options.targetWidth);
|
||||
if (isNaN(width) === false) {
|
||||
targetWidth = width.valueOf();
|
||||
}
|
||||
}
|
||||
|
||||
var targetHeight = -1;
|
||||
if (typeof options.targetHeight == "number") {
|
||||
targetHeight = options.targetHeight;
|
||||
} else if (typeof options.targetHeight == "string") {
|
||||
var height = new Number(options.targetHeight);
|
||||
if (isNaN(height) === false) {
|
||||
targetHeight = height.valueOf();
|
||||
}
|
||||
}
|
||||
|
||||
PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
|
||||
if (typeof navigator.camera === "undefined") {
|
||||
navigator.camera = new Camera();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
191
framework/assets/js/capture.js
Normal file
191
framework/assets/js/capture.js
Normal file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("capture")) {
|
||||
PhoneGap.addResource("capture");
|
||||
|
||||
/**
|
||||
* Represents a single file.
|
||||
*
|
||||
* name {DOMString} name of the file, without path information
|
||||
* fullPath {DOMString} the full path of the file, including the name
|
||||
* type {DOMString} mime type
|
||||
* lastModifiedDate {Date} last modified date
|
||||
* size {Number} size of the file in bytes
|
||||
*/
|
||||
var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
|
||||
this.name = name || null;
|
||||
this.fullPath = fullPath || null;
|
||||
this.type = type || null;
|
||||
this.lastModifiedDate = lastModifiedDate || null;
|
||||
this.size = size || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch device camera application for recording video(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
*/
|
||||
MediaFile.prototype.getFormatData = function(successCallback, errorCallback){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* MediaFileData encapsulates format information of a media file.
|
||||
*
|
||||
* @param {DOMString} codecs
|
||||
* @param {long} bitrate
|
||||
* @param {long} height
|
||||
* @param {long} width
|
||||
* @param {float} duration
|
||||
*/
|
||||
var MediaFileData = function(codecs, bitrate, height, width, duration){
|
||||
this.codecs = codecs || null;
|
||||
this.bitrate = bitrate || 0;
|
||||
this.height = height || 0;
|
||||
this.width = width || 0;
|
||||
this.duration = duration || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The CaptureError interface encapsulates all errors in the Capture API.
|
||||
*/
|
||||
var CaptureError = function(){
|
||||
this.code = null;
|
||||
};
|
||||
|
||||
// Capture error codes
|
||||
CaptureError.CAPTURE_INTERNAL_ERR = 0;
|
||||
CaptureError.CAPTURE_APPLICATION_BUSY = 1;
|
||||
CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
|
||||
CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
|
||||
CaptureError.CAPTURE_NOT_SUPPORTED = 20;
|
||||
|
||||
/**
|
||||
* The Capture interface exposes an interface to the camera and microphone of the hosting device.
|
||||
*/
|
||||
var Capture = function(){
|
||||
this.supportedAudioModes = [];
|
||||
this.supportedImageModes = [];
|
||||
this.supportedVideoModes = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch audio recorder application for recording audio clip(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureAudioOptions} options
|
||||
*/
|
||||
Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureAudio", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch camera application for taking image(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureImageOptions} options
|
||||
*/
|
||||
Capture.prototype.captureImage = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureImage", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch camera application for taking image(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureImageOptions} options
|
||||
*/
|
||||
Capture.prototype._castMediaFile = function(pluginResult){
|
||||
var mediaFiles = [];
|
||||
var i;
|
||||
for (i = 0; i < pluginResult.message.length; i++) {
|
||||
var mediaFile = new MediaFile();
|
||||
mediaFile.name = pluginResult.message[i].name;
|
||||
mediaFile.fullPath = pluginResult.message[i].fullPath;
|
||||
mediaFile.type = pluginResult.message[i].type;
|
||||
mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
|
||||
mediaFile.size = pluginResult.message[i].size;
|
||||
mediaFiles.push(mediaFile);
|
||||
}
|
||||
pluginResult.message = mediaFiles;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch device camera application for recording video(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureVideoOptions} options
|
||||
*/
|
||||
Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureVideo", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates a set of parameters that the capture device supports.
|
||||
*/
|
||||
var ConfigurationData = function(){
|
||||
// The ASCII-encoded string in lower case representing the media type.
|
||||
this.type = null;
|
||||
// The height attribute represents height of the image or video in pixels.
|
||||
// In the case of a sound clip this attribute has value 0.
|
||||
this.height = 0;
|
||||
// The width attribute represents width of the image or video in pixels.
|
||||
// In the case of a sound clip this attribute has value 0
|
||||
this.width = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all image capture operation configuration options.
|
||||
*/
|
||||
var CaptureImageOptions = function(){
|
||||
// Upper limit of images user can take. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// The selected image mode. Must match with one of the elements in supportedImageModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all video capture operation configuration options.
|
||||
*/
|
||||
var CaptureVideoOptions = function(){
|
||||
// Upper limit of videos user can record. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// Maximum duration of a single video clip in seconds.
|
||||
this.duration = 0;
|
||||
// The selected video mode. Must match with one of the elements in supportedVideoModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all audio capture operation configuration options.
|
||||
*/
|
||||
var CaptureAudioOptions = function(){
|
||||
// Upper limit of sound clips user can record. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// Maximum duration of a single sound clip in seconds.
|
||||
this.duration = 0;
|
||||
// The selected audio mode. Must match with one of the elements in supportedAudioModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function(){
|
||||
if (typeof navigator.device === "undefined") {
|
||||
navigator.device = window.device = new Device();
|
||||
}
|
||||
if (typeof navigator.device.capture === "undefined") {
|
||||
navigator.device.capture = window.device.capture = new Capture();
|
||||
}
|
||||
});
|
||||
}
|
||||
151
framework/assets/js/compass.js
Normal file → Executable file
151
framework/assets/js/compass.js
Normal file → Executable file
@@ -1,96 +1,119 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("compass")) {
|
||||
PhoneGap.addResource("compass");
|
||||
|
||||
/**
|
||||
* This class provides access to device Compass data.
|
||||
* @constructor
|
||||
*/
|
||||
function Compass() {
|
||||
var Compass = function() {
|
||||
/**
|
||||
* The last known Compass position.
|
||||
*/
|
||||
this.lastHeading = null;
|
||||
this.lastError = null;
|
||||
this.callbacks = {
|
||||
onHeadingChanged: [],
|
||||
onError: []
|
||||
};
|
||||
this.lastHeading = null;
|
||||
|
||||
/**
|
||||
* List of compass watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
};
|
||||
|
||||
Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
/**
|
||||
* Asynchronously aquires the current heading.
|
||||
* @param {Function} successCallback The function to call when the heading
|
||||
* data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error
|
||||
* getting the heading data.
|
||||
* @param {PositionOptions} options The options for getting the heading data
|
||||
* such as timeout.
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the heading data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
|
||||
* @param {PositionOptions} options The options for getting the heading data such as timeout. (OPTIONAL)
|
||||
*/
|
||||
Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
|
||||
if (this.lastHeading == null) {
|
||||
this.start(options);
|
||||
}
|
||||
else
|
||||
if (typeof successCallback == "function") {
|
||||
successCallback(this.lastHeading);
|
||||
}
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Compass Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Compass Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get heading
|
||||
PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously aquires the heading repeatedly at a given interval.
|
||||
* @param {Function} successCallback The function to call each time the heading
|
||||
* data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error
|
||||
* getting the heading data.
|
||||
* @param {HeadingOptions} options The options for getting the heading data
|
||||
* such as timeout and the frequency of the watch.
|
||||
*
|
||||
* @param {Function} successCallback The function to call each time the heading data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
|
||||
* @param {HeadingOptions} options The options for getting the heading data such as timeout and the frequency of the watch. (OPTIONAL)
|
||||
* @return String The watch id that must be passed to #clearWatch to stop watching.
|
||||
*/
|
||||
Compass.prototype.watchHeading= function(successCallback, errorCallback, options) {
|
||||
// Invoke the appropriate callback with a new Position object every time the implementation
|
||||
// determines that the position of the hosting device has changed.
|
||||
|
||||
this.getCurrentHeading(successCallback, errorCallback, options);
|
||||
var frequency = 100;
|
||||
if (typeof(options) == 'object' && options.frequency)
|
||||
frequency = options.frequency;
|
||||
|
||||
var self = this;
|
||||
return setInterval(function() {
|
||||
self.getCurrentHeading(successCallback, errorCallback, options);
|
||||
}, frequency);
|
||||
// Default interval (100 msec)
|
||||
var frequency = (options !== undefined) ? options.frequency : 100;
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Compass Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Compass Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure compass timeout > frequency + 10 sec
|
||||
PhoneGap.exec(
|
||||
function(timeout) {
|
||||
if (timeout < (frequency + 10000)) {
|
||||
PhoneGap.exec(null, null, "Compass", "setTimeout", [frequency + 10000]);
|
||||
}
|
||||
},
|
||||
function(e) { }, "Compass", "getTimeout", []);
|
||||
|
||||
// Start watch timer to get headings
|
||||
var id = PhoneGap.createUUID();
|
||||
navigator.compass.timers[id] = setInterval(
|
||||
function() {
|
||||
PhoneGap.exec(successCallback, errorCallback, "Compass", "getHeading", []);
|
||||
}, (frequency ? frequency : 1));
|
||||
|
||||
return id;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Clears the specified heading watch.
|
||||
* @param {String} watchId The ID of the watch returned from #watchHeading.
|
||||
*
|
||||
* @param {String} id The ID of the watch returned from #watchHeading.
|
||||
*/
|
||||
Compass.prototype.clearWatch = function(watchId) {
|
||||
clearInterval(watchId);
|
||||
};
|
||||
Compass.prototype.clearWatch = function(id) {
|
||||
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when the current heading is found.
|
||||
* @param {HeadingOptions} position The current heading.
|
||||
*/
|
||||
Compass.prototype.setHeading = function(heading) {
|
||||
this.lastHeading = heading;
|
||||
for (var i = 0; i < this.callbacks.onHeadingChanged.length; i++) {
|
||||
var f = this.callbacks.onHeadingChanged.shift();
|
||||
f(heading);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when an error occurs while looking up the current position.
|
||||
* @param {String} message The text of the error message.
|
||||
*/
|
||||
Compass.prototype.setError = function(message) {
|
||||
this.lastError = message;
|
||||
for (var i = 0; i < this.callbacks.onError.length; i++) {
|
||||
var f = this.callbacks.onError.shift();
|
||||
f(message);
|
||||
// Stop javascript timer & remove from timer list
|
||||
if (id && navigator.compass.timers[id]) {
|
||||
clearInterval(navigator.compass.timers[id]);
|
||||
delete navigator.compass.timers[id];
|
||||
}
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.compass == "undefined") navigator.compass = new Compass();
|
||||
if (typeof navigator.compass === "undefined") {
|
||||
navigator.compass = new Compass();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
367
framework/assets/js/contact.js
Normal file → Executable file
367
framework/assets/js/contact.js
Normal file → Executable file
@@ -1,81 +1,310 @@
|
||||
var Contact = function(){
|
||||
this.name = new ContactName();
|
||||
this.emails = [];
|
||||
this.phones = [];
|
||||
}
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
var ContactName = function()
|
||||
{
|
||||
this.formatted = "";
|
||||
this.familyName = "";
|
||||
this.givenName = "";
|
||||
this.additionalNames = [];
|
||||
this.prefixes = [];
|
||||
this.suffixes = [];
|
||||
}
|
||||
if (!PhoneGap.hasResource("contact")) {
|
||||
PhoneGap.addResource("contact");
|
||||
|
||||
/**
|
||||
* Contains information about a single contact.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier
|
||||
* @param {DOMString} displayName
|
||||
* @param {ContactName} name
|
||||
* @param {DOMString} nickname
|
||||
* @param {Array.<ContactField>} phoneNumbers array of phone numbers
|
||||
* @param {Array.<ContactField>} emails array of email addresses
|
||||
* @param {Array.<ContactAddress>} addresses array of addresses
|
||||
* @param {Array.<ContactField>} ims instant messaging user ids
|
||||
* @param {Array.<ContactOrganization>} organizations
|
||||
* @param {DOMString} birthday contact's birthday
|
||||
* @param {DOMString} note user notes about contact
|
||||
* @param {Array.<ContactField>} photos
|
||||
* @param {Array.<ContactField>} categories
|
||||
* @param {Array.<ContactField>} urls contact's web sites
|
||||
*/
|
||||
var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
|
||||
ims, organizations, birthday, note, photos, categories, urls) {
|
||||
this.id = id || null;
|
||||
this.rawId = null;
|
||||
this.displayName = displayName || null;
|
||||
this.name = name || null; // ContactName
|
||||
this.nickname = nickname || null;
|
||||
this.phoneNumbers = phoneNumbers || null; // ContactField[]
|
||||
this.emails = emails || null; // ContactField[]
|
||||
this.addresses = addresses || null; // ContactAddress[]
|
||||
this.ims = ims || null; // ContactField[]
|
||||
this.organizations = organizations || null; // ContactOrganization[]
|
||||
this.birthday = birthday || null;
|
||||
this.note = note || null;
|
||||
this.photos = photos || null; // ContactField[]
|
||||
this.categories = categories || null; // ContactField[]
|
||||
this.urls = urls || null; // ContactField[]
|
||||
};
|
||||
|
||||
var ContactEmail = function()
|
||||
{
|
||||
this.types = [];
|
||||
this.address = "";
|
||||
}
|
||||
/**
|
||||
* ContactError.
|
||||
* An error code assigned by an implementation when an error has occurreds
|
||||
* @constructor
|
||||
*/
|
||||
var ContactError = function() {
|
||||
this.code=null;
|
||||
};
|
||||
|
||||
var ContactPhoneNumber = function()
|
||||
{
|
||||
this.types = [];
|
||||
this.number = "";
|
||||
}
|
||||
/**
|
||||
* Error codes
|
||||
*/
|
||||
ContactError.UNKNOWN_ERROR = 0;
|
||||
ContactError.INVALID_ARGUMENT_ERROR = 1;
|
||||
ContactError.TIMEOUT_ERROR = 2;
|
||||
ContactError.PENDING_OPERATION_ERROR = 3;
|
||||
ContactError.IO_ERROR = 4;
|
||||
ContactError.NOT_SUPPORTED_ERROR = 5;
|
||||
ContactError.PERMISSION_DENIED_ERROR = 20;
|
||||
|
||||
/**
|
||||
* Removes contact from device storage.
|
||||
* @param successCB success callback
|
||||
* @param errorCB error callback
|
||||
*/
|
||||
Contact.prototype.remove = function(successCB, errorCB) {
|
||||
if (this.id === null) {
|
||||
var errorObj = new ContactError();
|
||||
errorObj.code = ContactError.UNKNOWN_ERROR;
|
||||
errorCB(errorObj);
|
||||
}
|
||||
else {
|
||||
PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]);
|
||||
}
|
||||
};
|
||||
|
||||
var Contacts = function()
|
||||
{
|
||||
this.records = [];
|
||||
}
|
||||
/**
|
||||
* Creates a deep copy of this Contact.
|
||||
* With the contact ID set to null.
|
||||
* @return copy of this Contact
|
||||
*/
|
||||
Contact.prototype.clone = function() {
|
||||
var clonedContact = PhoneGap.clone(this);
|
||||
var i;
|
||||
clonedContact.id = null;
|
||||
clonedContact.rawId = null;
|
||||
// Loop through and clear out any id's in phones, emails, etc.
|
||||
if (clonedContact.phoneNumbers) {
|
||||
for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
|
||||
clonedContact.phoneNumbers[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.emails) {
|
||||
for (i = 0; i < clonedContact.emails.length; i++) {
|
||||
clonedContact.emails[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.addresses) {
|
||||
for (i = 0; i < clonedContact.addresses.length; i++) {
|
||||
clonedContact.addresses[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.ims) {
|
||||
for (i = 0; i < clonedContact.ims.length; i++) {
|
||||
clonedContact.ims[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.organizations) {
|
||||
for (i = 0; i < clonedContact.organizations.length; i++) {
|
||||
clonedContact.organizations[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.tags) {
|
||||
for (i = 0; i < clonedContact.tags.length; i++) {
|
||||
clonedContact.tags[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.photos) {
|
||||
for (i = 0; i < clonedContact.photos.length; i++) {
|
||||
clonedContact.photos[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.urls) {
|
||||
for (i = 0; i < clonedContact.urls.length; i++) {
|
||||
clonedContact.urls[i].id = null;
|
||||
}
|
||||
}
|
||||
return clonedContact;
|
||||
};
|
||||
|
||||
Contacts.prototype.find = function(obj, win, fail)
|
||||
{
|
||||
if(obj.name != null)
|
||||
{
|
||||
// Build up the search term that we'll use in SQL, based on the structure/contents of the contact object passed into find.
|
||||
var searchTerm = '';
|
||||
if (obj.name.givenName && obj.name.givenName.length > 0) {
|
||||
searchTerm = obj.name.givenName.split(' ').join('%');
|
||||
}
|
||||
if (obj.name.familyName && obj.name.familyName.length > 0) {
|
||||
searchTerm += obj.name.familyName.split(' ').join('%');
|
||||
}
|
||||
if (!obj.name.familyName && !obj.name.givenName && obj.name.formatted) {
|
||||
searchTerm = obj.name.formatted;
|
||||
}
|
||||
ContactHook.search(searchTerm, "", "");
|
||||
}
|
||||
this.win = win;
|
||||
this.fail = fail;
|
||||
}
|
||||
/**
|
||||
* Persists contact to device storage.
|
||||
* @param successCB success callback
|
||||
* @param errorCB error callback
|
||||
*/
|
||||
Contact.prototype.save = function(successCB, errorCB) {
|
||||
PhoneGap.exec(successCB, errorCB, "Contacts", "save", [this]);
|
||||
};
|
||||
|
||||
Contacts.prototype.droidFoundContact = function(name, npa, email)
|
||||
{
|
||||
var contact = new Contact();
|
||||
contact.name = new ContactName();
|
||||
contact.name.formatted = name;
|
||||
contact.name.givenName = name;
|
||||
var mail = new ContactEmail();
|
||||
mail.types.push("home");
|
||||
mail.address = email;
|
||||
contact.emails.push(mail);
|
||||
phone = new ContactPhoneNumber();
|
||||
phone.types.push("home");
|
||||
phone.number = npa;
|
||||
contact.phones.push(phone);
|
||||
this.records.push(contact);
|
||||
}
|
||||
/**
|
||||
* Contact name.
|
||||
* @constructor
|
||||
* @param formatted
|
||||
* @param familyName
|
||||
* @param givenName
|
||||
* @param middle
|
||||
* @param prefix
|
||||
* @param suffix
|
||||
*/
|
||||
var ContactName = function(formatted, familyName, givenName, middle, prefix, suffix) {
|
||||
this.formatted = formatted || null;
|
||||
this.familyName = familyName || null;
|
||||
this.givenName = givenName || null;
|
||||
this.middleName = middle || null;
|
||||
this.honorificPrefix = prefix || null;
|
||||
this.honorificSuffix = suffix || null;
|
||||
};
|
||||
|
||||
Contacts.prototype.droidDone = function()
|
||||
{
|
||||
this.win(this.records);
|
||||
}
|
||||
/**
|
||||
* Generic contact field.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param type
|
||||
* @param value
|
||||
* @param pref
|
||||
*/
|
||||
var ContactField = function(type, value, pref) {
|
||||
this.id = null;
|
||||
this.type = type || null;
|
||||
this.value = value || null;
|
||||
this.pref = pref || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contact address.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param formatted
|
||||
* @param streetAddress
|
||||
* @param locality
|
||||
* @param region
|
||||
* @param postalCode
|
||||
* @param country
|
||||
*/
|
||||
var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
|
||||
this.id = null;
|
||||
this.pref = pref || null;
|
||||
this.type = type || null;
|
||||
this.formatted = formatted || null;
|
||||
this.streetAddress = streetAddress || null;
|
||||
this.locality = locality || null;
|
||||
this.region = region || null;
|
||||
this.postalCode = postalCode || null;
|
||||
this.country = country || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contact organization.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param name
|
||||
* @param dept
|
||||
* @param title
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
* @param location
|
||||
* @param desc
|
||||
*/
|
||||
var ContactOrganization = function(pref, type, name, dept, title) {
|
||||
this.id = null;
|
||||
this.pref = pref || null;
|
||||
this.type = type || null;
|
||||
this.name = name || null;
|
||||
this.department = dept || null;
|
||||
this.title = title || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a group of Contacts.
|
||||
* @constructor
|
||||
*/
|
||||
var Contacts = function() {
|
||||
this.inProgress = false;
|
||||
this.records = [];
|
||||
};
|
||||
/**
|
||||
* Returns an array of Contacts matching the search criteria.
|
||||
* @param fields that should be searched
|
||||
* @param successCB success callback
|
||||
* @param errorCB error callback
|
||||
* @param {ContactFindOptions} options that can be applied to contact searching
|
||||
* @return array of Contacts matching search criteria
|
||||
*/
|
||||
Contacts.prototype.find = function(fields, successCB, errorCB, options) {
|
||||
if (successCB === null) {
|
||||
throw new TypeError("You must specify a success callback for the find command.");
|
||||
}
|
||||
if (fields === null || fields === "undefined" || fields.length === "undefined" || fields.length <= 0) {
|
||||
if (typeof errorCB === "function") {
|
||||
errorCB({"code": ContactError.INVALID_ARGUMENT_ERROR});
|
||||
}
|
||||
} else {
|
||||
PhoneGap.exec(successCB, errorCB, "Contacts", "search", [fields, options]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function creates a new contact, but it does not persist the contact
|
||||
* to device storage. To persist the contact to device storage, invoke
|
||||
* contact.save().
|
||||
* @param properties an object who's properties will be examined to create a new Contact
|
||||
* @returns new Contact object
|
||||
*/
|
||||
Contacts.prototype.create = function(properties) {
|
||||
var i;
|
||||
var contact = new Contact();
|
||||
for (i in properties) {
|
||||
if (contact[i] !== 'undefined') {
|
||||
contact[i] = properties[i];
|
||||
}
|
||||
}
|
||||
return contact;
|
||||
};
|
||||
|
||||
/**
|
||||
* This function returns and array of contacts. It is required as we need to convert raw
|
||||
* JSON objects into concrete Contact objects. Currently this method is called after
|
||||
* navigator.contacts.find but before the find methods success call back.
|
||||
*
|
||||
* @param jsonArray an array of JSON Objects that need to be converted to Contact objects.
|
||||
* @returns an array of Contact objects
|
||||
*/
|
||||
Contacts.prototype.cast = function(pluginResult) {
|
||||
var contacts = [];
|
||||
var i;
|
||||
for (i=0; i<pluginResult.message.length; i++) {
|
||||
contacts.push(navigator.contacts.create(pluginResult.message[i]));
|
||||
}
|
||||
pluginResult.message = contacts;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* ContactFindOptions.
|
||||
* @constructor
|
||||
* @param filter used to match contacts against
|
||||
* @param multiple boolean used to determine if more than one contact should be returned
|
||||
*/
|
||||
var ContactFindOptions = function(filter, multiple) {
|
||||
this.filter = filter || '';
|
||||
this.multiple = multiple || false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the contact interface into the browser.
|
||||
*/
|
||||
PhoneGap.addConstructor(function() {
|
||||
if(typeof navigator.contacts == "undefined") navigator.contacts = new Contacts();
|
||||
if(typeof navigator.contacts === "undefined") {
|
||||
navigator.contacts = new Contacts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
62
framework/assets/js/crypto.js
Normal file → Executable file
62
framework/assets/js/crypto.js
Normal file → Executable file
@@ -1,33 +1,43 @@
|
||||
var Crypto = function()
|
||||
{
|
||||
}
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
Crypto.prototype.encrypt = function(seed, string, callback)
|
||||
{
|
||||
GapCrypto.encrypt(seed, string);
|
||||
this.encryptWin = callback;
|
||||
}
|
||||
// TODO: Needs to be commented
|
||||
|
||||
Crypto.prototype.decrypt = function(seed, string, callback)
|
||||
{
|
||||
GapCrypto.decrypt(seed, string);
|
||||
this.decryptWin = callback;
|
||||
}
|
||||
if (!PhoneGap.hasResource("crypto")) {
|
||||
PhoneGap.addResource("crypto");
|
||||
|
||||
Crypto.prototype.gotCryptedString = function(string)
|
||||
{
|
||||
this.encryptWin(string);
|
||||
}
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var Crypto = function() {
|
||||
};
|
||||
|
||||
Crypto.prototype.getPlainString = function(string)
|
||||
{
|
||||
this.decryptWin(string);
|
||||
}
|
||||
Crypto.prototype.encrypt = function(seed, string, callback) {
|
||||
this.encryptWin = callback;
|
||||
PhoneGap.exec(null, null, "Crypto", "encrypt", [seed, string]);
|
||||
};
|
||||
|
||||
Crypto.prototype.decrypt = function(seed, string, callback) {
|
||||
this.decryptWin = callback;
|
||||
PhoneGap.exec(null, null, "Crypto", "decrypt", [seed, string]);
|
||||
};
|
||||
|
||||
Crypto.prototype.gotCryptedString = function(string) {
|
||||
this.encryptWin(string);
|
||||
};
|
||||
|
||||
Crypto.prototype.getPlainString = function(string) {
|
||||
this.decryptWin(string);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.Crypto == "undefined")
|
||||
{
|
||||
navigator.Crypto = new Crypto();
|
||||
}
|
||||
if (typeof navigator.Crypto === "undefined") {
|
||||
navigator.Crypto = new Crypto();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
118
framework/assets/js/device.js
Normal file → Executable file
118
framework/assets/js/device.js
Normal file → Executable file
@@ -1,33 +1,105 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("device")) {
|
||||
PhoneGap.addResource("device");
|
||||
|
||||
/**
|
||||
* this represents the mobile device, and provides properties for inspecting the model, version, UUID of the
|
||||
* This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
|
||||
* phone, etc.
|
||||
* @constructor
|
||||
*/
|
||||
function Device() {
|
||||
var Device = function() {
|
||||
this.available = PhoneGap.available;
|
||||
this.platform = null;
|
||||
this.version = null;
|
||||
this.name = null;
|
||||
this.gap = null;
|
||||
this.uuid = null;
|
||||
try {
|
||||
if (window.DroidGap) {
|
||||
this.available = true;
|
||||
this.uuid = window.DroidGap.getUuid();
|
||||
this.version = window.DroidGap.getOSVersion();
|
||||
this.gapVersion = window.DroidGap.getVersion();
|
||||
this.platform = window.DroidGap.getPlatform();
|
||||
this.name = window.DroidGap.getProductName();
|
||||
this.line1Number = window.DroidGap.getLine1Number();
|
||||
this.deviceId = window.DroidGap.getDeviceId();
|
||||
this.simSerialNumber = window.DroidGap.getSimSerialNumber();
|
||||
this.subscriberId = window.DroidGap.getSubscriberId();
|
||||
}
|
||||
} catch(e) {
|
||||
this.available = false;
|
||||
this.version = null;
|
||||
this.name = null;
|
||||
this.uuid = null;
|
||||
this.phonegap = null;
|
||||
|
||||
var me = this;
|
||||
this.getInfo(
|
||||
function(info) {
|
||||
me.available = true;
|
||||
me.platform = info.platform;
|
||||
me.version = info.version;
|
||||
me.name = info.name;
|
||||
me.uuid = info.uuid;
|
||||
me.phonegap = info.phonegap;
|
||||
PhoneGap.onPhoneGapInfoReady.fire();
|
||||
},
|
||||
function(e) {
|
||||
me.available = false;
|
||||
console.log("Error initializing PhoneGap: " + e);
|
||||
alert("Error initializing PhoneGap: "+e);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get device info
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the heading data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the heading data. (OPTIONAL)
|
||||
*/
|
||||
Device.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Device Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Device Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get info
|
||||
PhoneGap.exec(successCallback, errorCallback, "Device", "getDeviceInfo", []);
|
||||
};
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
* This is only for Android.
|
||||
*
|
||||
* You must explicitly override the back button.
|
||||
*/
|
||||
Device.prototype.overrideBackButton = function() {
|
||||
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
|
||||
navigator.app.overrideBackbutton(true);
|
||||
};
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
* This is only for Android.
|
||||
*
|
||||
* This resets the back button to the default behaviour
|
||||
*/
|
||||
Device.prototype.resetBackButton = function() {
|
||||
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
|
||||
navigator.app.overrideBackbutton(false);
|
||||
};
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
* This is only for Android.
|
||||
*
|
||||
* This terminates the activity!
|
||||
*/
|
||||
Device.prototype.exitApp = function() {
|
||||
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
|
||||
navigator.app.exitApp();
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.device = window.device = new Device();
|
||||
if (typeof navigator.device === "undefined") {
|
||||
navigator.device = window.device = new Device();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
1245
framework/assets/js/file.js
Normal file → Executable file
1245
framework/assets/js/file.js
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
85
framework/assets/js/filetransfer.js
Normal file
85
framework/assets/js/filetransfer.js
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("filetransfer")) {
|
||||
PhoneGap.addResource("filetransfer");
|
||||
|
||||
/**
|
||||
* FileTransfer uploads a file to a remote server.
|
||||
* @constructor
|
||||
*/
|
||||
var FileTransfer = function() {};
|
||||
|
||||
/**
|
||||
* FileUploadResult
|
||||
* @constructor
|
||||
*/
|
||||
var FileUploadResult = function() {
|
||||
this.bytesSent = 0;
|
||||
this.responseCode = null;
|
||||
this.response = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* FileTransferError
|
||||
* @constructor
|
||||
*/
|
||||
var FileTransferError = function() {
|
||||
this.code = null;
|
||||
};
|
||||
|
||||
FileTransferError.FILE_NOT_FOUND_ERR = 1;
|
||||
FileTransferError.INVALID_URL_ERR = 2;
|
||||
FileTransferError.CONNECTION_ERR = 3;
|
||||
|
||||
/**
|
||||
* Given an absolute file path, uploads a file on the device to a remote server
|
||||
* using a multipart HTTP request.
|
||||
* @param filePath {String} Full path of the file on the device
|
||||
* @param server {String} URL of the server to receive the file
|
||||
* @param successCallback (Function} Callback to be invoked when upload has completed
|
||||
* @param errorCallback {Function} Callback to be invoked upon error
|
||||
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
|
||||
*/
|
||||
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) {
|
||||
|
||||
// check for options
|
||||
var fileKey = null;
|
||||
var fileName = null;
|
||||
var mimeType = null;
|
||||
var params = null;
|
||||
if (options) {
|
||||
fileKey = options.fileKey;
|
||||
fileName = options.fileName;
|
||||
mimeType = options.mimeType;
|
||||
if (options.params) {
|
||||
params = options.params;
|
||||
}
|
||||
else {
|
||||
params = {};
|
||||
}
|
||||
}
|
||||
|
||||
PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Options to customize the HTTP request used to upload files.
|
||||
* @constructor
|
||||
* @param fileKey {String} Name of file request parameter.
|
||||
* @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
|
||||
* @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
|
||||
* @param params {Object} Object with key: value params to send to the server.
|
||||
*/
|
||||
var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
|
||||
this.fileKey = fileKey || null;
|
||||
this.fileName = fileName || null;
|
||||
this.mimeType = mimeType || null;
|
||||
this.params = params || null;
|
||||
};
|
||||
}
|
||||
264
framework/assets/js/geolocation.js
Normal file → Executable file
264
framework/assets/js/geolocation.js
Normal file → Executable file
@@ -1,90 +1,198 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("geolocation")) {
|
||||
PhoneGap.addResource("geolocation");
|
||||
|
||||
/**
|
||||
* This class provides access to device GPS data.
|
||||
* @constructor
|
||||
*/
|
||||
function Geolocation() {
|
||||
/**
|
||||
* The last known GPS position.
|
||||
*/
|
||||
var Geolocation = function() {
|
||||
|
||||
// The last known GPS position.
|
||||
this.lastPosition = null;
|
||||
this.lastError = null;
|
||||
this.listeners = null;
|
||||
|
||||
// Geolocation listeners
|
||||
this.listeners = {};
|
||||
};
|
||||
|
||||
var geoListeners = [];
|
||||
|
||||
Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options)
|
||||
{
|
||||
var position = Geo.getCurrentLocation();
|
||||
this.global_success = successCallback;
|
||||
this.fail = errorCallback;
|
||||
}
|
||||
|
||||
// Run the global callback
|
||||
Geolocation.prototype.gotCurrentPosition = function(lat, lng, alt, altacc, head, vel, stamp)
|
||||
{
|
||||
if (lat == "undefined" || lng == "undefined")
|
||||
{
|
||||
this.fail();
|
||||
}
|
||||
else
|
||||
{
|
||||
coords = new Coordinates(lat, lng, alt, altacc, head, vel);
|
||||
loc = new Position(coords, stamp);
|
||||
this.lastPosition = loc;
|
||||
this.global_success(loc);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This turns on the GeoLocator class, which has two listeners.
|
||||
* The listeners have their own timeouts, and run independently of this process
|
||||
* In this case, we return the key to the watch hash
|
||||
*/
|
||||
|
||||
Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options)
|
||||
{
|
||||
var frequency = (options != undefined)? options.frequency : 10000;
|
||||
|
||||
var key = geoListeners.push( {"success" : successCallback, "fail" : errorCallback }) - 1;
|
||||
|
||||
// TO-DO: Get the names of the method and pass them as strings to the Java.
|
||||
return Geo.start(frequency, key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve and stop this listener from listening to the GPS
|
||||
/**
|
||||
* Position error object
|
||||
*
|
||||
* @constructor
|
||||
* @param code
|
||||
* @param message
|
||||
*/
|
||||
Geolocation.prototype.success = function(key, lat, lng, alt, altacc, head, vel, stamp)
|
||||
{
|
||||
var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
|
||||
var loc = new Position(coords, stamp);
|
||||
geoListeners[key].success(loc);
|
||||
}
|
||||
var PositionError = function(code, message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
};
|
||||
|
||||
Geolocation.prototype.fail = function(key)
|
||||
{
|
||||
geoListeners[key].fail();
|
||||
}
|
||||
|
||||
Geolocation.prototype.clearWatch = function(watchId)
|
||||
{
|
||||
Geo.stop(watchId);
|
||||
}
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
PositionError.TIMEOUT = 3;
|
||||
|
||||
/**
|
||||
* Asynchronously aquires the current position.
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the position data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the heading position. (OPTIONAL)
|
||||
* @param {PositionOptions} options The options for getting the position data. (OPTIONAL)
|
||||
*/
|
||||
Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) {
|
||||
if (navigator._geo.listeners.global) {
|
||||
console.log("Geolocation Error: Still waiting for previous getCurrentPosition() request.");
|
||||
try {
|
||||
errorCallback(new PositionError(PositionError.TIMEOUT, "Geolocation Error: Still waiting for previous getCurrentPosition() request."));
|
||||
} catch (e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
var maximumAge = 10000;
|
||||
var enableHighAccuracy = false;
|
||||
var timeout = 10000;
|
||||
if (typeof options !== "undefined") {
|
||||
if (typeof options.maximumAge !== "undefined") {
|
||||
maximumAge = options.maximumAge;
|
||||
}
|
||||
if (typeof options.enableHighAccuracy !== "undefined") {
|
||||
enableHighAccuracy = options.enableHighAccuracy;
|
||||
}
|
||||
if (typeof options.timeout !== "undefined") {
|
||||
timeout = options.timeout;
|
||||
}
|
||||
}
|
||||
navigator._geo.listeners.global = {"success" : successCallback, "fail" : errorCallback };
|
||||
PhoneGap.exec(null, null, "Geolocation", "getCurrentLocation", [enableHighAccuracy, timeout, maximumAge]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously watches the geolocation for changes to geolocation. When a change occurs,
|
||||
* the successCallback is called with the new location.
|
||||
*
|
||||
* @param {Function} successCallback The function to call each time the location data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the location data. (OPTIONAL)
|
||||
* @param {PositionOptions} options The options for getting the location data such as frequency. (OPTIONAL)
|
||||
* @return String The watch id that must be passed to #clearWatch to stop watching.
|
||||
*/
|
||||
Geolocation.prototype.watchPosition = function(successCallback, errorCallback, options) {
|
||||
var maximumAge = 10000;
|
||||
var enableHighAccuracy = false;
|
||||
var timeout = 10000;
|
||||
if (typeof options !== "undefined") {
|
||||
if (typeof options.frequency !== "undefined") {
|
||||
maximumAge = options.frequency;
|
||||
}
|
||||
if (typeof options.maximumAge !== "undefined") {
|
||||
maximumAge = options.maximumAge;
|
||||
}
|
||||
if (typeof options.enableHighAccuracy !== "undefined") {
|
||||
enableHighAccuracy = options.enableHighAccuracy;
|
||||
}
|
||||
if (typeof options.timeout !== "undefined") {
|
||||
timeout = options.timeout;
|
||||
}
|
||||
}
|
||||
var id = PhoneGap.createUUID();
|
||||
navigator._geo.listeners[id] = {"success" : successCallback, "fail" : errorCallback };
|
||||
PhoneGap.exec(null, null, "Geolocation", "start", [id, enableHighAccuracy, timeout, maximumAge]);
|
||||
return id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Native callback when watch position has a new position.
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param {String} id
|
||||
* @param {Number} lat
|
||||
* @param {Number} lng
|
||||
* @param {Number} alt
|
||||
* @param {Number} altacc
|
||||
* @param {Number} head
|
||||
* @param {Number} vel
|
||||
* @param {Number} stamp
|
||||
*/
|
||||
Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, stamp) {
|
||||
var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
|
||||
var loc = new Position(coords, stamp);
|
||||
try {
|
||||
if (lat === "undefined" || lng === "undefined") {
|
||||
navigator._geo.listeners[id].fail(new PositionError(PositionError.POSITION_UNAVAILABLE, "Lat/Lng are undefined."));
|
||||
}
|
||||
else {
|
||||
navigator._geo.lastPosition = loc;
|
||||
navigator._geo.listeners[id].success(loc);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Geolocation Error: Error calling success callback function.");
|
||||
}
|
||||
|
||||
if (id === "global") {
|
||||
delete navigator._geo.listeners.global;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Native callback when watch position has an error.
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param {String} id The ID of the watch
|
||||
* @param {Number} code The error code
|
||||
* @param {String} msg The error message
|
||||
*/
|
||||
Geolocation.prototype.fail = function(id, code, msg) {
|
||||
try {
|
||||
navigator._geo.listeners[id].fail(new PositionError(code, msg));
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Geolocation Error: Error calling error callback function.");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the specified heading watch.
|
||||
*
|
||||
* @param {String} id The ID of the watch returned from #watchPosition
|
||||
*/
|
||||
Geolocation.prototype.clearWatch = function(id) {
|
||||
PhoneGap.exec(null, null, "Geolocation", "stop", [id]);
|
||||
delete navigator._geo.listeners[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Force the PhoneGap geolocation to be used instead of built-in.
|
||||
*/
|
||||
Geolocation.usingPhoneGap = false;
|
||||
Geolocation.usePhoneGap = function() {
|
||||
if (Geolocation.usingPhoneGap) {
|
||||
return;
|
||||
}
|
||||
Geolocation.usingPhoneGap = true;
|
||||
|
||||
// Set built-in geolocation methods to our own implementations
|
||||
// (Cannot replace entire geolocation, but can replace individual methods)
|
||||
navigator.geolocation.setLocation = navigator._geo.setLocation;
|
||||
navigator.geolocation.getCurrentPosition = navigator._geo.getCurrentPosition;
|
||||
navigator.geolocation.watchPosition = navigator._geo.watchPosition;
|
||||
navigator.geolocation.clearWatch = navigator._geo.clearWatch;
|
||||
navigator.geolocation.start = navigator._geo.start;
|
||||
navigator.geolocation.stop = navigator._geo.stop;
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
// Taken from Jesse's geo fix (similar problem) in PhoneGap iPhone. Go figure, same browser!
|
||||
function __proxyObj(origObj, proxyObj, funkList) {
|
||||
for (var v in funkList) {
|
||||
origObj[funkList[v]] = proxyObj[funkList[v]];
|
||||
}
|
||||
}
|
||||
// In case a native geolocation object exists, proxy the native one over to a diff object so that we can overwrite the native implementation.
|
||||
if (typeof navigator.geolocation != 'undefined') {
|
||||
navigator._geo = new Geolocation();
|
||||
__proxyObj(navigator.geolocation, navigator._geo, ["setLocation", "getCurrentPosition", "watchPosition", "clearWatch", "setError", "start", "stop", "gotCurrentPosition"]);
|
||||
} else {
|
||||
navigator.geolocation = new Geolocation();
|
||||
}
|
||||
});
|
||||
navigator._geo = new Geolocation();
|
||||
|
||||
// No native geolocation object for Android 1.x, so use PhoneGap geolocation
|
||||
if (typeof navigator.geolocation === 'undefined') {
|
||||
navigator.geolocation = navigator._geo;
|
||||
Geolocation.usingPhoneGap = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
7
framework/assets/js/header.txt
Executable file
7
framework/assets/js/header.txt
Executable file
@@ -0,0 +1,7 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
@@ -1,22 +0,0 @@
|
||||
function KeyEvent()
|
||||
{
|
||||
}
|
||||
|
||||
KeyEvent.prototype.menuTrigger = function()
|
||||
{
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('menuKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
|
||||
KeyEvent.prototype.searchTrigger= function()
|
||||
{
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('searchKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
|
||||
if (document.keyEvent == null || typeof document.keyEvent == 'undefined')
|
||||
{
|
||||
window.keyEvent = document.keyEvent = new KeyEvent();
|
||||
}
|
||||
230
framework/assets/js/media.js
Normal file → Executable file
230
framework/assets/js/media.js
Normal file → Executable file
@@ -1,62 +1,214 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("media")) {
|
||||
PhoneGap.addResource("media");
|
||||
|
||||
/**
|
||||
* This class provides access to the device media, interfaces to both sound and video
|
||||
*
|
||||
* @constructor
|
||||
* @param src The file name or url to play
|
||||
* @param successCallback The callback to be called when the file is done playing or recording.
|
||||
* successCallback() - OPTIONAL
|
||||
* @param errorCallback The callback to be called if there is an error.
|
||||
* errorCallback(int errorCode) - OPTIONAL
|
||||
* @param statusCallback The callback to be called when media status has changed.
|
||||
* statusCallback(int statusCode) - OPTIONAL
|
||||
* @param positionCallback The callback to be called when media position has changed.
|
||||
* positionCallback(long position) - OPTIONAL
|
||||
*/
|
||||
function Media(src, successCallback, errorCallback) {
|
||||
this.src = src;
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
}
|
||||
var Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
|
||||
|
||||
Media.prototype.record = function() {
|
||||
}
|
||||
// successCallback optional
|
||||
if (successCallback && (typeof successCallback !== "function")) {
|
||||
console.log("Media Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
Media.prototype.play = function() {
|
||||
}
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Media Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
Media.prototype.pause = function() {
|
||||
}
|
||||
// statusCallback optional
|
||||
if (statusCallback && (typeof statusCallback !== "function")) {
|
||||
console.log("Media Error: statusCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
Media.prototype.stop = function() {
|
||||
}
|
||||
// statusCallback optional
|
||||
if (positionCallback && (typeof positionCallback !== "function")) {
|
||||
console.log("Media Error: positionCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
this.id = PhoneGap.createUUID();
|
||||
PhoneGap.mediaObjects[this.id] = this;
|
||||
this.src = src;
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
this.statusCallback = statusCallback;
|
||||
this.positionCallback = positionCallback;
|
||||
this._duration = -1;
|
||||
this._position = -1;
|
||||
};
|
||||
|
||||
// Media messages
|
||||
Media.MEDIA_STATE = 1;
|
||||
Media.MEDIA_DURATION = 2;
|
||||
Media.MEDIA_POSITION = 3;
|
||||
Media.MEDIA_ERROR = 9;
|
||||
|
||||
// Media states
|
||||
Media.MEDIA_NONE = 0;
|
||||
Media.MEDIA_STARTING = 1;
|
||||
Media.MEDIA_RUNNING = 2;
|
||||
Media.MEDIA_PAUSED = 3;
|
||||
Media.MEDIA_STOPPED = 4;
|
||||
Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
|
||||
|
||||
// TODO: Will MediaError be used?
|
||||
/**
|
||||
* This class contains information about any Media errors.
|
||||
* @constructor
|
||||
*/
|
||||
function MediaError() {
|
||||
this.code = null,
|
||||
this.message = "";
|
||||
}
|
||||
var MediaError = function() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
};
|
||||
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
MediaError.MEDIA_ERR_DECODE = 3;
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
MediaError.MEDIA_ERR_DECODE = 3;
|
||||
MediaError.MEDIA_ERR_NONE_SUPPORTED = 4;
|
||||
|
||||
|
||||
//if (typeof navigator.audio == "undefined") navigator.audio = new Media(src);
|
||||
/**
|
||||
* Start or resume playing audio file.
|
||||
*/
|
||||
Media.prototype.play = function() {
|
||||
PhoneGap.exec(null, null, "Media", "startPlayingAudio", [this.id, this.src]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class provides access to the device media, interfaces to both sound and video
|
||||
* Stop playing audio file.
|
||||
*/
|
||||
Media.prototype.stop = function() {
|
||||
return PhoneGap.exec(null, null, "Media", "stopPlayingAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Seek or jump to a new time in the track..
|
||||
*/
|
||||
Media.prototype.seekTo = function(milliseconds) {
|
||||
PhoneGap.exec(null, null, "Media", "seekToAudio", [this.id, milliseconds]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Pause playing audio file.
|
||||
*/
|
||||
Media.prototype.pause = function() {
|
||||
PhoneGap.exec(null, null, "Media", "pausePlayingAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get duration of an audio file.
|
||||
* The duration is only set for audio that is playing, paused or stopped.
|
||||
*
|
||||
* @return duration or -1 if not known.
|
||||
*/
|
||||
Media.prototype.getDuration = function() {
|
||||
return this._duration;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get position of audio.
|
||||
*/
|
||||
Media.prototype.getCurrentPosition = function(success, fail) {
|
||||
PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start recording audio file.
|
||||
*/
|
||||
Media.prototype.startRecord = function() {
|
||||
PhoneGap.exec(null, null, "Media", "startRecordingAudio", [this.id, this.src]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop recording audio file.
|
||||
*/
|
||||
Media.prototype.stopRecord = function() {
|
||||
PhoneGap.exec(null, null, "Media", "stopRecordingAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Release the resources.
|
||||
*/
|
||||
Media.prototype.release = function() {
|
||||
PhoneGap.exec(null, null, "Media", "release", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* List of media objects.
|
||||
* PRIVATE
|
||||
*/
|
||||
PhoneGap.mediaObjects = {};
|
||||
|
||||
/**
|
||||
* Object that receives native callbacks.
|
||||
* PRIVATE
|
||||
* @constructor
|
||||
*/
|
||||
PhoneGap.Media = function() {};
|
||||
|
||||
Media.prototype.play = function() {
|
||||
DroidGap.startPlayingAudio(this.src);
|
||||
/**
|
||||
* Get the media object.
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
*/
|
||||
PhoneGap.Media.getMediaObject = function(id) {
|
||||
return PhoneGap.mediaObjects[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio has status update.
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
* @param status The status code (int)
|
||||
* @param msg The status message (string)
|
||||
*/
|
||||
PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
var media = PhoneGap.mediaObjects[id];
|
||||
// If state update
|
||||
if (msg === Media.MEDIA_STATE) {
|
||||
if (value === Media.MEDIA_STOPPED) {
|
||||
if (media.successCallback) {
|
||||
media.successCallback();
|
||||
}
|
||||
}
|
||||
if (media.statusCallback) {
|
||||
media.statusCallback(value);
|
||||
}
|
||||
}
|
||||
else if (msg === Media.MEDIA_DURATION) {
|
||||
media._duration = value;
|
||||
}
|
||||
else if (msg === Media.MEDIA_ERROR) {
|
||||
if (media.errorCallback) {
|
||||
media.errorCallback(value);
|
||||
}
|
||||
}
|
||||
else if (msg == Media.MEDIA_POSITION) {
|
||||
media._position = value;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Media.prototype.stop = function() {
|
||||
DroidGap.stopPlayingAudio();
|
||||
}
|
||||
|
||||
Media.prototype.startRecord = function() {
|
||||
DroidGap.startRecordingAudio(this.src);
|
||||
}
|
||||
|
||||
Media.prototype.stopRecordingAudio = function() {
|
||||
DroidGap.stopRecordingAudio();
|
||||
}
|
||||
|
||||
|
||||
|
||||
120
framework/assets/js/network.js
Normal file → Executable file
120
framework/assets/js/network.js
Normal file → Executable file
@@ -1,54 +1,82 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("network")) {
|
||||
PhoneGap.addResource("network");
|
||||
|
||||
/**
|
||||
* This class contains information about any NetworkStatus.
|
||||
* This class contains information about the current network Connection.
|
||||
* @constructor
|
||||
*/
|
||||
function NetworkStatus() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
}
|
||||
NetworkStatus.NOT_REACHABLE = 0;
|
||||
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
/**
|
||||
* This class provides access to device Network data (reachability).
|
||||
* @constructor
|
||||
*/
|
||||
function Network() {
|
||||
/**
|
||||
* The last known Network status.
|
||||
* { hostName: string, ipAddress: string,
|
||||
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
|
||||
*/
|
||||
this.lastReachability = null;
|
||||
var Connection = function() {
|
||||
this.type = null;
|
||||
this._firstRun = true;
|
||||
this._timer = null;
|
||||
this.timeout = 500;
|
||||
|
||||
var me = this;
|
||||
this.getInfo(
|
||||
function(type) {
|
||||
// Need to send events if we are on or offline
|
||||
if (type == "none") {
|
||||
// set a timer if still offline at the end of timer send the offline event
|
||||
me._timer = setTimeout(function(){
|
||||
me.type = type;
|
||||
PhoneGap.fireEvent('offline');
|
||||
me._timer = null;
|
||||
}, me.timeout);
|
||||
} else {
|
||||
// If there is a current offline event pending clear it
|
||||
if (me._timer != null) {
|
||||
clearTimeout(me._timer);
|
||||
me._timer = null;
|
||||
}
|
||||
me.type = type;
|
||||
PhoneGap.fireEvent('online');
|
||||
}
|
||||
|
||||
// should only fire this once
|
||||
if (me._firstRun) {
|
||||
me._firstRun = false;
|
||||
PhoneGap.onPhoneGapConnectionReady.fire();
|
||||
}
|
||||
},
|
||||
function(e) {
|
||||
console.log("Error initializing Network Connection: " + e);
|
||||
});
|
||||
};
|
||||
|
||||
Connection.UNKNOWN = "unknown";
|
||||
Connection.ETHERNET = "ethernet";
|
||||
Connection.WIFI = "wifi";
|
||||
Connection.CELL_2G = "2g";
|
||||
Connection.CELL_3G = "3g";
|
||||
Connection.CELL_4G = "4g";
|
||||
Connection.NONE = "none";
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when the reachability status has changed.
|
||||
* @param {Reachibility} reachability The current reachability status.
|
||||
* Get connection info
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the Connection data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the Connection data. (OPTIONAL)
|
||||
*/
|
||||
Network.prototype.updateReachability = function(reachability) {
|
||||
this.lastReachability = reachability;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param {Object} uri
|
||||
* @param {Function} win
|
||||
* @param {Object} options (isIpAddress:boolean)
|
||||
*/
|
||||
Network.prototype.isReachable = function(uri, win, options)
|
||||
{
|
||||
var status = new NetworkStatus();
|
||||
if(NetworkManager.isReachable(uri))
|
||||
{
|
||||
if (NetworkManager.isWifiActive()) {
|
||||
status.code = NetworkStatus.REACHABLE_VIA_WIFI_NETWORK;
|
||||
} else {
|
||||
status.code = NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK;
|
||||
}
|
||||
} else {
|
||||
status.code = NetworkStatus.NOT_REACHABLE;
|
||||
}
|
||||
win(status);
|
||||
Connection.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
// Get info
|
||||
PhoneGap.exec(successCallback, errorCallback, "Network Status", "getConnectionInfo", []);
|
||||
};
|
||||
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.network == "undefined") navigator.network = new Network();
|
||||
});
|
||||
if (typeof navigator.network === "undefined") {
|
||||
navigator.network = new Object();
|
||||
}
|
||||
if (typeof navigator.network.connection === "undefined") {
|
||||
navigator.network.connection = new Connection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
117
framework/assets/js/notification.js
Normal file → Executable file
117
framework/assets/js/notification.js
Normal file → Executable file
@@ -1,77 +1,122 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("notification")) {
|
||||
PhoneGap.addResource("notification");
|
||||
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
* @constructor
|
||||
*/
|
||||
function Notification() {
|
||||
|
||||
}
|
||||
var Notification = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a native alert dialog, with a customizable title and button text.
|
||||
* @param {String} message Message to print in the body of the alert
|
||||
* @param {String} [title="Alert"] Title of the alert dialog (default: Alert)
|
||||
* @param {String} [buttonLabel="OK"] Label of the close button (default: OK)
|
||||
*
|
||||
* @param {String} message Message to print in the body of the alert
|
||||
* @param {Function} completeCallback The callback that is called when user clicks on a button.
|
||||
* @param {String} title Title of the alert dialog (default: Alert)
|
||||
* @param {String} buttonLabel Label of the close button (default: OK)
|
||||
*/
|
||||
Notification.prototype.alert = function(message, title, buttonLabel) {
|
||||
// Default is to use a browser alert; this will use "index.html" as the title though
|
||||
alert(message);
|
||||
Notification.prototype.alert = function(message, completeCallback, title, buttonLabel) {
|
||||
var _title = (title || "Alert");
|
||||
var _buttonLabel = (buttonLabel || "OK");
|
||||
PhoneGap.exec(completeCallback, null, "Notification", "alert", [message,_title,_buttonLabel]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a native confirm dialog, with a customizable title and button text.
|
||||
* The result that the user selects is returned to the result callback.
|
||||
*
|
||||
* @param {String} message Message to print in the body of the alert
|
||||
* @param {Function} resultCallback The callback that is called when user clicks on a button.
|
||||
* @param {String} title Title of the alert dialog (default: Confirm)
|
||||
* @param {String} buttonLabels Comma separated list of the labels of the buttons (default: 'OK,Cancel')
|
||||
*/
|
||||
Notification.prototype.confirm = function(message, resultCallback, title, buttonLabels) {
|
||||
var _title = (title || "Confirm");
|
||||
var _buttonLabels = (buttonLabels || "OK,Cancel");
|
||||
PhoneGap.exec(resultCallback, null, "Notification", "confirm", [message,_title,_buttonLabels]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start spinning the activity indicator on the statusbar
|
||||
*/
|
||||
Notification.prototype.activityStart = function() {
|
||||
PhoneGap.exec(null, null, "Notification", "activityStart", ["Busy","Please wait..."]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stop spinning the activity indicator on the statusbar, if it's currently spinning
|
||||
*/
|
||||
Notification.prototype.activityStop = function() {
|
||||
PhoneGap.exec(null, null, "Notification", "activityStop", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Display a progress dialog with progress bar that goes from 0 to 100.
|
||||
*
|
||||
* @param {String} title Title of the progress dialog.
|
||||
* @param {String} message Message to display in the dialog.
|
||||
*/
|
||||
Notification.prototype.progressStart = function(title, message) {
|
||||
PhoneGap.exec(null, null, "Notification", "progressStart", [title, message]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the progress dialog value.
|
||||
*
|
||||
* @param {Number} value 0-100
|
||||
*/
|
||||
Notification.prototype.progressValue = function(value) {
|
||||
PhoneGap.exec(null, null, "Notification", "progressValue", [value]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the progress dialog.
|
||||
*/
|
||||
Notification.prototype.progressStop = function() {
|
||||
PhoneGap.exec(null, null, "Notification", "progressStop", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Causes the device to blink a status LED.
|
||||
* @param {Integer} count The number of blinks.
|
||||
* @param {String} colour The colour of the light.
|
||||
*
|
||||
* @param {Integer} count The number of blinks.
|
||||
* @param {String} colour The colour of the light.
|
||||
*/
|
||||
Notification.prototype.blink = function(count, colour) {
|
||||
|
||||
// NOT IMPLEMENTED
|
||||
};
|
||||
|
||||
/**
|
||||
* Causes the device to vibrate.
|
||||
* @param {Integer} mills The number of milliseconds to vibrate for.
|
||||
*
|
||||
* @param {Integer} mills The number of milliseconds to vibrate for.
|
||||
*/
|
||||
Notification.prototype.vibrate = function(mills) {
|
||||
|
||||
PhoneGap.exec(null, null, "Notification", "vibrate", [mills]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Causes the device to beep.
|
||||
* @param {Integer} count The number of beeps.
|
||||
* @param {Integer} volume The volume of the beep.
|
||||
* On Android, the default notification ringtone is played "count" times.
|
||||
*
|
||||
* @param {Integer} count The number of beeps.
|
||||
*/
|
||||
Notification.prototype.beep = function(count, volume) {
|
||||
|
||||
Notification.prototype.beep = function(count) {
|
||||
PhoneGap.exec(null, null, "Notification", "beep", [count]);
|
||||
};
|
||||
|
||||
// TODO: of course on Blackberry and Android there notifications in the UI as well
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
|
||||
if (typeof navigator.notification === "undefined") {
|
||||
navigator.notification = new Notification();
|
||||
}
|
||||
});
|
||||
|
||||
Notification.prototype.vibrate = function(mills)
|
||||
{
|
||||
DroidGap.vibrate(mills);
|
||||
}
|
||||
|
||||
/*
|
||||
* On the Android, we don't beep, we notify you with your
|
||||
* notification! We shouldn't keep hammering on this, and should
|
||||
* review what we want beep to do.
|
||||
*/
|
||||
|
||||
Notification.prototype.beep = function(count, volume)
|
||||
{
|
||||
DroidGap.beep(count);
|
||||
}
|
||||
|
||||
938
framework/assets/js/phonegap.js.base
Normal file → Executable file
938
framework/assets/js/phonegap.js.base
Normal file → Executable file
@@ -1,24 +1,195 @@
|
||||
if (typeof(DeviceInfo) != 'object')
|
||||
DeviceInfo = {};
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (typeof PhoneGap === "undefined") {
|
||||
|
||||
/**
|
||||
* The order of events during page load and PhoneGap startup is as follows:
|
||||
*
|
||||
* onDOMContentLoaded Internal event that is received when the web page is loaded and parsed.
|
||||
* window.onload Body onload event.
|
||||
* onNativeReady Internal event that indicates the PhoneGap native side is ready.
|
||||
* onPhoneGapInit Internal event that kicks off creation of all PhoneGap JavaScript objects (runs constructors).
|
||||
* onPhoneGapReady Internal event fired when all PhoneGap JavaScript objects have been created
|
||||
* onPhoneGapInfoReady Internal event fired when device properties are available
|
||||
* onDeviceReady User event fired to indicate that PhoneGap is ready
|
||||
* onResume User event fired to indicate a start/resume lifecycle event
|
||||
* onPause User event fired to indicate a pause lifecycle event
|
||||
* onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
|
||||
*
|
||||
* The only PhoneGap events that user code should register for are:
|
||||
* onDeviceReady
|
||||
* onResume
|
||||
*
|
||||
* Listeners can be registered as:
|
||||
* document.addEventListener("deviceready", myDeviceReadyListener, false);
|
||||
* document.addEventListener("resume", myResumeListener, false);
|
||||
* document.addEventListener("pause", myPauseListener, false);
|
||||
*/
|
||||
|
||||
if (typeof(DeviceInfo) !== 'object') {
|
||||
var DeviceInfo = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents the PhoneGap API itself, and provides a global namespace for accessing
|
||||
* information about the state of PhoneGap.
|
||||
* @class
|
||||
*/
|
||||
PhoneGap = {
|
||||
var PhoneGap = {
|
||||
queue: {
|
||||
ready: true,
|
||||
commands: [],
|
||||
timer: null
|
||||
},
|
||||
_constructors: []
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* List of resource files loaded by PhoneGap.
|
||||
* This is used to ensure JS and other files are loaded only once.
|
||||
*/
|
||||
PhoneGap.resources = {base: true};
|
||||
|
||||
/**
|
||||
* Determine if resource has been loaded by PhoneGap
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.hasResource = function(name) {
|
||||
return PhoneGap.resources[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a resource to list of loaded resources by PhoneGap
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
PhoneGap.addResource = function(name) {
|
||||
PhoneGap.resources[name] = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom pub-sub channel that can have functions subscribed to it
|
||||
* @constructor
|
||||
*/
|
||||
PhoneGap.Channel = function (type)
|
||||
{
|
||||
this.type = type;
|
||||
this.handlers = {};
|
||||
this.guid = 0;
|
||||
this.fired = false;
|
||||
this.enabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribes the given function to the channel. Any time that
|
||||
* Channel.fire is called so too will the function.
|
||||
* Optionally specify an execution context for the function
|
||||
* and a guid that can be used to stop subscribing to the channel.
|
||||
* Returns the guid.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.subscribe = function(f, c, g) {
|
||||
// need a function to call
|
||||
if (f === null) { return; }
|
||||
|
||||
var func = f;
|
||||
if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); }
|
||||
|
||||
g = g || func.observer_guid || f.observer_guid || this.guid++;
|
||||
func.observer_guid = g;
|
||||
f.observer_guid = g;
|
||||
this.handlers[g] = func;
|
||||
return g;
|
||||
};
|
||||
|
||||
/**
|
||||
* Like subscribe but the function is only called once and then it
|
||||
* auto-unsubscribes itself.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
|
||||
var g = null;
|
||||
var _this = this;
|
||||
var m = function() {
|
||||
f.apply(c || null, arguments);
|
||||
_this.unsubscribe(g);
|
||||
};
|
||||
if (this.fired) {
|
||||
if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); }
|
||||
f.apply(this, this.fireArgs);
|
||||
} else {
|
||||
g = this.subscribe(m);
|
||||
}
|
||||
return g;
|
||||
};
|
||||
|
||||
/**
|
||||
* Unsubscribes the function with the given guid from the channel.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.unsubscribe = function(g) {
|
||||
if (typeof g === "function") { g = g.observer_guid; }
|
||||
this.handlers[g] = null;
|
||||
delete this.handlers[g];
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls all functions subscribed to this channel.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.fire = function(e) {
|
||||
if (this.enabled) {
|
||||
var fail = false;
|
||||
var item, handler, rv;
|
||||
for (item in this.handlers) {
|
||||
if (this.handlers.hasOwnProperty(item)) {
|
||||
handler = this.handlers[item];
|
||||
if (typeof handler === "function") {
|
||||
rv = (handler.apply(this, arguments) === false);
|
||||
fail = fail || rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fired = true;
|
||||
this.fireArgs = arguments;
|
||||
return !fail;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calls the provided function only after all of the channels specified
|
||||
* have been fired.
|
||||
*/
|
||||
PhoneGap.Channel.join = function(h, c) {
|
||||
var i = c.length;
|
||||
var f = function() {
|
||||
if (!(--i)) {
|
||||
h();
|
||||
}
|
||||
};
|
||||
var len = i;
|
||||
var j;
|
||||
for (j=0; j<len; j++) {
|
||||
if (!c[j].fired) {
|
||||
c[j].subscribeOnce(f);
|
||||
}
|
||||
else {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
if (!i) {
|
||||
h();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Boolean flag indicating if the PhoneGap API is available and initialized.
|
||||
*/ // TODO: Remove this, it is unused here ... -jm
|
||||
PhoneGap.available = DeviceInfo.uuid != undefined;
|
||||
PhoneGap.available = DeviceInfo.uuid !== undefined;
|
||||
|
||||
/**
|
||||
* Add an initialization function to a queue that ensures it will run and initialize
|
||||
@@ -26,69 +197,487 @@ PhoneGap.available = DeviceInfo.uuid != undefined;
|
||||
* @param {Function} func The function callback you want run once PhoneGap is initialized
|
||||
*/
|
||||
PhoneGap.addConstructor = function(func) {
|
||||
var state = document.readyState;
|
||||
if ( state == 'loaded' || state == 'complete' )
|
||||
{
|
||||
func();
|
||||
}
|
||||
else
|
||||
{
|
||||
PhoneGap._constructors.push(func);
|
||||
}
|
||||
PhoneGap.onPhoneGapInit.subscribeOnce(function() {
|
||||
try {
|
||||
func();
|
||||
} catch(e) {
|
||||
console.log("Failed to run constructor: " + e);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
(function()
|
||||
{
|
||||
var timer = setInterval(function()
|
||||
{
|
||||
|
||||
var state = document.readyState;
|
||||
|
||||
if ( state == 'loaded' || state == 'complete' )
|
||||
{
|
||||
clearInterval(timer); // stop looking
|
||||
// run our constructors list
|
||||
while (PhoneGap._constructors.length > 0)
|
||||
{
|
||||
var constructor = PhoneGap._constructors.shift();
|
||||
try
|
||||
{
|
||||
constructor();
|
||||
}
|
||||
catch(e)
|
||||
{
|
||||
if (typeof(debug['log']) == 'function')
|
||||
{
|
||||
debug.log("Failed to run constructor: " + debug.processMessage(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
alert("Failed to run constructor: " + e.message);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Plugins object
|
||||
*/
|
||||
if (!window.plugins) {
|
||||
window.plugins = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a plugin object to window.plugins.
|
||||
* The plugin is accessed using window.plugins.<name>
|
||||
*
|
||||
* @param name The plugin name
|
||||
* @param obj The plugin object
|
||||
*/
|
||||
PhoneGap.addPlugin = function(name, obj) {
|
||||
if (!window.plugins[name]) {
|
||||
window.plugins[name] = obj;
|
||||
}
|
||||
else {
|
||||
console.log("Error: Plugin "+name+" already exists.");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* onDOMContentLoaded channel is fired when the DOM content
|
||||
* of the page has been parsed.
|
||||
*/
|
||||
PhoneGap.onDOMContentLoaded = new PhoneGap.Channel('onDOMContentLoaded');
|
||||
|
||||
/**
|
||||
* onNativeReady channel is fired when the PhoneGap native code
|
||||
* has been initialized.
|
||||
*/
|
||||
PhoneGap.onNativeReady = new PhoneGap.Channel('onNativeReady');
|
||||
|
||||
/**
|
||||
* onPhoneGapInit channel is fired when the web page is fully loaded and
|
||||
* PhoneGap native code has been initialized.
|
||||
*/
|
||||
PhoneGap.onPhoneGapInit = new PhoneGap.Channel('onPhoneGapInit');
|
||||
|
||||
/**
|
||||
* onPhoneGapReady channel is fired when the JS PhoneGap objects have been created.
|
||||
*/
|
||||
PhoneGap.onPhoneGapReady = new PhoneGap.Channel('onPhoneGapReady');
|
||||
|
||||
/**
|
||||
* onPhoneGapInfoReady channel is fired when the PhoneGap device properties
|
||||
* has been set.
|
||||
*/
|
||||
PhoneGap.onPhoneGapInfoReady = new PhoneGap.Channel('onPhoneGapInfoReady');
|
||||
|
||||
/**
|
||||
* onPhoneGapConnectionReady channel is fired when the PhoneGap connection properties
|
||||
* has been set.
|
||||
*/
|
||||
PhoneGap.onPhoneGapConnectionReady = new PhoneGap.Channel('onPhoneGapConnectionReady');
|
||||
|
||||
/**
|
||||
* onResume channel is fired when the PhoneGap native code
|
||||
* resumes.
|
||||
*/
|
||||
PhoneGap.onResume = new PhoneGap.Channel('onResume');
|
||||
|
||||
/**
|
||||
* onPause channel is fired when the PhoneGap native code
|
||||
* pauses.
|
||||
*/
|
||||
PhoneGap.onPause = new PhoneGap.Channel('onPause');
|
||||
|
||||
/**
|
||||
* onDestroy channel is fired when the PhoneGap native code
|
||||
* is destroyed. It is used internally.
|
||||
* Window.onunload should be used by the user.
|
||||
*/
|
||||
PhoneGap.onDestroy = new PhoneGap.Channel('onDestroy');
|
||||
PhoneGap.onDestroy.subscribeOnce(function() {
|
||||
PhoneGap.shuttingDown = true;
|
||||
});
|
||||
PhoneGap.shuttingDown = false;
|
||||
|
||||
// _nativeReady is global variable that the native side can set
|
||||
// to signify that the native code is ready. It is a global since
|
||||
// it may be called before any PhoneGap JS is ready.
|
||||
if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); }
|
||||
|
||||
/**
|
||||
* onDeviceReady is fired only after all PhoneGap objects are created and
|
||||
* the device properties are set.
|
||||
*/
|
||||
PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady');
|
||||
|
||||
|
||||
// Array of channels that must fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady, PhoneGap.onPhoneGapConnectionReady];
|
||||
|
||||
// Hashtable of user defined channels that must also fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsMap = {};
|
||||
|
||||
/**
|
||||
* Indicate that a feature needs to be initialized before it is ready to be used.
|
||||
* This holds up PhoneGap's "deviceready" event until the feature has been initialized
|
||||
* and PhoneGap.initComplete(feature) is called.
|
||||
*
|
||||
* @param feature {String} The unique feature name
|
||||
*/
|
||||
PhoneGap.waitForInitialization = function(feature) {
|
||||
if (feature) {
|
||||
var channel = new PhoneGap.Channel(feature);
|
||||
PhoneGap.deviceReadyChannelsMap[feature] = channel;
|
||||
PhoneGap.deviceReadyChannelsArray.push(channel);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicate that initialization code has completed and the feature is ready to be used.
|
||||
*
|
||||
* @param feature {String} The unique feature name
|
||||
*/
|
||||
PhoneGap.initializationComplete = function(feature) {
|
||||
var channel = PhoneGap.deviceReadyChannelsMap[feature];
|
||||
if (channel) {
|
||||
channel.fire();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create all PhoneGap objects once page has fully loaded and native side is ready.
|
||||
*/
|
||||
PhoneGap.Channel.join(function() {
|
||||
|
||||
// Start listening for XHR callbacks
|
||||
setTimeout(function() {
|
||||
if (PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
// all constructors run, now fire the deviceready event
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('deviceready');
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
}, 5);
|
||||
})();
|
||||
else {
|
||||
var polling = prompt("usePolling", "gap_callbackServer:");
|
||||
PhoneGap.UsePolling = polling;
|
||||
if (polling == "true") {
|
||||
PhoneGap.UsePolling = true;
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
else {
|
||||
PhoneGap.UsePolling = false;
|
||||
PhoneGap.JSCallback();
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
|
||||
// Run PhoneGap constructors
|
||||
PhoneGap.onPhoneGapInit.fire();
|
||||
|
||||
// Fire event to notify that all objects are created
|
||||
PhoneGap.onPhoneGapReady.fire();
|
||||
|
||||
// Fire onDeviceReady event once all constructors have run and PhoneGap info has been
|
||||
// received from native side, and any user defined initialization channels.
|
||||
PhoneGap.Channel.join(function() {
|
||||
PhoneGap.onDeviceReady.fire();
|
||||
|
||||
// Fire the onresume event, since first one happens before JavaScript is loaded
|
||||
PhoneGap.onResume.fire();
|
||||
}, PhoneGap.deviceReadyChannelsArray);
|
||||
|
||||
}, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]);
|
||||
|
||||
// Listen for DOMContentLoaded and notify our channel subscribers
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
PhoneGap.onDOMContentLoaded.fire();
|
||||
}, false);
|
||||
|
||||
// Intercept calls to document.addEventListener and watch for deviceready
|
||||
PhoneGap.m_document_addEventListener = document.addEventListener;
|
||||
|
||||
document.addEventListener = function(evt, handler, capture) {
|
||||
var e = evt.toLowerCase();
|
||||
if (e === 'deviceready') {
|
||||
PhoneGap.onDeviceReady.subscribeOnce(handler);
|
||||
} else if (e === 'resume') {
|
||||
PhoneGap.onResume.subscribe(handler);
|
||||
if (PhoneGap.onDeviceReady.fired) {
|
||||
PhoneGap.onResume.fire();
|
||||
}
|
||||
} else if (e === 'pause') {
|
||||
PhoneGap.onPause.subscribe(handler);
|
||||
}
|
||||
else {
|
||||
// If subscribing to Android backbutton
|
||||
if (e === 'backbutton') {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]);
|
||||
}
|
||||
|
||||
PhoneGap.m_document_addEventListener.call(document, evt, handler, capture);
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept calls to document.removeEventListener and watch for events that
|
||||
// are generated by PhoneGap native code
|
||||
PhoneGap.m_document_removeEventListener = document.removeEventListener;
|
||||
|
||||
document.removeEventListener = function(evt, handler, capture) {
|
||||
var e = evt.toLowerCase();
|
||||
|
||||
// If unsubscribing to Android backbutton
|
||||
if (e === 'backbutton') {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]);
|
||||
}
|
||||
|
||||
PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to fire event from native code
|
||||
*/
|
||||
PhoneGap.fireEvent = function(type) {
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent(type);
|
||||
document.dispatchEvent(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* If JSON not included, use our own stringify. (Android 1.6)
|
||||
* The restriction on ours is that it must be an array of simple types.
|
||||
*
|
||||
* @param args
|
||||
* @return {String}
|
||||
*/
|
||||
PhoneGap.stringify = function(args) {
|
||||
if (typeof JSON === "undefined") {
|
||||
var s = "[";
|
||||
var i, type, start, name, nameType, a;
|
||||
for (i = 0; i < args.length; i++) {
|
||||
if (args[i] !== null) {
|
||||
if (i > 0) {
|
||||
s = s + ",";
|
||||
}
|
||||
type = typeof args[i];
|
||||
if ((type === "number") || (type === "boolean")) {
|
||||
s = s + args[i];
|
||||
} else if (args[i] instanceof Array) {
|
||||
s = s + "[" + args[i] + "]";
|
||||
} else if (args[i] instanceof Object) {
|
||||
start = true;
|
||||
s = s + '{';
|
||||
for (name in args[i]) {
|
||||
if (args[i][name] !== null) {
|
||||
if (!start) {
|
||||
s = s + ',';
|
||||
}
|
||||
s = s + '"' + name + '":';
|
||||
nameType = typeof args[i][name];
|
||||
if ((nameType === "number") || (nameType === "boolean")) {
|
||||
s = s + args[i][name];
|
||||
} else if ((typeof args[i][name]) === 'function') {
|
||||
// don't copy the functions
|
||||
s = s + '""';
|
||||
} else if (args[i][name] instanceof Object) {
|
||||
s = s + PhoneGap.stringify(args[i][name]);
|
||||
} else {
|
||||
s = s + '"' + args[i][name] + '"';
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
}
|
||||
s = s + '}';
|
||||
} else {
|
||||
a = args[i].replace(/\\/g, '\\\\');
|
||||
a = a.replace(/"/g, '\\"');
|
||||
s = s + '"' + a + '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
s = s + "]";
|
||||
return s;
|
||||
} else {
|
||||
return JSON.stringify(args);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Does a deep clone of the object.
|
||||
*
|
||||
* @param obj
|
||||
* @return {Object}
|
||||
*/
|
||||
PhoneGap.clone = function(obj) {
|
||||
var i, retVal;
|
||||
if(!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(obj instanceof Array){
|
||||
retVal = [];
|
||||
for(i = 0; i < obj.length; ++i){
|
||||
retVal.push(PhoneGap.clone(obj[i]));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (typeof obj === "function") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(!(obj instanceof Object)){
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
retVal = {};
|
||||
for(i in obj){
|
||||
if(!(i in retVal) || retVal[i] !== obj[i]) {
|
||||
retVal[i] = PhoneGap.clone(obj[i]);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
PhoneGap.callbackId = 0;
|
||||
PhoneGap.callbacks = {};
|
||||
PhoneGap.callbackStatus = {
|
||||
NO_RESULT: 0,
|
||||
OK: 1,
|
||||
CLASS_NOT_FOUND_EXCEPTION: 2,
|
||||
ILLEGAL_ACCESS_EXCEPTION: 3,
|
||||
INSTANTIATION_EXCEPTION: 4,
|
||||
MALFORMED_URL_EXCEPTION: 5,
|
||||
IO_EXCEPTION: 6,
|
||||
INVALID_ACTION: 7,
|
||||
JSON_EXCEPTION: 8,
|
||||
ERROR: 9
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Execute a PhoneGap command in a queued fashion, to ensure commands do not
|
||||
* execute with any race conditions, and only run when PhoneGap is ready to
|
||||
* recieve them.
|
||||
* @param {String} command Command to be run in PhoneGap, e.g. "ClassName.method"
|
||||
* @param {String[]} [args] Zero or more arguments to pass to the method
|
||||
* Execute a PhoneGap command. It is up to the native side whether this action is synch or async.
|
||||
* The native side can return:
|
||||
* Synchronous: PluginResult object as a JSON string
|
||||
* Asynchrounous: Empty string ""
|
||||
* If async, the native side will PhoneGap.callbackSuccess or PhoneGap.callbackError,
|
||||
* depending upon the result of the action.
|
||||
*
|
||||
* @param {Function} success The success callback
|
||||
* @param {Function} fail The fail callback
|
||||
* @param {String} service The name of the service to use
|
||||
* @param {String} action Action to be run in PhoneGap
|
||||
* @param {Array.<String>} [args] Zero or more arguments to pass to the method
|
||||
*/
|
||||
PhoneGap.exec = function() {
|
||||
PhoneGap.queue.commands.push(arguments);
|
||||
if (PhoneGap.queue.timer == null)
|
||||
PhoneGap.queue.timer = setInterval(PhoneGap.run_command, 10);
|
||||
PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
try {
|
||||
var callbackId = service + PhoneGap.callbackId++;
|
||||
if (success || fail) {
|
||||
PhoneGap.callbacks[callbackId] = {success:success, fail:fail};
|
||||
}
|
||||
|
||||
var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));
|
||||
|
||||
// If a result was returned
|
||||
if (r.length > 0) {
|
||||
eval("var v="+r+";");
|
||||
|
||||
// If status is OK, then return value back to caller
|
||||
if (v.status === PhoneGap.callbackStatus.OK) {
|
||||
|
||||
// If there is a success callback, then call it now with
|
||||
// returned value
|
||||
if (success) {
|
||||
try {
|
||||
success(v.message);
|
||||
} catch (e) {
|
||||
console.log("Error in success callback: " + callbackId + " = " + e);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return v.message;
|
||||
}
|
||||
|
||||
// If no result
|
||||
else if (v.status === PhoneGap.callbackStatus.NO_RESULT) {
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
|
||||
// If error, then display error
|
||||
else {
|
||||
console.log("Error: Status="+v.status+" Message="+v.message);
|
||||
|
||||
// If there is a fail callback, then call it now with returned value
|
||||
if (fail) {
|
||||
try {
|
||||
fail(v.message);
|
||||
}
|
||||
catch (e1) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e1);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (e2) {
|
||||
console.log("Error: "+e2);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by native code when returning successful result from an action.
|
||||
*
|
||||
* @param callbackId
|
||||
* @param args
|
||||
*/
|
||||
PhoneGap.callbackSuccess = function(callbackId, args) {
|
||||
if (PhoneGap.callbacks[callbackId]) {
|
||||
|
||||
// If result is to be sent to callback
|
||||
if (args.status === PhoneGap.callbackStatus.OK) {
|
||||
try {
|
||||
if (PhoneGap.callbacks[callbackId].success) {
|
||||
PhoneGap.callbacks[callbackId].success(args.message);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error in success callback: "+callbackId+" = "+e);
|
||||
}
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!args.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by native code when returning error result from an action.
|
||||
*
|
||||
* @param callbackId
|
||||
* @param args
|
||||
*/
|
||||
PhoneGap.callbackError = function(callbackId, args) {
|
||||
if (PhoneGap.callbacks[callbackId]) {
|
||||
try {
|
||||
if (PhoneGap.callbacks[callbackId].fail) {
|
||||
PhoneGap.callbacks[callbackId].fail(args.message);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!args.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Internal function used to dispatch the request to PhoneGap. It processes the
|
||||
* command queue and executes the next command on the list. If one of the
|
||||
@@ -96,40 +685,243 @@ PhoneGap.exec = function() {
|
||||
* url, which will be turned into a dictionary on the other end.
|
||||
* @private
|
||||
*/
|
||||
// TODO: Is this used?
|
||||
PhoneGap.run_command = function() {
|
||||
if (!PhoneGap.available || !PhoneGap.queue.ready)
|
||||
if (!PhoneGap.available || !PhoneGap.queue.ready) {
|
||||
return;
|
||||
|
||||
}
|
||||
PhoneGap.queue.ready = false;
|
||||
|
||||
var args = PhoneGap.queue.commands.shift();
|
||||
if (PhoneGap.queue.commands.length == 0) {
|
||||
if (PhoneGap.queue.commands.length === 0) {
|
||||
clearInterval(PhoneGap.queue.timer);
|
||||
PhoneGap.queue.timer = null;
|
||||
}
|
||||
|
||||
var uri = [];
|
||||
var dict = null;
|
||||
for (var i = 1; i < args.length; i++) {
|
||||
var i;
|
||||
for (i = 1; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
if (arg == undefined || arg == null)
|
||||
if (arg === undefined || arg === null) {
|
||||
arg = '';
|
||||
if (typeof(arg) == 'object')
|
||||
}
|
||||
if (typeof(arg) === 'object') {
|
||||
dict = arg;
|
||||
else
|
||||
} else {
|
||||
uri.push(encodeURIComponent(arg));
|
||||
}
|
||||
}
|
||||
var url = "gap://" + args[0] + "/" + uri.join("/");
|
||||
if (dict != null) {
|
||||
if (dict !== null) {
|
||||
var name;
|
||||
var query_args = [];
|
||||
for (var name in dict) {
|
||||
if (typeof(name) != 'string')
|
||||
continue;
|
||||
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
|
||||
for (name in dict) {
|
||||
if (dict.hasOwnProperty(name) && (typeof (name) === 'string')) {
|
||||
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
|
||||
}
|
||||
}
|
||||
if (query_args.length > 0)
|
||||
if (query_args.length > 0) {
|
||||
url += "?" + query_args.join("&");
|
||||
}
|
||||
}
|
||||
document.location = url;
|
||||
|
||||
};
|
||||
|
||||
PhoneGap.JSCallbackPort = null;
|
||||
PhoneGap.JSCallbackToken = null;
|
||||
|
||||
/**
|
||||
* This is only for Android.
|
||||
*
|
||||
* Internal function that uses XHR to call into PhoneGap Java code and retrieve
|
||||
* any JavaScript code that needs to be run. This is used for callbacks from
|
||||
* Java to JavaScript.
|
||||
*/
|
||||
PhoneGap.JSCallback = function() {
|
||||
|
||||
// Exit if shutting down app
|
||||
if (PhoneGap.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If polling flag was changed, start using polling from now on
|
||||
if (PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
return;
|
||||
}
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
|
||||
// Callback function when XMLHttpRequest is ready
|
||||
xmlhttp.onreadystatechange=function(){
|
||||
if(xmlhttp.readyState === 4){
|
||||
|
||||
// Exit if shutting down app
|
||||
if (PhoneGap.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status === 200) {
|
||||
|
||||
// Need to url decode the response
|
||||
var msg = decodeURIComponent(xmlhttp.responseText);
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(msg);
|
||||
}
|
||||
catch (e) {
|
||||
// If we're getting an error here, seeing the message will help in debugging
|
||||
console.log("JSCallback: Message from Server: " + msg);
|
||||
console.log("JSCallback Error: "+e);
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(PhoneGap.JSCallback, 1);
|
||||
}
|
||||
|
||||
// If callback ping (used to keep XHR request from timing out)
|
||||
else if (xmlhttp.status === 404) {
|
||||
setTimeout(PhoneGap.JSCallback, 10);
|
||||
}
|
||||
|
||||
// If security error
|
||||
else if (xmlhttp.status === 403) {
|
||||
console.log("JSCallback Error: Invalid token. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If server is stopping
|
||||
else if (xmlhttp.status === 503) {
|
||||
console.log("JSCallback Error: Service unavailable. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If request wasn't GET
|
||||
else if (xmlhttp.status === 400) {
|
||||
console.log("JSCallback Error: Bad request. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If error, revert to polling
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed.");
|
||||
PhoneGap.UsePolling = true;
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (PhoneGap.JSCallbackPort === null) {
|
||||
PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:");
|
||||
}
|
||||
if (PhoneGap.JSCallbackToken === null) {
|
||||
PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:");
|
||||
}
|
||||
xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true);
|
||||
xmlhttp.send();
|
||||
};
|
||||
|
||||
/**
|
||||
* The polling period to use with JSCallbackPolling.
|
||||
* This can be changed by the application. The default is 50ms.
|
||||
*/
|
||||
PhoneGap.JSCallbackPollingPeriod = 50;
|
||||
|
||||
/**
|
||||
* Flag that can be set by the user to force polling to be used or force XHR to be used.
|
||||
*/
|
||||
PhoneGap.UsePolling = false; // T=use polling, F=use XHR
|
||||
|
||||
/**
|
||||
* This is only for Android.
|
||||
*
|
||||
* Internal function that uses polling to call into PhoneGap Java code and retrieve
|
||||
* any JavaScript code that needs to be run. This is used for callbacks from
|
||||
* Java to JavaScript.
|
||||
*/
|
||||
PhoneGap.JSCallbackPolling = function() {
|
||||
|
||||
// Exit if shutting down app
|
||||
if (PhoneGap.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If polling flag was changed, stop using polling from now on
|
||||
if (!PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = prompt("", "gap_poll:");
|
||||
if (msg) {
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(""+msg);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("JSCallbackPolling: Message from Server: " + msg);
|
||||
console.log("JSCallbackPolling Error: "+e);
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(PhoneGap.JSCallbackPolling, 1);
|
||||
}
|
||||
else {
|
||||
setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a UUID
|
||||
*
|
||||
* @return {String}
|
||||
*/
|
||||
PhoneGap.createUUID = function() {
|
||||
return PhoneGap.UUIDcreatePart(4) + '-' +
|
||||
PhoneGap.UUIDcreatePart(2) + '-' +
|
||||
PhoneGap.UUIDcreatePart(2) + '-' +
|
||||
PhoneGap.UUIDcreatePart(2) + '-' +
|
||||
PhoneGap.UUIDcreatePart(6);
|
||||
};
|
||||
|
||||
PhoneGap.UUIDcreatePart = function(length) {
|
||||
var uuidpart = "";
|
||||
var i, uuidchar;
|
||||
for (i=0; i<length; i++) {
|
||||
uuidchar = parseInt((Math.random() * 256),0).toString(16);
|
||||
if (uuidchar.length === 1) {
|
||||
uuidchar = "0" + uuidchar;
|
||||
}
|
||||
uuidpart += uuidchar;
|
||||
}
|
||||
return uuidpart;
|
||||
};
|
||||
|
||||
PhoneGap.close = function(context, func, params) {
|
||||
if (typeof params === 'undefined') {
|
||||
return function() {
|
||||
return func.apply(context, arguments);
|
||||
};
|
||||
} else {
|
||||
return function() {
|
||||
return func.apply(context, params);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a JavaScript file after page has loaded.
|
||||
*
|
||||
* @param {String} jsfile The url of the JavaScript file to load.
|
||||
* @param {Function} successCallback The callback to call when the file has been loaded.
|
||||
*/
|
||||
PhoneGap.includeJavascript = function(jsfile, successCallback) {
|
||||
var id = document.getElementsByTagName("head")[0];
|
||||
var el = document.createElement('script');
|
||||
el.type = 'text/javascript';
|
||||
if (typeof successCallback === 'function') {
|
||||
el.onload = successCallback;
|
||||
}
|
||||
el.src = jsfile;
|
||||
id.appendChild(el);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
35
framework/assets/js/position.js
Normal file → Executable file
35
framework/assets/js/position.js
Normal file → Executable file
@@ -1,3 +1,14 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("position")) {
|
||||
PhoneGap.addResource("position");
|
||||
|
||||
/**
|
||||
* This class contains position information.
|
||||
* @param {Object} lat
|
||||
@@ -9,12 +20,13 @@
|
||||
* @param {Object} vel
|
||||
* @constructor
|
||||
*/
|
||||
function Position(coords, timestamp) {
|
||||
var Position = function(coords, timestamp) {
|
||||
this.coords = coords;
|
||||
this.timestamp = new Date().getTime();
|
||||
}
|
||||
this.timestamp = (timestamp !== 'undefined') ? timestamp : new Date().getTime();
|
||||
};
|
||||
|
||||
function Coordinates(lat, lng, alt, acc, head, vel) {
|
||||
/** @constructor */
|
||||
var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/**
|
||||
* The latitude of the position.
|
||||
*/
|
||||
@@ -39,13 +51,17 @@ function Coordinates(lat, lng, alt, acc, head, vel) {
|
||||
* The velocity with which the device is moving at the position.
|
||||
*/
|
||||
this.speed = vel;
|
||||
}
|
||||
/**
|
||||
* The altitude accuracy of the position.
|
||||
*/
|
||||
this.altitudeAccuracy = (altacc !== 'undefined') ? altacc : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class specifies the options for requesting position data.
|
||||
* @constructor
|
||||
*/
|
||||
function PositionOptions() {
|
||||
var PositionOptions = function() {
|
||||
/**
|
||||
* Specifies the desired position accuracy.
|
||||
*/
|
||||
@@ -55,18 +71,19 @@ function PositionOptions() {
|
||||
* is called.
|
||||
*/
|
||||
this.timeout = 10000;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class contains information about any GSP errors.
|
||||
* @constructor
|
||||
*/
|
||||
function PositionError() {
|
||||
var PositionError = function() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
}
|
||||
};
|
||||
|
||||
PositionError.UNKNOWN_ERROR = 0;
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
PositionError.TIMEOUT = 3;
|
||||
}
|
||||
|
||||
479
framework/assets/js/storage.js
Normal file → Executable file
479
framework/assets/js/storage.js
Normal file → Executable file
@@ -1,89 +1,428 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
/*
|
||||
* This is purely for the Android 1.5/1.6 HTML 5 Storage
|
||||
* I was hoping that Android 2.0 would deprecate this, but given the fact that
|
||||
* I was hoping that Android 2.0 would deprecate this, but given the fact that
|
||||
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
|
||||
*/
|
||||
|
||||
var DroidDB = function()
|
||||
{
|
||||
this.txQueue = [];
|
||||
}
|
||||
if (!PhoneGap.hasResource("storage")) {
|
||||
PhoneGap.addResource("storage");
|
||||
|
||||
DroidDB.prototype.addResult = function(rawdata, tx_id)
|
||||
{
|
||||
eval("var data = " + rawdata);
|
||||
var tx = this.txQueue[tx_id];
|
||||
tx.resultSet.push(data);
|
||||
}
|
||||
/**
|
||||
* SQL result set object
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB_Rows = function() {
|
||||
this.resultSet = []; // results array
|
||||
this.length = 0; // number of rows
|
||||
};
|
||||
|
||||
DroidDB.prototype.completeQuery = function(tx_id)
|
||||
{
|
||||
var tx = this.txQueue[tx_id];
|
||||
var r = new result();
|
||||
r.rows.resultSet = tx.resultSet;
|
||||
r.rows.length = tx.resultSet.length;
|
||||
tx.win(r);
|
||||
}
|
||||
/**
|
||||
* Get item from SQL result set
|
||||
*
|
||||
* @param row The row number to return
|
||||
* @return The row object
|
||||
*/
|
||||
DroidDB_Rows.prototype.item = function(row) {
|
||||
return this.resultSet[row];
|
||||
};
|
||||
|
||||
DroidDB.prototype.fail = function(reason, tx_id)
|
||||
{
|
||||
var tx = this.txQueue[tx_id];
|
||||
tx.fail(reason);
|
||||
}
|
||||
/**
|
||||
* SQL result set that is returned to user.
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB_Result = function() {
|
||||
this.rows = new DroidDB_Rows();
|
||||
};
|
||||
|
||||
var DatabaseShell = function()
|
||||
{
|
||||
|
||||
}
|
||||
/**
|
||||
* Storage object that is called by native code when performing queries.
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB = function() {
|
||||
this.queryQueue = {};
|
||||
};
|
||||
|
||||
DatabaseShell.prototype.transaction = function(process)
|
||||
{
|
||||
tx = new Tx();
|
||||
process(tx);
|
||||
}
|
||||
/**
|
||||
* Callback from native code when query is complete.
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param id Query id
|
||||
*/
|
||||
DroidDB.prototype.completeQuery = function(id, data) {
|
||||
var query = this.queryQueue[id];
|
||||
if (query) {
|
||||
try {
|
||||
delete this.queryQueue[id];
|
||||
|
||||
var Tx = function()
|
||||
{
|
||||
droiddb.txQueue.push(this);
|
||||
this.id = droiddb.txQueue.length - 1;
|
||||
this.resultSet = [];
|
||||
}
|
||||
// Get transaction
|
||||
var tx = query.tx;
|
||||
|
||||
Tx.prototype.executeSql = function(query, params, win, fail)
|
||||
{
|
||||
droidStorage.executeSql(query, params, this.id);
|
||||
tx.win = win;
|
||||
tx.fail = fail;
|
||||
}
|
||||
// If transaction hasn't failed
|
||||
// Note: We ignore all query results if previous query
|
||||
// in the same transaction failed.
|
||||
if (tx && tx.queryList[id]) {
|
||||
|
||||
var result = function()
|
||||
{
|
||||
this.rows = new Rows();
|
||||
}
|
||||
// Save query results
|
||||
var r = new DroidDB_Result();
|
||||
r.rows.resultSet = data;
|
||||
r.rows.length = data.length;
|
||||
try {
|
||||
if (typeof query.successCallback === 'function') {
|
||||
query.successCallback(query.tx, r);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.log("executeSql error calling user success callback: "+ex);
|
||||
}
|
||||
|
||||
var Rows = function()
|
||||
{
|
||||
this.resultSet = [];
|
||||
this.length = 0;
|
||||
}
|
||||
tx.queryComplete(id);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("executeSql error: "+e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Rows.prototype.item = function(row_id)
|
||||
{
|
||||
return this.resultSet[id];
|
||||
}
|
||||
/**
|
||||
* Callback from native code when query fails
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param reason Error message
|
||||
* @param id Query id
|
||||
*/
|
||||
DroidDB.prototype.fail = function(reason, id) {
|
||||
var query = this.queryQueue[id];
|
||||
if (query) {
|
||||
try {
|
||||
delete this.queryQueue[id];
|
||||
|
||||
var dbSetup = function(name, version, display_name, size)
|
||||
{
|
||||
droidStorage.openDatabase(name, version, display_name, size)
|
||||
db_object = new DatabaseShell();
|
||||
return db_object;
|
||||
}
|
||||
// Get transaction
|
||||
var tx = query.tx;
|
||||
|
||||
// If transaction hasn't failed
|
||||
// Note: We ignore all query results if previous query
|
||||
// in the same transaction failed.
|
||||
if (tx && tx.queryList[id]) {
|
||||
tx.queryList = {};
|
||||
|
||||
try {
|
||||
if (typeof query.errorCallback === 'function') {
|
||||
query.errorCallback(query.tx, reason);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.log("executeSql error calling user error callback: "+ex);
|
||||
}
|
||||
|
||||
tx.queryFailed(id, reason);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
console.log("executeSql error: "+e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL query object
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @constructor
|
||||
* @param tx The transaction object that this query belongs to
|
||||
*/
|
||||
var DroidDB_Query = function(tx) {
|
||||
|
||||
// Set the id of the query
|
||||
this.id = PhoneGap.createUUID();
|
||||
|
||||
// Add this query to the queue
|
||||
droiddb.queryQueue[this.id] = this;
|
||||
|
||||
// Init result
|
||||
this.resultSet = [];
|
||||
|
||||
// Set transaction that this query belongs to
|
||||
this.tx = tx;
|
||||
|
||||
// Add this query to transaction list
|
||||
this.tx.queryList[this.id] = this;
|
||||
|
||||
// Callbacks
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction object
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB_Tx = function() {
|
||||
|
||||
// Set the id of the transaction
|
||||
this.id = PhoneGap.createUUID();
|
||||
|
||||
// Callbacks
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
|
||||
// Query list
|
||||
this.queryList = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark query in transaction as complete.
|
||||
* If all queries are complete, call the user's transaction success callback.
|
||||
*
|
||||
* @param id Query id
|
||||
*/
|
||||
DroidDB_Tx.prototype.queryComplete = function(id) {
|
||||
delete this.queryList[id];
|
||||
|
||||
// If no more outstanding queries, then fire transaction success
|
||||
if (this.successCallback) {
|
||||
var count = 0;
|
||||
var i;
|
||||
for (i in this.queryList) {
|
||||
if (this.queryList.hasOwnProperty(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count === 0) {
|
||||
try {
|
||||
this.successCallback();
|
||||
} catch(e) {
|
||||
console.log("Transaction error calling user success callback: " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mark query in transaction as failed.
|
||||
*
|
||||
* @param id Query id
|
||||
* @param reason Error message
|
||||
*/
|
||||
DroidDB_Tx.prototype.queryFailed = function(id, reason) {
|
||||
|
||||
// The sql queries in this transaction have already been run, since
|
||||
// we really don't have a real transaction implemented in native code.
|
||||
// However, the user callbacks for the remaining sql queries in transaction
|
||||
// will not be called.
|
||||
this.queryList = {};
|
||||
|
||||
if (this.errorCallback) {
|
||||
try {
|
||||
this.errorCallback(reason);
|
||||
} catch(e) {
|
||||
console.log("Transaction error calling user error callback: " + e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute SQL statement
|
||||
*
|
||||
* @param sql SQL statement to execute
|
||||
* @param params Statement parameters
|
||||
* @param successCallback Success callback
|
||||
* @param errorCallback Error callback
|
||||
*/
|
||||
DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
|
||||
|
||||
// Init params array
|
||||
if (typeof params === 'undefined') {
|
||||
params = [];
|
||||
}
|
||||
|
||||
// Create query and add to queue
|
||||
var query = new DroidDB_Query(this);
|
||||
droiddb.queryQueue[query.id] = query;
|
||||
|
||||
// Save callbacks
|
||||
query.successCallback = successCallback;
|
||||
query.errorCallback = errorCallback;
|
||||
|
||||
// Call native code
|
||||
PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
|
||||
};
|
||||
|
||||
var DatabaseShell = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a transaction.
|
||||
* Does not support rollback in event of failure.
|
||||
*
|
||||
* @param process {Function} The transaction function
|
||||
* @param successCallback {Function}
|
||||
* @param errorCallback {Function}
|
||||
*/
|
||||
DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
|
||||
var tx = new DroidDB_Tx();
|
||||
tx.successCallback = successCallback;
|
||||
tx.errorCallback = errorCallback;
|
||||
try {
|
||||
process(tx);
|
||||
} catch (e) {
|
||||
console.log("Transaction error: "+e);
|
||||
if (tx.errorCallback) {
|
||||
try {
|
||||
tx.errorCallback(e);
|
||||
} catch (ex) {
|
||||
console.log("Transaction error calling user error callback: "+e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Open database
|
||||
*
|
||||
* @param name Database name
|
||||
* @param version Database version
|
||||
* @param display_name Database display name
|
||||
* @param size Database size in bytes
|
||||
* @return Database object
|
||||
*/
|
||||
var DroidDB_openDatabase = function(name, version, display_name, size) {
|
||||
PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
|
||||
var db = new DatabaseShell();
|
||||
return db;
|
||||
};
|
||||
|
||||
/**
|
||||
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
|
||||
* TODO: Do similar for sessionStorage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var CupcakeLocalStorage = function() {
|
||||
try {
|
||||
|
||||
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
|
||||
var storage = {};
|
||||
this.length = 0;
|
||||
function setLength (length) {
|
||||
this.length = length;
|
||||
localStorage.length = length;
|
||||
}
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
var i;
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
|
||||
for(var i = 0; i < result.rows.length; i++) {
|
||||
storage[result.rows.item(i)['id']] = result.rows.item(i)['body'];
|
||||
}
|
||||
setLength(result.rows.length);
|
||||
PhoneGap.initializationComplete("cupcakeStorage");
|
||||
});
|
||||
|
||||
},
|
||||
function (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
);
|
||||
this.setItem = function(key, val) {
|
||||
if (typeof(storage[key])=='undefined') {
|
||||
this.length++;
|
||||
}
|
||||
storage[key] = val;
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
|
||||
}
|
||||
);
|
||||
};
|
||||
this.getItem = function(key) {
|
||||
return storage[key];
|
||||
};
|
||||
this.removeItem = function(key) {
|
||||
delete storage[key];
|
||||
this.length--;
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
transaction.executeSql('DELETE FROM storage where id=?', [key]);
|
||||
}
|
||||
);
|
||||
};
|
||||
this.clear = function() {
|
||||
storage = {};
|
||||
this.length = 0;
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
transaction.executeSql('DELETE FROM storage', []);
|
||||
}
|
||||
);
|
||||
};
|
||||
this.key = function(index) {
|
||||
var i = 0;
|
||||
for (var j in storage) {
|
||||
if (i==index) {
|
||||
return j;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
} catch(e) {
|
||||
alert("Database error "+e+".");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof window.openDatabase == "undefined")
|
||||
{
|
||||
navigator.openDatabase = window.openDatabase = dbSetup;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
});
|
||||
var setupDroidDB = function() {
|
||||
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
if (typeof window.openDatabase === "undefined") {
|
||||
setupDroidDB();
|
||||
} else {
|
||||
window.openDatabase_orig = window.openDatabase;
|
||||
window.openDatabase = function(name, version, desc, size){
|
||||
// Some versions of Android will throw a SECURITY_ERR so we need
|
||||
// to catch the exception and seutp our own DB handling.
|
||||
var db = null;
|
||||
try {
|
||||
db = window.openDatabase_orig(name, version, desc, size);
|
||||
}
|
||||
catch (ex) {
|
||||
db = null;
|
||||
}
|
||||
|
||||
if (db == null) {
|
||||
setupDroidDB();
|
||||
return DroidDB_openDatabase(name, version, desc, size);
|
||||
}
|
||||
else {
|
||||
return db;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window.localStorage === "undefined") {
|
||||
navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
|
||||
PhoneGap.waitForInitialization("cupcakeStorage");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
9
framework/assets/www/index.html
Normal file
9
framework/assets/www/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="phonegap-1.0.0.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
2861
framework/assets/www/phonegap.js
Normal file
2861
framework/assets/www/phonegap.js
Normal file
File diff suppressed because it is too large
Load Diff
76
framework/build.xml
Normal file → Executable file
76
framework/build.xml
Normal file → Executable file
@@ -1,5 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="PhoneGap" default="help">
|
||||
<project name="PhoneGap" default="jar">
|
||||
|
||||
<!-- LOAD VERSION -->
|
||||
<loadfile property="version" srcFile="../VERSION">
|
||||
<filterchain>
|
||||
<striplinebreaks/>
|
||||
</filterchain>
|
||||
</loadfile>
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked in in Version
|
||||
@@ -33,6 +40,9 @@
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems. -->
|
||||
<property file="default.properties" />
|
||||
|
||||
<!-- We need to setup the double quote. -->
|
||||
<property name="dblQuote">"</property>
|
||||
|
||||
<!-- Custom Android task to deal with the project target, and import the proper rules.
|
||||
This requires ant 1.6.0 or above. -->
|
||||
@@ -64,21 +74,73 @@
|
||||
-->
|
||||
<setup />
|
||||
|
||||
<target name="move_files">
|
||||
<concat destfile="../example/phonegap.js">
|
||||
<target name="check-javascript" depends="build-javascript">
|
||||
<delete dir="assets/lib"/>
|
||||
<mkdir dir="assets/lib"/>
|
||||
<echo file="assets/lib/lint.js">var alert=function(){},device={},Element={},debug={};</echo>
|
||||
<concat destfile="assets/lib/phonegap-lint.js" append="true">
|
||||
<fileset dir="assets/lib">
|
||||
<include name="lint.js" />
|
||||
</fileset>
|
||||
<fileset dir="assets/www">
|
||||
<include name="phonegap-${version}.js" />
|
||||
</fileset>
|
||||
</concat>
|
||||
|
||||
<exec executable="cmd" os="Windows 7">
|
||||
<arg value="/c"/>
|
||||
<arg value="java"/>
|
||||
<arg value="-cp"/>
|
||||
<arg value="${basedir}/util/js.jar"/>
|
||||
<arg value="org.mozilla.javascript.tools.shell.Main"/>
|
||||
<arg value="${basedir}/util/jslint.js"/>
|
||||
<arg value="${basedir}/js/lib/phonegap-lint.js"/>
|
||||
</exec>
|
||||
<exec executable="java" os="Mac OS X">
|
||||
<arg value="-cp"/>
|
||||
<arg value="../util/js.jar"/>
|
||||
<arg value="org.mozilla.javascript.tools.shell.Main"/>
|
||||
<arg value="../util/jslint.js"/>
|
||||
<arg value="assets/lib/phonegap-lint.js"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- Combine JavaScript files into one phonegap-uncompressed.js file. -->
|
||||
<target name="build-javascript">
|
||||
|
||||
<!-- Clean up existing files -->
|
||||
<delete file="assets/www/phonegap_${version}.js"/>
|
||||
|
||||
<!-- Create uncompressed JS file -->
|
||||
<concat destfile="assets/www/phonegap-${version}.js">
|
||||
<fileset dir="assets/js" includes="phonegap.js.base" />
|
||||
<fileset dir="assets/js" includes="*.js" />
|
||||
</concat>
|
||||
|
||||
<!-- update project files to reference phonegap-x.x.x.min.js -->
|
||||
<replaceregexp match="phonegap(.*)\.js" replace="phonegap-${version}.js" byline="true">
|
||||
<fileset file="assets/www/index.html" />
|
||||
<fileset file="../example/index.html" />
|
||||
</replaceregexp>
|
||||
<replaceregexp match="phonegapVersion = [\u0022].*[\u0022];" replace="phonegapVersion = ${dblQuote}${version}${dblQuote};" byline="true">
|
||||
<fileset file="src/com/phonegap/Device.java" />
|
||||
</replaceregexp>
|
||||
|
||||
<!-- Delete temp file -->
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="move_files, compile">
|
||||
<jar jarfile="phonegap.jar" basedir="bin/classes" excludes="**/R*.class" />
|
||||
<!-- Build PhoneGap jar file that includes all native code, and PhoneGap JS file
|
||||
that includes all JavaScript code.
|
||||
-->
|
||||
<target name="jar" depends="build-javascript, compile">
|
||||
<jar jarfile="phonegap-${version}.jar" basedir="bin/classes" excludes="com/phonegap/R.class,com/phonegap/R$*.class"/>
|
||||
</target>
|
||||
|
||||
<target name="phonegap_debug" depends="move_files, debug">
|
||||
<target name="phonegap_debug" depends="build-javascript, debug">
|
||||
</target>
|
||||
|
||||
<target name="phonegap_release" depends="move_files, release">
|
||||
<target name="phonegap_release" depends="build-javascript, release">
|
||||
</target>
|
||||
|
||||
|
||||
|
||||
@@ -10,5 +10,5 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-8
|
||||
target=android-12
|
||||
apk-configurations=
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<jardesc>
|
||||
<jar path="PhoneGap/PhoneGap.jar"/>
|
||||
<options buildIfNeeded="true" compress="true" descriptionLocation="/PhoneGap/export-phonegap.jardesc" exportErrors="true" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
|
||||
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
|
||||
<selectedProjects/>
|
||||
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
|
||||
<sealing sealJar="false">
|
||||
<packagesToSeal/>
|
||||
<packagesToUnSeal/>
|
||||
</sealing>
|
||||
</manifest>
|
||||
<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
|
||||
<javaElement handleIdentifier="=PhoneGap/src"/>
|
||||
<javaElement handleIdentifier="=PhoneGap/gen"/>
|
||||
</selectedElements>
|
||||
</jardesc>
|
||||
@@ -1,29 +0,0 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
|
||||
package com.phonegap;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
}
|
||||
public static final class drawable {
|
||||
public static final int icon=0x7f020000;
|
||||
}
|
||||
public static final class id {
|
||||
public static final int appView=0x7f050000;
|
||||
public static final int go=0x7f050002;
|
||||
public static final int surface=0x7f050001;
|
||||
}
|
||||
public static final class layout {
|
||||
public static final int main=0x7f030000;
|
||||
public static final int preview=0x7f030001;
|
||||
}
|
||||
public static final class string {
|
||||
public static final int app_name=0x7f040000;
|
||||
public static final int go=0x7f040001;
|
||||
}
|
||||
}
|
||||
BIN
framework/res/drawable/splash.png
Executable file
BIN
framework/res/drawable/splash.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -1,23 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||
android:orientation="horizontal">
|
||||
<SurfaceView android:id="@+id/surface"
|
||||
android:layout_width="fill_parent" android:layout_height="fill_parent"
|
||||
android:layout_weight="1">
|
||||
</SurfaceView>
|
||||
<Button
|
||||
android:id="@+id/go"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
|
||||
android:text="@string/go"
|
||||
android:minWidth="50dip"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginRight="5dip"
|
||||
android:layout_marginTop="5dip"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
19
framework/res/xml/plugins.xml
Normal file
19
framework/res/xml/plugins.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<plugins>
|
||||
<plugin name="App" value="com.phonegap.App"/>
|
||||
<plugin name="Geolocation" value="com.phonegap.GeoBroker"/>
|
||||
<plugin name="Device" value="com.phonegap.Device"/>
|
||||
<plugin name="Accelerometer" value="com.phonegap.AccelListener"/>
|
||||
<plugin name="Compass" value="com.phonegap.CompassListener"/>
|
||||
<plugin name="Media" value="com.phonegap.AudioHandler"/>
|
||||
<plugin name="Camera" value="com.phonegap.CameraLauncher"/>
|
||||
<plugin name="Contacts" value="com.phonegap.ContactManager"/>
|
||||
<plugin name="Crypto" value="com.phonegap.CryptoHandler"/>
|
||||
<plugin name="File" value="com.phonegap.FileUtils"/>
|
||||
<plugin name="Network Status" value="com.phonegap.NetworkManager"/>
|
||||
<plugin name="Notification" value="com.phonegap.Notification"/>
|
||||
<plugin name="Storage" value="com.phonegap.Storage"/>
|
||||
<plugin name="Temperature" value="com.phonegap.TempListener"/>
|
||||
<plugin name="FileTransfer" value="com.phonegap.FileTransfer"/>
|
||||
<plugin name="Capture" value="com.phonegap.Capture"/>
|
||||
</plugins>
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.content.Context;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class AccelBroker {
|
||||
private WebView mAppView;
|
||||
private Context mCtx;
|
||||
private HashMap<String, AccelListener> accelListeners;
|
||||
|
||||
public AccelBroker(WebView view, Context ctx)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mAppView = view;
|
||||
accelListeners = new HashMap<String, AccelListener>();
|
||||
}
|
||||
|
||||
public String start(int freq, String key)
|
||||
{
|
||||
AccelListener listener = new AccelListener(key, freq, mCtx, mAppView);
|
||||
listener.start(freq);
|
||||
accelListeners.put(key, listener);
|
||||
return key;
|
||||
}
|
||||
|
||||
public void stop(String key)
|
||||
{
|
||||
AccelListener acc = accelListeners.get(key);
|
||||
acc.stop();
|
||||
}
|
||||
|
||||
}
|
||||
333
framework/src/com/phonegap/AccelListener.java
Normal file → Executable file
333
framework/src/com/phonegap/AccelListener.java
Normal file → Executable file
@@ -1,80 +1,297 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class AccelListener implements SensorEventListener{
|
||||
/**
|
||||
* This class listens to the accelerometer sensor and stores the latest
|
||||
* acceleration values x,y,z.
|
||||
*/
|
||||
public class AccelListener extends Plugin implements SensorEventListener {
|
||||
|
||||
WebView mAppView;
|
||||
Context mCtx;
|
||||
String mKey;
|
||||
Sensor mSensor;
|
||||
int mTime = 10000;
|
||||
boolean started = false;
|
||||
|
||||
private SensorManager sensorManager;
|
||||
|
||||
private long lastUpdate = -1;
|
||||
|
||||
public AccelListener(String key, int freq, Context ctx, WebView appView)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mAppView = appView;
|
||||
mKey = key;
|
||||
mTime = freq;
|
||||
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
|
||||
|
||||
public static int STOPPED = 0;
|
||||
public static int STARTING = 1;
|
||||
public static int RUNNING = 2;
|
||||
public static int ERROR_FAILED_TO_START = 3;
|
||||
|
||||
public float TIMEOUT = 30000; // Timeout in msec to shut off listener
|
||||
|
||||
float x,y,z; // most recent acceleration values
|
||||
long timestamp; // time of most recent value
|
||||
int status; // status of listener
|
||||
long lastAccessTime; // time the value was last retrieved
|
||||
|
||||
private SensorManager sensorManager;// Sensor manager
|
||||
Sensor mSensor; // Acceleration sensor returned by sensor manager
|
||||
|
||||
/**
|
||||
* Create an accelerometer listener.
|
||||
*/
|
||||
public AccelListener() {
|
||||
this.x = 0;
|
||||
this.y = 0;
|
||||
this.z = 0;
|
||||
this.timestamp = 0;
|
||||
this.setStatus(AccelListener.STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE);
|
||||
}
|
||||
|
||||
public void start(int time)
|
||||
{
|
||||
mTime = time;
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAppView.loadUrl("javascript:navigator.accelerometer.epicFail(" + mKey + ", 'Failed to start')");
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
try {
|
||||
if (action.equals("getStatus")) {
|
||||
int i = this.getStatus();
|
||||
return new PluginResult(status, i);
|
||||
}
|
||||
else if (action.equals("start")) {
|
||||
int i = this.start();
|
||||
return new PluginResult(status, i);
|
||||
}
|
||||
else if (action.equals("stop")) {
|
||||
this.stop();
|
||||
return new PluginResult(status, 0);
|
||||
}
|
||||
else if (action.equals("getAcceleration")) {
|
||||
// If not running, then this is an async call, so don't worry about waiting
|
||||
if (this.status != AccelListener.RUNNING) {
|
||||
int r = this.start();
|
||||
if (r == AccelListener.ERROR_FAILED_TO_START) {
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, AccelListener.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, AccelListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
}
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("x", this.x);
|
||||
r.put("y", this.y);
|
||||
r.put("z", this.z);
|
||||
// TODO: Should timestamp be sent?
|
||||
r.put("timestamp", this.timestamp);
|
||||
return new PluginResult(status, r);
|
||||
}
|
||||
else if (action.equals("setTimeout")) {
|
||||
try {
|
||||
float timeout = Float.parseFloat(args.getString(0));
|
||||
this.setTimeout(timeout);
|
||||
return new PluginResult(status, 0);
|
||||
} catch (NumberFormatException e) {
|
||||
status = PluginResult.Status.INVALID_ACTION;
|
||||
e.printStackTrace();
|
||||
} catch (JSONException e) {
|
||||
status = PluginResult.Status.JSON_EXCEPTION;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
float f = this.getTimeout();
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
if(started)
|
||||
sensorManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("getStatus")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("getAcceleration")) {
|
||||
// Can only return value if RUNNING
|
||||
if (this.status == RUNNING) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by AccelBroker when listener is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER)
|
||||
return;
|
||||
long curTime = System.currentTimeMillis();
|
||||
if (lastUpdate == -1 || (curTime - lastUpdate) > mTime) {
|
||||
lastUpdate = curTime;
|
||||
|
||||
float x = event.values[0];
|
||||
float y = event.values[1];
|
||||
float z = event.values[2];
|
||||
//mAppView.loadUrl("javascript:gotAccel(" + x + ", " + y + "," + z + " )");
|
||||
mAppView.loadUrl("javascript:navigator.accelerometer.gotAccel(" + mKey + "," + x + "," + y + "," + z + ")");
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Start listening for acceleration sensor.
|
||||
*
|
||||
* @return status of listener
|
||||
*/
|
||||
public int start() {
|
||||
|
||||
// If already starting or running, then just return
|
||||
if ((this.status == AccelListener.RUNNING) || (this.status == AccelListener.STARTING)) {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
// Get accelerometer from sensor manager
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
|
||||
|
||||
// If found, then register as listener
|
||||
if ((list != null) && (list.size() > 0)) {
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_FASTEST);
|
||||
this.setStatus(AccelListener.STARTING);
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// If error, then set status to error
|
||||
else {
|
||||
this.setStatus(AccelListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to acceleration sensor.
|
||||
*/
|
||||
public void stop() {
|
||||
if (this.status != AccelListener.STOPPED) {
|
||||
this.sensorManager.unregisterListener(this);
|
||||
}
|
||||
this.setStatus(AccelListener.STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the accuracy of the sensor has changed.
|
||||
*
|
||||
* @param sensor
|
||||
* @param accuracy
|
||||
*/
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sensor listener event.
|
||||
*
|
||||
* @param SensorEvent event
|
||||
*/
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
|
||||
// Only look at accelerometer events
|
||||
if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not running, then just return
|
||||
if (this.status == AccelListener.STOPPED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save time that event was received
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
this.x = event.values[0];
|
||||
this.y = event.values[1];
|
||||
this.z = event.values[2];
|
||||
|
||||
this.setStatus(AccelListener.RUNNING);
|
||||
|
||||
// If values haven't been read for TIMEOUT time, then turn off accelerometer sensor to save power
|
||||
if ((this.timestamp - this.lastAccessTime) > this.TIMEOUT) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of accelerometer sensor.
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout to turn off accelerometer sensor if getX() hasn't been called.
|
||||
*
|
||||
* @param timeout Timeout in msec.
|
||||
*/
|
||||
public void setTimeout(float timeout) {
|
||||
this.TIMEOUT = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timeout to turn off accelerometer sensor if getX() hasn't been called.
|
||||
*
|
||||
* @return timeout in msec
|
||||
*/
|
||||
public float getTimeout() {
|
||||
return this.TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status and send it to JavaScript.
|
||||
* @param status
|
||||
*/
|
||||
private void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
175
framework/src/com/phonegap/App.java
Executable file
175
framework/src/com/phonegap/App.java
Executable file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||
*/
|
||||
public class App extends Plugin {
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
if (action.equals("clearCache")) {
|
||||
this.clearCache();
|
||||
}
|
||||
else if (action.equals("loadUrl")) {
|
||||
this.loadUrl(args.getString(0), args.optJSONObject(1));
|
||||
}
|
||||
else if (action.equals("cancelLoadUrl")) {
|
||||
this.cancelLoadUrl();
|
||||
}
|
||||
else if (action.equals("clearHistory")) {
|
||||
this.clearHistory();
|
||||
}
|
||||
else if (action.equals("overrideBackbutton")) {
|
||||
this.overrideBackbutton(args.getBoolean(0));
|
||||
}
|
||||
else if (action.equals("isBackbuttonOverridden")) {
|
||||
boolean b = this.isBackbuttonOverridden();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("exitApp")) {
|
||||
this.exitApp();
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
*/
|
||||
public void clearCache() {
|
||||
((DroidGap)this.ctx).clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url
|
||||
* @param props Properties that can be passed in to the DroidGap activity (i.e. loadingDialog, wait, ...)
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void loadUrl(String url, JSONObject props) throws JSONException {
|
||||
System.out.println("App.loadUrl("+url+","+props+")");
|
||||
int wait = 0;
|
||||
boolean usePhoneGap = true;
|
||||
boolean clearPrev = false;
|
||||
|
||||
// If there are properties, then set them on the Activity
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
if (props != null) {
|
||||
JSONArray keys = props.names();
|
||||
for (int i=0; i<keys.length(); i++) {
|
||||
String key = keys.getString(i);
|
||||
if (key.equals("wait")) {
|
||||
wait = props.getInt(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("usephonegap")) {
|
||||
usePhoneGap = props.getBoolean(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("clearprev")) {
|
||||
clearPrev = props.getBoolean(key);
|
||||
}
|
||||
else {
|
||||
Object value = props.get(key);
|
||||
if (value == null) {
|
||||
|
||||
}
|
||||
else if (value.getClass().equals(String.class)) {
|
||||
params.put(key, (String)value);
|
||||
}
|
||||
else if (value.getClass().equals(Boolean.class)) {
|
||||
params.put(key, (Boolean)value);
|
||||
}
|
||||
else if (value.getClass().equals(Integer.class)) {
|
||||
params.put(key, (Integer)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If wait property, then delay loading
|
||||
|
||||
if (wait > 0) {
|
||||
try {
|
||||
synchronized(this) {
|
||||
this.wait(wait);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
((DroidGap)this.ctx).showWebPage(url, usePhoneGap, clearPrev, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel loadUrl before it has been loaded.
|
||||
*/
|
||||
public void cancelLoadUrl() {
|
||||
((DroidGap)this.ctx).cancelLoadUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear web history in this web view.
|
||||
*/
|
||||
public void clearHistory() {
|
||||
((DroidGap)this.ctx).clearHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
public void overrideBackbutton(boolean override) {
|
||||
System.out.println("WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!");
|
||||
((DroidGap)this.ctx).bound = override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the Android back button is overridden by the user.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isBackbuttonOverridden() {
|
||||
return ((DroidGap)this.ctx).bound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit the Android application.
|
||||
*/
|
||||
public void exitApp() {
|
||||
((DroidGap)this.ctx).finish();
|
||||
}
|
||||
|
||||
}
|
||||
444
framework/src/com/phonegap/AudioHandler.java
Normal file → Executable file
444
framework/src/com/phonegap/AudioHandler.java
Normal file → Executable file
@@ -1,193 +1,299 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.MediaPlayer.OnErrorListener;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaPlayer.OnBufferingUpdateListener;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
import android.util.Log;
|
||||
|
||||
public class AudioHandler implements OnCompletionListener, OnPreparedListener, OnErrorListener {
|
||||
private MediaRecorder recorder;
|
||||
private boolean isRecording = false;
|
||||
MediaPlayer mPlayer;
|
||||
private boolean isPlaying = false;
|
||||
private String recording;
|
||||
private String saveFile;
|
||||
private Context mCtx;
|
||||
/**
|
||||
* This class called by PhonegapActivity to play and record audio.
|
||||
* The file can be local or over a network using http.
|
||||
*
|
||||
* Audio formats supported (tested):
|
||||
* .mp3, .wav
|
||||
*
|
||||
* Local audio files must reside in one of two places:
|
||||
* android_asset: file name must start with /android_asset/sound.mp3
|
||||
* sdcard: file name is just sound.mp3
|
||||
*/
|
||||
public class AudioHandler extends Plugin {
|
||||
|
||||
HashMap<String,AudioPlayer> players; // Audio player object
|
||||
|
||||
public AudioHandler(String file, Context ctx) {
|
||||
this.recording = file;
|
||||
this.mCtx = ctx;
|
||||
}
|
||||
|
||||
protected void startRecording(String file){
|
||||
if (!isRecording){
|
||||
saveFile=file;
|
||||
recorder = new MediaRecorder();
|
||||
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
|
||||
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
|
||||
recorder.setOutputFile(this.recording);
|
||||
try {
|
||||
recorder.prepare();
|
||||
} catch (IllegalStateException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
isRecording = true;
|
||||
recorder.start();
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AudioHandler() {
|
||||
this.players = new HashMap<String,AudioPlayer>();
|
||||
}
|
||||
|
||||
private void moveFile(String file) {
|
||||
/* this is a hack to save the file as the specified name */
|
||||
File f = new File (this.recording);
|
||||
f.renameTo(new File("/sdcard" + file));
|
||||
}
|
||||
|
||||
protected void stopRecording(){
|
||||
try{
|
||||
if((recorder != null)&&(isRecording))
|
||||
{
|
||||
isRecording = false;
|
||||
recorder.stop();
|
||||
recorder.release();
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
try {
|
||||
if (action.equals("startRecordingAudio")) {
|
||||
this.startRecordingAudio(args.getString(0), args.getString(1));
|
||||
}
|
||||
moveFile(saveFile);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
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("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);
|
||||
}
|
||||
}
|
||||
|
||||
protected void startPlaying(String file) {
|
||||
if (isPlaying==false) {
|
||||
try {
|
||||
mPlayer = new MediaPlayer();
|
||||
isPlaying=true;
|
||||
Log.d("Audio startPlaying", "audio: " + file);
|
||||
if (isStreaming(file))
|
||||
{
|
||||
Log.d("AudioStartPlaying", "Streaming");
|
||||
// Streaming prepare async
|
||||
mPlayer.setDataSource(file);
|
||||
mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mPlayer.prepareAsync();
|
||||
} else {
|
||||
Log.d("AudioStartPlaying", "File");
|
||||
// Not streaming prepare synchronous, abstract base directory
|
||||
mPlayer.setDataSource("/sdcard/" + file);
|
||||
mPlayer.prepare();
|
||||
}
|
||||
mPlayer.setOnPreparedListener(this);
|
||||
} catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stopPlaying() {
|
||||
if (isPlaying) {
|
||||
mPlayer.stop();
|
||||
mPlayer.release();
|
||||
isPlaying=false;
|
||||
}
|
||||
}
|
||||
|
||||
public void onCompletion(MediaPlayer mPlayer) {
|
||||
mPlayer.stop();
|
||||
mPlayer.release();
|
||||
isPlaying=false;
|
||||
}
|
||||
|
||||
protected long getCurrentPosition() {
|
||||
if (isPlaying)
|
||||
{
|
||||
return(mPlayer.getCurrentPosition());
|
||||
} else { return(-1); }
|
||||
}
|
||||
|
||||
private boolean isStreaming(String file)
|
||||
{
|
||||
if (file.contains("http://")) {
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("getCurrentPositionAudio")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected long getDuration(String file) {
|
||||
long duration = -2;
|
||||
if (!isPlaying & !isStreaming(file)) {
|
||||
try {
|
||||
mPlayer = new MediaPlayer();
|
||||
mPlayer.setDataSource("/sdcard/" + file);
|
||||
mPlayer.prepare();
|
||||
duration = mPlayer.getDuration();
|
||||
mPlayer.release();
|
||||
} catch (Exception e) { e.printStackTrace(); return(-3); }
|
||||
} else
|
||||
if (isPlaying & !isStreaming(file)) {
|
||||
duration = mPlayer.getDuration();
|
||||
} else
|
||||
if (isPlaying & isStreaming(file)) {
|
||||
try {
|
||||
duration = mPlayer.getDuration();
|
||||
} catch (Exception e) { e.printStackTrace(); return(-4); }
|
||||
}else { return -1; }
|
||||
return duration;
|
||||
}
|
||||
|
||||
public void onPrepared(MediaPlayer mPlayer) {
|
||||
if (isPlaying) {
|
||||
mPlayer.setOnCompletionListener(this);
|
||||
mPlayer.setOnBufferingUpdateListener(new OnBufferingUpdateListener()
|
||||
{
|
||||
public void onBufferingUpdate(MediaPlayer mPlayer, int percent)
|
||||
{
|
||||
/* TODO: call back, e.g. update outer progress bar */
|
||||
Log.d("AudioOnBufferingUpdate", "percent: " + percent);
|
||||
}
|
||||
});
|
||||
mPlayer.start();
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) {
|
||||
Log.e("AUDIO onError", "error " + arg1 + " " + arg2);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop all audio players and recorders.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
java.util.Set<Entry<String,AudioPlayer>> s = this.players.entrySet();
|
||||
java.util.Iterator<Entry<String,AudioPlayer>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,AudioPlayer> entry = it.next();
|
||||
AudioPlayer audio = entry.getValue();
|
||||
audio.destroy();
|
||||
}
|
||||
this.players.clear();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
protected void setAudioOutputDevice(int output){
|
||||
// Changes the default audio output device to speaker or earpiece
|
||||
AudioManager audiMgr = (AudioManager) mCtx.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (output == (2))
|
||||
/**
|
||||
* Release the audio player instance to save memory.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
private boolean release(String id) {
|
||||
if (!this.players.containsKey(id)) {
|
||||
return false;
|
||||
}
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
this.players.remove(id);
|
||||
audio.destroy();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording and save the specified file.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecordingAudio(String id, String file) {
|
||||
// If already recording, then just return;
|
||||
if (this.players.containsKey(id)) {
|
||||
return;
|
||||
}
|
||||
AudioPlayer audio = new AudioPlayer(this, id);
|
||||
this.players.put(id, audio);
|
||||
audio.startRecording(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop recording and save to the file specified when recording started.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
public void stopRecordingAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopRecording();
|
||||
this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or resume playing audio file.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @param file The name of the audio file.
|
||||
*/
|
||||
public void startPlayingAudio(String id, String file) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio == null) {
|
||||
audio = new AudioPlayer(this, id);
|
||||
this.players.put(id, audio);
|
||||
}
|
||||
audio.startPlaying(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a location.
|
||||
*
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @param miliseconds int: number of milliseconds to skip 1000 = 1 second
|
||||
*/
|
||||
public void seekToAudio(String id, int milliseconds) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.seekToPlaying(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
public void pausePlayingAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.pausePlaying();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop playing the audio file.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
*/
|
||||
public void stopPlayingAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.stopPlaying();
|
||||
//audio.destroy();
|
||||
//this.players.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @return position in msec
|
||||
*/
|
||||
public float getCurrentPositionAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return(audio.getCurrentPosition()/1000.0f);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration of the audio file.
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
*/
|
||||
public float getDurationAudio(String id, String file) {
|
||||
|
||||
// Get audio file
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return(audio.getDuration(file));
|
||||
}
|
||||
|
||||
// If not already open, then open the file
|
||||
else {
|
||||
audio = new AudioPlayer(this, id);
|
||||
this.players.put(id, audio);
|
||||
return(audio.getDuration(file));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the audio device to be used for playback.
|
||||
*
|
||||
* @param output 1=earpiece, 2=speaker
|
||||
*/
|
||||
public void setAudioOutputDevice(int output) {
|
||||
AudioManager audiMgr = (AudioManager) this.ctx.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (output == 2) {
|
||||
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
|
||||
else if (output == (1)){
|
||||
}
|
||||
else if (output == 1) {
|
||||
audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
|
||||
}else
|
||||
Log.e("AudioHandler setAudioOutputDevice", " unknown output device");
|
||||
}
|
||||
|
||||
protected int getAudioOutputDevice(){
|
||||
AudioManager audiMgr = (AudioManager) mCtx.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE)
|
||||
}
|
||||
else {
|
||||
System.out.println("AudioHandler.setAudioOutputDevice() Error: Unknown output device.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the audio device to be used for playback.
|
||||
*
|
||||
* @return 1=earpiece, 2=speaker
|
||||
*/
|
||||
public int getAudioOutputDevice() {
|
||||
AudioManager audiMgr = (AudioManager) this.ctx.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
|
||||
return 1;
|
||||
else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER)
|
||||
}
|
||||
else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) {
|
||||
return 2;
|
||||
else
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
420
framework/src/com/phonegap/AudioPlayer.java
Executable file
420
framework/src/com/phonegap/AudioPlayer.java
Executable file
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnErrorListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class implements the audio playback and recording capabilities used by PhoneGap.
|
||||
* It is called by the AudioHandler PhoneGap class.
|
||||
* Only one file can be played or recorded per class instance.
|
||||
*
|
||||
* Local audio files must reside in one of two places:
|
||||
* android_asset: file name must start with /android_asset/sound.mp3
|
||||
* sdcard: file name is just sound.mp3
|
||||
*/
|
||||
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
|
||||
|
||||
private static final String LOG_TAG = "AudioPlayer";
|
||||
|
||||
// AudioPlayer states
|
||||
private static int MEDIA_NONE = 0;
|
||||
private static int MEDIA_STARTING = 1;
|
||||
private static int MEDIA_RUNNING = 2;
|
||||
private static int MEDIA_PAUSED = 3;
|
||||
private static int MEDIA_STOPPED = 4;
|
||||
|
||||
// AudioPlayer message ids
|
||||
private static int MEDIA_STATE = 1;
|
||||
private static int MEDIA_DURATION = 2;
|
||||
private static int MEDIA_POSITION = 3;
|
||||
private static int MEDIA_ERROR = 9;
|
||||
|
||||
// AudioPlayer error codes
|
||||
private static int MEDIA_ERROR_PLAY_MODE_SET = 1;
|
||||
private static int MEDIA_ERROR_ALREADY_RECORDING = 2;
|
||||
private static int MEDIA_ERROR_STARTING_RECORDING = 3;
|
||||
private static int MEDIA_ERROR_RECORD_MODE_SET = 4;
|
||||
private static int MEDIA_ERROR_STARTING_PLAYBACK = 5;
|
||||
private static int MEDIA_ERROR_RESUME_STATE = 6;
|
||||
private static int MEDIA_ERROR_PAUSE_STATE = 7;
|
||||
private static int MEDIA_ERROR_STOP_STATE = 8;
|
||||
|
||||
private AudioHandler handler; // The AudioHandler object
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private int state = MEDIA_NONE; // State of recording or playback
|
||||
private String audioFile = null; // File name to play or record to
|
||||
private float duration = -1; // Duration of audio
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
|
||||
private MediaPlayer mPlayer = null; // Audio player object
|
||||
private boolean prepareOnly = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param handler The audio handler object
|
||||
* @param id The id of this audio player
|
||||
*/
|
||||
public AudioPlayer(AudioHandler handler, String id) {
|
||||
this.handler = handler;
|
||||
this.id = id;
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy player and stop audio playing or recording.
|
||||
*/
|
||||
public void destroy() {
|
||||
|
||||
// Stop any play or record
|
||||
if (this.mPlayer != null) {
|
||||
this.stopPlaying();
|
||||
this.mPlayer.release();
|
||||
this.mPlayer = null;
|
||||
}
|
||||
if (this.recorder != null) {
|
||||
this.stopRecording();
|
||||
this.recorder.release();
|
||||
this.recorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start recording the specified file.
|
||||
*
|
||||
* @param file The name of the file
|
||||
*/
|
||||
public void startRecording(String file) {
|
||||
if (this.mPlayer != null) {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PLAY_MODE_SET+");");
|
||||
}
|
||||
|
||||
// Make sure we're not already recording
|
||||
else if (this.recorder == null) {
|
||||
this.audioFile = file;
|
||||
this.recorder = new MediaRecorder();
|
||||
this.recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
|
||||
this.recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT); // THREE_GPP);
|
||||
this.recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT); //AMR_NB);
|
||||
this.recorder.setOutputFile(this.tempFile);
|
||||
try {
|
||||
this.recorder.prepare();
|
||||
this.recorder.start();
|
||||
this.setState(MEDIA_RUNNING);
|
||||
return;
|
||||
} catch (IllegalStateException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STARTING_RECORDING+");");
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_ALREADY_RECORDING+");");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save temporary recorded file to specified name
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public void moveFile(String file) {
|
||||
|
||||
/* this is a hack to save the file as the specified name */
|
||||
File f = new File(this.tempFile);
|
||||
f.renameTo(new File("/sdcard/" + file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop recording and save to the file specified when recording started.
|
||||
*/
|
||||
public void stopRecording() {
|
||||
if (this.recorder != null) {
|
||||
try{
|
||||
if (this.state == MEDIA_RUNNING) {
|
||||
this.recorder.stop();
|
||||
this.setState(MEDIA_STOPPED);
|
||||
}
|
||||
this.moveFile(this.audioFile);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start or resume playing audio file.
|
||||
*
|
||||
* @param file The name of the audio file.
|
||||
*/
|
||||
public void startPlaying(String file) {
|
||||
if (this.recorder != null) {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RECORD_MODE_SET+");");
|
||||
}
|
||||
|
||||
// If this is a new request to play audio, or stopped
|
||||
else if ((this.mPlayer == null) || (this.state == MEDIA_STOPPED)) {
|
||||
try {
|
||||
// If stopped, then reset player
|
||||
if (this.mPlayer != null) {
|
||||
this.mPlayer.reset();
|
||||
}
|
||||
// Otherwise, create a new one
|
||||
else {
|
||||
this.mPlayer = new MediaPlayer();
|
||||
}
|
||||
this.audioFile = file;
|
||||
|
||||
// If streaming file
|
||||
if (this.isStreaming(file)) {
|
||||
this.mPlayer.setDataSource(file);
|
||||
this.mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
this.setState(MEDIA_STARTING);
|
||||
this.mPlayer.setOnPreparedListener(this);
|
||||
this.mPlayer.prepareAsync();
|
||||
}
|
||||
|
||||
// If local file
|
||||
else {
|
||||
if (file.startsWith("/android_asset/")) {
|
||||
String f = file.substring(15);
|
||||
android.content.res.AssetFileDescriptor fd = this.handler.ctx.getBaseContext().getAssets().openFd(f);
|
||||
this.mPlayer.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
|
||||
}
|
||||
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("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STARTING_PLAYBACK+");");
|
||||
}
|
||||
}
|
||||
|
||||
// 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("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RESUME_STATE+");");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek or jump to a new time in the track.
|
||||
*/
|
||||
public void seekToPlaying(int milliseconds) {
|
||||
if (this.mPlayer != null) {
|
||||
this.mPlayer.seekTo(milliseconds);
|
||||
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_POSITION+", "+milliseconds/1000.0f+");");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
*/
|
||||
public void pausePlaying() {
|
||||
|
||||
// If playing, then pause
|
||||
if (this.state == MEDIA_RUNNING) {
|
||||
this.mPlayer.pause();
|
||||
this.setState(MEDIA_PAUSED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: "+this.state);
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PAUSE_STATE+");");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop playing the audio file.
|
||||
*/
|
||||
public void stopPlaying() {
|
||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
||||
this.mPlayer.stop();
|
||||
this.setState(MEDIA_STOPPED);
|
||||
}
|
||||
else {
|
||||
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: "+this.state);
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STOP_STATE+");");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when playback of a media source has completed.
|
||||
*
|
||||
* @param mPlayer The MediaPlayer that reached the end of the file
|
||||
*/
|
||||
public void onCompletion(MediaPlayer mPlayer) {
|
||||
this.setState(MEDIA_STOPPED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current position of playback.
|
||||
*
|
||||
* @return position in msec or -1 if not playing
|
||||
*/
|
||||
public long getCurrentPosition() {
|
||||
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
|
||||
int curPos = this.mPlayer.getCurrentPosition();
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_POSITION+", "+curPos/1000.0f+");");
|
||||
return curPos;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if playback file is streaming or local.
|
||||
* It is streaming if file name starts with "http://"
|
||||
*
|
||||
* @param file The file name
|
||||
* @return T=streaming, F=local
|
||||
*/
|
||||
public boolean isStreaming(String file) {
|
||||
if (file.contains("http://")) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the duration of the audio file.
|
||||
*
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
* -1=can't be determined
|
||||
* -2=not allowed
|
||||
*/
|
||||
public float getDuration(String file) {
|
||||
|
||||
// Can't get duration of recording
|
||||
if (this.recorder != null) {
|
||||
return(-2); // not allowed
|
||||
}
|
||||
|
||||
// If audio file already loaded and started, then return duration
|
||||
if (this.mPlayer != null) {
|
||||
return this.duration;
|
||||
}
|
||||
|
||||
// If no player yet, then create one
|
||||
else {
|
||||
this.prepareOnly = true;
|
||||
this.startPlaying(file);
|
||||
|
||||
// This will only return value for local, since streaming
|
||||
// file hasn't been read yet.
|
||||
return this.duration;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the media source is ready for playback.
|
||||
*
|
||||
* @param mPlayer The MediaPlayer that is ready for playback
|
||||
*/
|
||||
public void onPrepared(MediaPlayer mPlayer) {
|
||||
// Listen for playback completion
|
||||
this.mPlayer.setOnCompletionListener(this);
|
||||
|
||||
// If start playing after prepared
|
||||
if (!this.prepareOnly) {
|
||||
|
||||
// Start playing
|
||||
this.mPlayer.start();
|
||||
|
||||
// Set player init flag
|
||||
this.setState(MEDIA_RUNNING);
|
||||
}
|
||||
|
||||
// Save off duration
|
||||
this.duration = getDurationInSeconds();
|
||||
this.prepareOnly = false;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_DURATION+","+this.duration+");");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* By default Android returns the length of audio in mills but we want seconds
|
||||
*
|
||||
* @return length of clip in seconds
|
||||
*/
|
||||
private float getDurationInSeconds() {
|
||||
return (this.mPlayer.getDuration() / 1000.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||
* (other errors will throw exceptions at method call time).
|
||||
*
|
||||
* @param mPlayer the MediaPlayer the error pertains to
|
||||
* @param arg1 the type of error that has occurred: (MEDIA_ERROR_UNKNOWN, MEDIA_ERROR_SERVER_DIED)
|
||||
* @param arg2 an extra code, specific to the error.
|
||||
*/
|
||||
public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) {
|
||||
Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2+")");
|
||||
|
||||
// TODO: Not sure if this needs to be sent?
|
||||
this.mPlayer.stop();
|
||||
this.mPlayer.release();
|
||||
|
||||
// Send error notification to JavaScript
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+arg1+");");
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the state and send it to JavaScript.
|
||||
*
|
||||
* @param state
|
||||
*/
|
||||
private void setState(int state) {
|
||||
if (this.state != state) {
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_STATE+", "+state+");");
|
||||
}
|
||||
|
||||
this.state = state;
|
||||
}
|
||||
}
|
||||
407
framework/src/com/phonegap/CallbackServer.java
Executable file
407
framework/src/com/phonegap/CallbackServer.java
Executable file
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
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.net.URLEncoder;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap.
|
||||
* 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 {
|
||||
|
||||
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() {
|
||||
//System.out.println("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 PhoneGap 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 PhoneGap app being loaded
|
||||
*/
|
||||
public void init(String url) {
|
||||
//System.out.println("CallbackServer.start("+url+")");
|
||||
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
//System.out.println("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();
|
||||
//System.out.println("CallbackServer -- using port " +this.port);
|
||||
this.token = java.util.UUID.randomUUID().toString();
|
||||
//System.out.println("CallbackServer -- using token "+this.token);
|
||||
|
||||
while (this.active) {
|
||||
//System.out.println("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 = "";
|
||||
//System.out.println("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))) {
|
||||
//System.out.println("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
|
||||
//System.out.println("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) {
|
||||
//System.out.println("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 {
|
||||
//System.out.println("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 ";
|
||||
}
|
||||
//System.out.println("CallbackServer: response="+response);
|
||||
//System.out.println("CallbackServer: closing output");
|
||||
output.writeBytes(response);
|
||||
output.flush();
|
||||
}
|
||||
output.close();
|
||||
xhrReader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.active = false;
|
||||
//System.out.println("CallbackServer.startServer() - EXIT");
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop server.
|
||||
* This stops the thread that the server is running on.
|
||||
*/
|
||||
public void stopServer() {
|
||||
//System.out.println("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() {
|
||||
int size = this.javascript.size();
|
||||
//System.out.println("getSize() = " + size);
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next JavaScript statement and remove from list.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public String getJavascript() {
|
||||
if (this.javascript.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
String statement = this.javascript.remove(0);
|
||||
//System.out.println("CallbackServer.getJavascript() = " + statement);
|
||||
if (this.javascript.size() == 0) {
|
||||
synchronized (this) {
|
||||
this.empty = true;
|
||||
}
|
||||
}
|
||||
return statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a JavaScript statement to the list.
|
||||
*
|
||||
* @param statement
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
//System.out.println("CallbackServer.sendJavascript("+statement+")");
|
||||
this.javascript.add(statement);
|
||||
synchronized (this) {
|
||||
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 */
|
||||
}
|
||||
378
framework/src/com/phonegap/CameraLauncher.java
Normal file → Executable file
378
framework/src/com/phonegap/CameraLauncher.java
Normal file → Executable file
@@ -1,34 +1,372 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import android.webkit.WebView;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class CameraLauncher {
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
* and returns the captured image. When the camera view is closed, the screen displayed before
|
||||
* the camera view was shown is redisplayed.
|
||||
*/
|
||||
public class CameraLauncher extends Plugin {
|
||||
|
||||
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 PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
|
||||
private static final int CAMERA = 1; // Take picture from camera
|
||||
private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android)
|
||||
|
||||
private static final int JPEG = 0; // Take a picture of type JPEG
|
||||
private static final int PNG = 1; // Take a picture of type PNG
|
||||
|
||||
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
private int targetWidth; // desired width of the image
|
||||
private int targetHeight; // desired height of the image
|
||||
private Uri imageUri; // Uri of captured image
|
||||
public String callbackId;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CameraLauncher() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
this.callbackId = callbackId;
|
||||
|
||||
private WebView mAppView;
|
||||
private DroidGap mGap;
|
||||
int quality;
|
||||
|
||||
CameraLauncher(WebView view, DroidGap gap)
|
||||
{
|
||||
mAppView = view;
|
||||
mGap = gap;
|
||||
try {
|
||||
if (action.equals("takePicture")) {
|
||||
int destType = DATA_URL;
|
||||
if (args.length() > 1) {
|
||||
destType = args.getInt(1);
|
||||
}
|
||||
int srcType = CAMERA;
|
||||
if (args.length() > 2) {
|
||||
srcType = args.getInt(2);
|
||||
}
|
||||
if (args.length() > 3) {
|
||||
this.targetWidth = args.getInt(3);
|
||||
}
|
||||
if (args.length() > 4) {
|
||||
this.targetHeight = args.getInt(4);
|
||||
}
|
||||
int encodingType = JPEG;
|
||||
if (args.length() > 5) {
|
||||
encodingType = args.getInt(5);
|
||||
}
|
||||
if (srcType == CAMERA) {
|
||||
this.takePicture(args.getInt(0), destType, encodingType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(args.getInt(0), srcType, destType);
|
||||
}
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public void takePicture(int quality)
|
||||
{
|
||||
mGap.startCamera(quality);
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Take a picture with the camera.
|
||||
* When an image is captured or the camera view is cancelled, the result is returned
|
||||
* in PhonegapActivity.onActivityResult, which forwards the result to this.onActivityResult.
|
||||
*
|
||||
* The image can either be returned as a base64 string or a URI that points to the file.
|
||||
* To display base64 string in an img tag, set the source to:
|
||||
* img.src="data:image/jpeg;base64,"+result;
|
||||
* or to display URI in an img tag
|
||||
* img.src=result;
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
* @param returnType Set the type of image to return.
|
||||
*/
|
||||
public void takePicture(int quality, int returnType, int encodingType) {
|
||||
this.mQuality = quality;
|
||||
|
||||
// Display camera
|
||||
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
// TODO: What if there isn't any external storage?
|
||||
File photo = createCaptureFile(encodingType);
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA+1)*16 + returnType+1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a file in the applications temporary directory based upon the supplied encoding.
|
||||
*
|
||||
* @param encodingType of the image to be taken
|
||||
* @return a File object pointing to the temporary picture
|
||||
*/
|
||||
private File createCaptureFile(int encodingType) {
|
||||
File photo = null;
|
||||
if (encodingType == JPEG) {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.jpg");
|
||||
} else {
|
||||
photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.png");
|
||||
}
|
||||
return photo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get image from photo library.
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
* @param srcType The album to get image from.
|
||||
* @param returnType Set the type of image to return.
|
||||
*/
|
||||
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
|
||||
public void getImage(int quality, int srcType, int returnType) {
|
||||
this.mQuality = quality;
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.setType("image/*");
|
||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
this.ctx.startActivityForResult((Plugin) this, Intent.createChooser(intent,
|
||||
new String("Get Picture")), (srcType+1)*16 + returnType + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the bitmap according to the requested size.
|
||||
*
|
||||
* @param bitmap The bitmap to scale.
|
||||
* @return Bitmap A new Bitmap object of the same bitmap after scaling.
|
||||
*/
|
||||
public Bitmap scaleBitmap(Bitmap bitmap) {
|
||||
int newWidth = this.targetWidth;
|
||||
int newHeight = this.targetHeight;
|
||||
int origWidth = bitmap.getWidth();
|
||||
int origHeight = bitmap.getHeight();
|
||||
|
||||
// If no new width or height were specified return the original bitmap
|
||||
if (newWidth <= 0 && newHeight <= 0) {
|
||||
return bitmap;
|
||||
}
|
||||
// Only the width was specified
|
||||
else if (newWidth > 0 && newHeight <= 0) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
}
|
||||
// only the height was specified
|
||||
else if (newWidth <= 0 && newHeight > 0) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
}
|
||||
// If the user specified both a positive width and height
|
||||
// (potentially different aspect ratio) then the width or height is
|
||||
// scaled so that the image fits while maintaining aspect ratio.
|
||||
// Alternatively, the specified width and height could have been
|
||||
// kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
|
||||
// would result in whitespace in the new image.
|
||||
else {
|
||||
double newRatio = newWidth / (double)newHeight;
|
||||
double origRatio = origWidth / (double)origHeight;
|
||||
|
||||
if (origRatio > newRatio) {
|
||||
newHeight = (newWidth * origHeight) / origWidth;
|
||||
} else if (origRatio < newRatio) {
|
||||
newWidth = (newHeight * origWidth) / origHeight;
|
||||
}
|
||||
}
|
||||
|
||||
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
|
||||
}
|
||||
|
||||
/* Return Base64 Encoded String to Javascript */
|
||||
public void processPicture( String js_out )
|
||||
{
|
||||
mAppView.loadUrl("javascript:navigator.camera.win('" + js_out + "');");
|
||||
/**
|
||||
* Called when the camera view exits.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
public 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;
|
||||
|
||||
// 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.getContentResolver(), imageUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Uri uri = intent.getData();
|
||||
android.content.ContentResolver resolver = this.ctx.getContentResolver();
|
||||
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
}
|
||||
|
||||
bitmap = scaleBitmap(bitmap);
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
this.processPicture(bitmap);
|
||||
}
|
||||
|
||||
// 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.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
System.out.println("Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
System.out.println("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.getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
|
||||
os.close();
|
||||
|
||||
// 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();
|
||||
} 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.getContentResolver();
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
try {
|
||||
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
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) {
|
||||
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!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void failPicture(String err)
|
||||
{
|
||||
mAppView.loadUrl("javascript:navigator.camera.fail('" + err + "');");
|
||||
/**
|
||||
* Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript.
|
||||
*
|
||||
* @param bitmap
|
||||
*/
|
||||
public void processPicture(Bitmap bitmap) {
|
||||
ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
|
||||
try {
|
||||
if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) {
|
||||
byte[] code = jpeg_data.toByteArray();
|
||||
byte[] output = Base64.encodeBase64(code);
|
||||
String js_out = new String(output);
|
||||
this.success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId);
|
||||
js_out = null;
|
||||
output = null;
|
||||
code = null;
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
this.failPicture("Error compressing image.");
|
||||
}
|
||||
jpeg_data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error message to JavaScript.
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
public void failPicture(String err) {
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,218 +0,0 @@
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Bitmap.CompressFormat;
|
||||
import android.hardware.Camera;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MenuItem;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class CameraPreview extends Activity implements SurfaceHolder.Callback{
|
||||
|
||||
private static final String TAG = "PhoneGapCamera";
|
||||
private SurfaceView mSurfaceView;
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
|
||||
private RelativeLayout root;
|
||||
|
||||
Camera mCamera;
|
||||
boolean mPreviewRunning = false;
|
||||
|
||||
int quality;
|
||||
Intent mIntent;
|
||||
|
||||
public void onCreate(Bundle icicle)
|
||||
{
|
||||
super.onCreate(icicle);
|
||||
|
||||
Log.e(TAG, "onCreate");
|
||||
|
||||
getWindow().setFormat(PixelFormat.TRANSLUCENT);
|
||||
|
||||
RelativeLayout.LayoutParams containerParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT);
|
||||
LinearLayout.LayoutParams surfaceParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F);
|
||||
RelativeLayout.LayoutParams buttonParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
root = new RelativeLayout(this);
|
||||
root.setLayoutParams(containerParams);
|
||||
|
||||
mSurfaceView = new SurfaceView(this);
|
||||
mSurfaceView.setLayoutParams(surfaceParams);
|
||||
root.addView(mSurfaceView);
|
||||
|
||||
Button stopButton = new Button(this);
|
||||
stopButton.setText("click");
|
||||
buttonParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
buttonParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
|
||||
buttonParams.rightMargin = 5;
|
||||
buttonParams.topMargin = 5;
|
||||
|
||||
stopButton.setLayoutParams(buttonParams);
|
||||
root.addView(stopButton);
|
||||
|
||||
setContentView(root);
|
||||
|
||||
mSurfaceHolder = mSurfaceView.getHolder();
|
||||
mSurfaceHolder.addCallback(this);
|
||||
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
mIntent = this.getIntent();
|
||||
|
||||
quality = mIntent.getIntExtra("quality", 100);
|
||||
|
||||
|
||||
stopButton.setOnClickListener(mSnapListener);
|
||||
}
|
||||
|
||||
private OnClickListener mSnapListener = new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
mCamera.takePicture(null, null, mPictureCallback);
|
||||
}
|
||||
};
|
||||
|
||||
public boolean onCreateOptionsMenu(android.view.Menu menu) {
|
||||
MenuItem item = menu.add(0, 0, 0, "goto gallery");
|
||||
item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
Uri target = Uri.parse("content://media/external/images/media");
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, target);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState)
|
||||
{
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
/*
|
||||
* We got the data, send it back to PhoneGap to be handled and processed.
|
||||
*
|
||||
*/
|
||||
|
||||
Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
|
||||
public void onPictureTaken(byte[] data, Camera c) {
|
||||
Log.e(TAG, "PICTURE CALLBACK: data.length = " + data.length);
|
||||
storeAndExit(data);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* We can't just store and exit, because Android freezes up when we try to cram a picture across a process in a Bundle.
|
||||
* We HAVE to compress this data and send back the compressed data
|
||||
*/
|
||||
public void storeAndExit(byte[] data)
|
||||
{
|
||||
ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
|
||||
Bitmap myMap = BitmapFactory.decodeByteArray(data, 0, data.length);
|
||||
try {
|
||||
if (myMap.compress(CompressFormat.JPEG, quality, jpeg_data))
|
||||
{
|
||||
byte[] code = jpeg_data.toByteArray();
|
||||
byte[] output = Base64.encodeBase64(code);
|
||||
String js_out = new String(output);
|
||||
mIntent.putExtra("picture", js_out);
|
||||
setResult(RESULT_OK, mIntent);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
//Do shit here
|
||||
}
|
||||
finish();
|
||||
}
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_CAMERA || keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
mCamera.takePicture(null, null, mPictureCallback);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void onResume()
|
||||
{
|
||||
Log.e(TAG, "onResume");
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
protected void onSaveInstanceState(Bundle outState)
|
||||
{
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
protected void onStop()
|
||||
{
|
||||
Log.e(TAG, "onStop");
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
public void surfaceCreated(SurfaceHolder holder)
|
||||
{
|
||||
Log.e(TAG, "surfaceCreated");
|
||||
mCamera = Camera.open();
|
||||
//mCamera.startPreview();
|
||||
}
|
||||
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
|
||||
{
|
||||
Log.e(TAG, "surfaceChanged");
|
||||
|
||||
// XXX stopPreview() will crash if preview is not running
|
||||
if (mPreviewRunning) {
|
||||
mCamera.stopPreview();
|
||||
}
|
||||
|
||||
Camera.Parameters p = mCamera.getParameters();
|
||||
p.setPreviewSize(w, h);
|
||||
mCamera.setParameters(p);
|
||||
try {
|
||||
mCamera.setPreviewDisplay(holder);
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
mCamera.startPreview();
|
||||
mPreviewRunning = true;
|
||||
}
|
||||
|
||||
public void surfaceDestroyed(SurfaceHolder holder)
|
||||
{
|
||||
Log.e(TAG, "surfaceDestroyed");
|
||||
mCamera.stopPreview();
|
||||
mPreviewRunning = false;
|
||||
mCamera.release();
|
||||
}
|
||||
|
||||
}
|
||||
359
framework/src/com/phonegap/Capture.java
Normal file
359
framework/src/com/phonegap/Capture.java
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
public class Capture extends Plugin {
|
||||
|
||||
private static final String _DATA = "_data"; // The column name where the file path is stored
|
||||
private static final int CAPTURE_AUDIO = 0; // Constant for capture audio
|
||||
private static final int CAPTURE_IMAGE = 1; // Constant for capture image
|
||||
private static final int CAPTURE_VIDEO = 2; // Constant for capture video
|
||||
private static final String LOG_TAG = "Capture";
|
||||
private String callbackId; // The ID of the callback to be invoked with our result
|
||||
private long limit; // the number of pics/vids/clips to take
|
||||
private double duration; // optional duration parameter for video recording
|
||||
private JSONArray results; // The array of results to be returned to the user
|
||||
private Uri imageUri; // Uri of captured image
|
||||
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
this.callbackId = callbackId;
|
||||
this.limit = 1;
|
||||
this.duration = 0.0f;
|
||||
this.results = new JSONArray();
|
||||
|
||||
JSONObject options = args.optJSONObject(0);
|
||||
if (options != null) {
|
||||
limit = options.optLong("limit", 1);
|
||||
duration = options.optDouble("duration", 0.0f);
|
||||
}
|
||||
|
||||
if (action.equals("getFormatData")) {
|
||||
try {
|
||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||
return new PluginResult(PluginResult.Status.OK, obj);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("captureAudio")) {
|
||||
this.captureAudio();
|
||||
}
|
||||
else if (action.equals("captureImage")) {
|
||||
this.captureImage();
|
||||
}
|
||||
else if (action.equals("captureVideo")) {
|
||||
this.captureVideo(duration);
|
||||
}
|
||||
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the media data file data depending on it's mime type
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param mimeType of the file
|
||||
* @return a MediaFileData object
|
||||
*/
|
||||
private JSONObject getFormatData(String filePath, String mimeType) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
// setup defaults
|
||||
obj.put("height", 0);
|
||||
obj.put("width", 0);
|
||||
obj.put("bitrate", 0);
|
||||
obj.put("duration", 0);
|
||||
obj.put("codecs", "");
|
||||
|
||||
// If the mimeType isn't set the rest will fail
|
||||
// so let's see if we can determine it.
|
||||
if (mimeType == null || mimeType.equals("")) {
|
||||
mimeType = FileUtils.getMimeType(filePath);
|
||||
}
|
||||
|
||||
if (mimeType.equals("image/jpeg") || filePath.endsWith(".jpg")) {
|
||||
obj = getImageData(filePath, obj);
|
||||
}
|
||||
else if (filePath.endsWith("audio/3gpp")) {
|
||||
obj = getAudioVideoData(filePath, obj, false);
|
||||
}
|
||||
else if (mimeType.equals("video/3gpp")) {
|
||||
obj = getAudioVideoData(filePath, obj, true);
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Error: setting media file data object");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
|
||||
obj.put("height", bitmap.getHeight());
|
||||
obj.put("width", bitmap.getWidth());
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @param video if true get video attributes as well
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
|
||||
MediaPlayer player = new MediaPlayer();
|
||||
try {
|
||||
player.setDataSource(filePath);
|
||||
player.prepare();
|
||||
obj.put("duration", player.getDuration());
|
||||
if (video) {
|
||||
obj.put("height", player.getVideoHeight());
|
||||
obj.put("width", player.getVideoWidth());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.d(LOG_TAG, "Error: loading video file");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture audio. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureAudio() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture images. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureImage() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Capture.jpg");
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture video. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureVideo(double duration) {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
// Introduced in API 8
|
||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the video view exits.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Result received okay
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// An audio clip was requested
|
||||
if (requestCode == CAPTURE_AUDIO) {
|
||||
// Get the uri of the audio clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for listening to audio
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more audio clips
|
||||
captureAudio();
|
||||
}
|
||||
} else if (requestCode == CAPTURE_IMAGE) {
|
||||
// For some reason if I try to do:
|
||||
// Uri data = intent.getData();
|
||||
// It crashes in the emulator and on my phone with a null pointer exception
|
||||
// To work around it I had to grab the code from CameraLauncher.java
|
||||
try {
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
|
||||
// Create entry in media store for image
|
||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri uri = null;
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
System.out.println("Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
System.out.println("Can't write to internal media storage.");
|
||||
this.fail("Error capturing image - no media storage found.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.ctx.getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
|
||||
os.close();
|
||||
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
|
||||
// Add image to results
|
||||
results.put(createMediaFile(uri));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more images
|
||||
captureImage();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.fail("Error capturing image.");
|
||||
}
|
||||
} else if (requestCode == CAPTURE_VIDEO) {
|
||||
// Get the uri of the video clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If canceled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
}
|
||||
// user canceled the action
|
||||
else {
|
||||
this.fail("Canceled.");
|
||||
}
|
||||
}
|
||||
// If something else
|
||||
else {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
}
|
||||
// something bad happened
|
||||
else {
|
||||
this.fail("Did not complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSONObject that represents a File from the Uri
|
||||
*
|
||||
* @param data the Uri of the audio/image/video
|
||||
* @return a JSONObject that represents a File
|
||||
*/
|
||||
private JSONObject createMediaFile(Uri data) {
|
||||
File fp = new File(getRealPathFromURI(data));
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
// File properties
|
||||
obj.put("name", fp.getName());
|
||||
obj.put("fullPath", fp.getAbsolutePath());
|
||||
obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath()));
|
||||
obj.put("lastModifiedDate", fp.lastModified());
|
||||
obj.put("size", fp.length());
|
||||
} catch (JSONException e) {
|
||||
// this will never happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return the full path to the file
|
||||
*/
|
||||
private String getRealPathFromURI(Uri contentUri) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = this.ctx.managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error message to JavaScript.
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
public void fail(String err) {
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
}
|
||||
}
|
||||
264
framework/src/com/phonegap/CompassListener.java
Normal file → Executable file
264
framework/src/com/phonegap/CompassListener.java
Normal file → Executable file
@@ -1,54 +1,270 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class CompassListener implements SensorEventListener{
|
||||
WebView mAppView;
|
||||
Context mCtx;
|
||||
Sensor mSensor;
|
||||
/**
|
||||
* This class listens to the compass sensor and stores the latest heading value.
|
||||
*/
|
||||
public class CompassListener extends Plugin implements SensorEventListener {
|
||||
|
||||
public static int STOPPED = 0;
|
||||
public static int STARTING = 1;
|
||||
public static int RUNNING = 2;
|
||||
public static int ERROR_FAILED_TO_START = 3;
|
||||
|
||||
public long TIMEOUT = 30000; // Timeout in msec to shut off listener
|
||||
|
||||
private SensorManager sensorManager;
|
||||
int status; // status of listener
|
||||
float heading; // most recent heading value
|
||||
long timeStamp; // time of most recent value
|
||||
long lastAccessTime; // time the value was last retrieved
|
||||
|
||||
CompassListener(Context ctx, WebView appView)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mAppView = appView;
|
||||
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
|
||||
private SensorManager sensorManager;// Sensor manager
|
||||
Sensor mSensor; // Compass sensor returned by sensor manager
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CompassListener() {
|
||||
this.timeStamp = 0;
|
||||
this.setStatus(CompassListener.STOPPED);
|
||||
}
|
||||
|
||||
|
||||
public void start()
|
||||
{
|
||||
/**
|
||||
* 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(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sensorManager = (SensorManager) ctx.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 = "";
|
||||
|
||||
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 != RUNNING) {
|
||||
int r = this.start();
|
||||
if (r == ERROR_FAILED_TO_START) {
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, 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, AccelListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
}
|
||||
float f = this.getHeading();
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("setTimeout")) {
|
||||
this.setTimeout(args.getLong(0));
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
long l = this.getTimeout();
|
||||
return new PluginResult(status, l);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
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("getStatus")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("getHeading")) {
|
||||
// Can only return value if RUNNING
|
||||
if (this.status == RUNNING) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (action.equals("getTimeout")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when listener is to be shut down and object is being destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Start listening for compass sensor.
|
||||
*
|
||||
* @return status of listener
|
||||
*/
|
||||
public int start() {
|
||||
|
||||
// If already starting or running, then just return
|
||||
if ((this.status == CompassListener.RUNNING) || (this.status == CompassListener.STARTING)) {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
// Get accelerometer from sensor manager
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
|
||||
// If found, then register as listener
|
||||
if (list.size() > 0) {
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
this.setStatus(CompassListener.STARTING);
|
||||
}
|
||||
|
||||
|
||||
// If error, then set status to error
|
||||
else {
|
||||
this.setStatus(CompassListener.ERROR_FAILED_TO_START);
|
||||
}
|
||||
|
||||
return this.status;
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
this.sensorManager.unregisterListener(this);
|
||||
/**
|
||||
* Stop listening to compass sensor.
|
||||
*/
|
||||
public void stop() {
|
||||
if (this.status != CompassListener.STOPPED) {
|
||||
this.sensorManager.unregisterListener(this);
|
||||
}
|
||||
this.setStatus(CompassListener.STOPPED);
|
||||
}
|
||||
|
||||
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sensor listener event.
|
||||
*
|
||||
* @param SensorEvent event
|
||||
*/
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
|
||||
// We only care about the orientation as far as it refers to Magnetic North
|
||||
float heading = event.values[0];
|
||||
mAppView.loadUrl("javascript:navigator.compass.setHeading(" + heading + ")");
|
||||
|
||||
// Save heading
|
||||
this.timeStamp = System.currentTimeMillis();
|
||||
this.heading = heading;
|
||||
this.setStatus(CompassListener.RUNNING);
|
||||
|
||||
// If heading hasn't been read for TIMEOUT time, then turn off compass sensor to save power
|
||||
if ((this.timeStamp - this.lastAccessTime) > this.TIMEOUT) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status of compass sensor.
|
||||
*
|
||||
* @return status
|
||||
*/
|
||||
public int getStatus() {
|
||||
return this.status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most recent compass heading.
|
||||
*
|
||||
* @return heading
|
||||
*/
|
||||
public float getHeading() {
|
||||
this.lastAccessTime = System.currentTimeMillis();
|
||||
return this.heading;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the timeout to turn off compass sensor if getHeading() hasn't been called.
|
||||
*
|
||||
* @param timeout Timeout in msec.
|
||||
*/
|
||||
public void setTimeout(long timeout) {
|
||||
this.TIMEOUT = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the timeout to turn off compass sensor if getHeading() hasn't been called.
|
||||
*
|
||||
* @return timeout in msec
|
||||
*/
|
||||
public long getTimeout() {
|
||||
return this.TIMEOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the status and send it to JavaScript.
|
||||
* @param status
|
||||
*/
|
||||
private void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
205
framework/src/com/phonegap/ContactAccessor.java
Normal file
205
framework/src/com/phonegap/ContactAccessor.java
Normal file
@@ -0,0 +1,205 @@
|
||||
// Taken from Android Tutorials
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed 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;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* This abstract class defines SDK-independent API for communication with
|
||||
* Contacts Provider. The actual implementation used by the application depends
|
||||
* on the level of API available on the device. If the API level is Cupcake or
|
||||
* Donut, we want to use the {@link ContactAccessorSdk3_4} class. If it is
|
||||
* Eclair or higher, we want to use {@link ContactAccessorSdk5}.
|
||||
*/
|
||||
public abstract class ContactAccessor {
|
||||
|
||||
protected final String LOG_TAG = "ContactsAccessor";
|
||||
protected Activity mApp;
|
||||
protected WebView mView;
|
||||
|
||||
/**
|
||||
* Check to see if the data associated with the key is required to
|
||||
* be populated in the Contact object.
|
||||
* @param key
|
||||
* @param map created by running buildPopulationSet.
|
||||
* @return true if the key data is required
|
||||
*/
|
||||
protected boolean isRequired(String key, HashMap<String,Boolean> map) {
|
||||
Boolean retVal = map.get(key);
|
||||
return (retVal == null) ? false : retVal.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a hash map of what data needs to be populated in the Contact object
|
||||
* @param fields the list of fields to populate
|
||||
* @return the hash map of required data
|
||||
*/
|
||||
protected HashMap<String,Boolean> buildPopulationSet(JSONArray fields) {
|
||||
HashMap<String,Boolean> map = new HashMap<String,Boolean>();
|
||||
|
||||
String key;
|
||||
try {
|
||||
if (fields.length() == 1 && fields.getString(0).equals("*")) {
|
||||
map.put("displayName", true);
|
||||
map.put("name", true);
|
||||
map.put("nickname", true);
|
||||
map.put("phoneNumbers", true);
|
||||
map.put("emails", true);
|
||||
map.put("addresses", true);
|
||||
map.put("ims", true);
|
||||
map.put("organizations", true);
|
||||
map.put("birthday", true);
|
||||
map.put("note", true);
|
||||
map.put("urls", true);
|
||||
map.put("photos", true);
|
||||
map.put("categories", true);
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
if (key.startsWith("displayName")) {
|
||||
map.put("displayName", true);
|
||||
}
|
||||
else if (key.startsWith("name")) {
|
||||
map.put("name", true);
|
||||
}
|
||||
else if (key.startsWith("nickname")) {
|
||||
map.put("nickname", true);
|
||||
}
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
map.put("phoneNumbers", true);
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
map.put("emails", true);
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
map.put("addresses", true);
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
map.put("ims", true);
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
map.put("organizations", true);
|
||||
}
|
||||
else if (key.startsWith("birthday")) {
|
||||
map.put("birthday", true);
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
map.put("note", true);
|
||||
}
|
||||
else if (key.startsWith("urls")) {
|
||||
map.put("urls", true);
|
||||
}
|
||||
else if (key.startsWith("photos")) {
|
||||
map.put("photos", true);
|
||||
}
|
||||
else if (key.startsWith("categories")) {
|
||||
map.put("categories", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to get a string from a JSON object. Saves a
|
||||
* lot of try/catch writing.
|
||||
* If the property is not found in the object null will be returned.
|
||||
*
|
||||
* @param obj contact object to search
|
||||
* @param property to be looked up
|
||||
* @return The value of the property
|
||||
*/
|
||||
protected String getJsonString(JSONObject obj, String property) {
|
||||
String value = null;
|
||||
try {
|
||||
if (obj != null) {
|
||||
value = obj.getString(property);
|
||||
if (value.equals("null")) {
|
||||
Log.d(LOG_TAG, property + " is string called 'null'");
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not get = " + e.getMessage());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding a JSON Contact object into the database.
|
||||
* @return TODO
|
||||
*/
|
||||
public abstract String save(JSONObject contact);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
*/
|
||||
public abstract JSONArray search(JSONArray filter, JSONObject options);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public abstract JSONObject getContactById(String id) throws JSONException;
|
||||
|
||||
/**
|
||||
* Handles removing a contact from the database.
|
||||
*/
|
||||
public abstract boolean remove(String id);
|
||||
|
||||
/**
|
||||
* A class that represents the where clause to be used in the database query
|
||||
*/
|
||||
class WhereOptions {
|
||||
private String where;
|
||||
private String[] whereArgs;
|
||||
public void setWhere(String where) {
|
||||
this.where = where;
|
||||
}
|
||||
public String getWhere() {
|
||||
return where;
|
||||
}
|
||||
public void setWhereArgs(String[] whereArgs) {
|
||||
this.whereArgs = whereArgs;
|
||||
}
|
||||
public String[] getWhereArgs() {
|
||||
return whereArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
1931
framework/src/com/phonegap/ContactAccessorSdk5.java
Normal file
1931
framework/src/com/phonegap/ContactAccessorSdk5.java
Normal file
File diff suppressed because it is too large
Load Diff
367
framework/src/com/phonegap/ContactManager.java
Normal file → Executable file
367
framework/src/com/phonegap/ContactManager.java
Normal file → Executable file
@@ -1,291 +1,102 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import android.provider.Contacts.ContactMethods;
|
||||
import android.provider.Contacts.People;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.net.Uri;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ContactManager {
|
||||
|
||||
public class ContactTriplet
|
||||
{
|
||||
public String name = "";
|
||||
public String email = "";
|
||||
public String phone = "";
|
||||
}
|
||||
public class ContactManager extends Plugin {
|
||||
|
||||
private ContactAccessor contactAccessor;
|
||||
private static final String LOG_TAG = "Contact Query";
|
||||
Activity mApp;
|
||||
WebView mView;
|
||||
Uri mPeople = android.provider.Contacts.People.CONTENT_URI;
|
||||
Uri mPhone = android.provider.Contacts.Phones.CONTENT_URI;
|
||||
Uri mEmail = android.provider.Contacts.ContactMethods.CONTENT_URI;
|
||||
|
||||
ContactManager(Activity app, WebView view)
|
||||
{
|
||||
mApp = app;
|
||||
mView = view;
|
||||
}
|
||||
|
||||
// This is to add backwards compatibility to the OLD Contacts API\
|
||||
public void getContactsAndSendBack()
|
||||
{
|
||||
String[] projection = new String[] {
|
||||
People._ID,
|
||||
People.NAME,
|
||||
People.NUMBER,
|
||||
People.PRIMARY_EMAIL_ID
|
||||
};
|
||||
|
||||
try{
|
||||
Cursor myCursor = mApp.managedQuery(mPeople, projection,
|
||||
null, null , People.NAME + " ASC");
|
||||
processResults(myCursor, true);
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public void search(String name, String npa, String email)
|
||||
{
|
||||
|
||||
if (email.length() > 0)
|
||||
searchByEmail(email);
|
||||
else
|
||||
searchPeople(name, npa);
|
||||
}
|
||||
|
||||
private void searchByEmail(String email)
|
||||
{
|
||||
String[] projection = new String[] {
|
||||
ContactMethods._ID,
|
||||
ContactMethods.DATA,
|
||||
ContactMethods.KIND,
|
||||
ContactMethods.PERSON_ID
|
||||
};
|
||||
String[] variables = new String[] {
|
||||
email
|
||||
};
|
||||
|
||||
try{
|
||||
Cursor myCursor = mApp.managedQuery(mEmail, projection,
|
||||
"contact_methods." + ContactMethods.DATA + " = ?" + "AND contact_methods.kind = 1", variables , ContactMethods.DATA + " ASC");
|
||||
getMethodData(myCursor);
|
||||
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void searchPeople(String name, String number)
|
||||
{
|
||||
String conditions = "";
|
||||
|
||||
if (name.length() == 0)
|
||||
{
|
||||
name = "%";
|
||||
conditions += People.NAME + " LIKE ? AND ";
|
||||
}
|
||||
else
|
||||
{
|
||||
conditions += People.NAME + " = ? AND ";
|
||||
}
|
||||
|
||||
if (number.length() == 0)
|
||||
number = "%";
|
||||
else
|
||||
{
|
||||
number = number.replace('+', '%');
|
||||
number = number.replace('.', '%');
|
||||
number = number.replace('-', '%');
|
||||
}
|
||||
|
||||
conditions += People.NUMBER + " LIKE ? ";
|
||||
|
||||
String[] projection = new String[] {
|
||||
People._ID,
|
||||
People.NAME,
|
||||
People.NUMBER,
|
||||
People.PRIMARY_EMAIL_ID
|
||||
};
|
||||
|
||||
String[] variables = new String[] {
|
||||
name, number
|
||||
};
|
||||
|
||||
try{
|
||||
Cursor myCursor = mApp.managedQuery(mPeople, projection,
|
||||
conditions, variables , People.NAME + " ASC");
|
||||
processResults(myCursor, false);
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
public static final int UNKNOWN_ERROR = 0;
|
||||
public static final int INVALID_ARGUMENT_ERROR = 1;
|
||||
public static final int TIMEOUT_ERROR = 2;
|
||||
public static final int PENDING_OPERATION_ERROR = 3;
|
||||
public static final int IO_ERROR = 4;
|
||||
public static final int NOT_SUPPORTED_ERROR = 5;
|
||||
public static final int PERMISSION_DENIED_ERROR = 20;
|
||||
|
||||
private void processResults(Cursor cur, boolean all){
|
||||
|
||||
if (cur.moveToFirst()) {
|
||||
|
||||
String name;
|
||||
String phoneNumber;
|
||||
String email_id;
|
||||
String email;
|
||||
|
||||
int nameColumn = cur.getColumnIndex(People.NAME);
|
||||
int phoneColumn = cur.getColumnIndex(People.NUMBER);
|
||||
int emailIdColumn = cur.getColumnIndex(People.PRIMARY_EMAIL_ID);
|
||||
|
||||
do {
|
||||
// Get the field values
|
||||
name = cur.getString(nameColumn);
|
||||
phoneNumber = cur.getString(phoneColumn);
|
||||
email_id = cur.getString(emailIdColumn);
|
||||
if (email_id != null && email_id.length() > 0)
|
||||
email = getEmail(email_id);
|
||||
else
|
||||
email = "";
|
||||
|
||||
// Code for backwards compatibility with the OLD Contacts API
|
||||
if (all)
|
||||
mView.loadUrl("javascript:navigator.ContactManager.droidAddContact('" + name + "','" + phoneNumber + "','" + email +"')");
|
||||
else
|
||||
mView.loadUrl("javascript:navigator.contacts.droidFoundContact('" + name + "','" + phoneNumber + "','" + email +"')");
|
||||
|
||||
} while (cur.moveToNext());
|
||||
if (all)
|
||||
mView.loadUrl("javascript:navigator.ContactManager.droidDone()");
|
||||
else
|
||||
mView.loadUrl("javascript:navigator.contacts.droidDone();");
|
||||
}
|
||||
else
|
||||
{
|
||||
if(all)
|
||||
mView.loadUrl("javascript:navigator.ContactManager.fail()");
|
||||
else
|
||||
mView.loadUrl("javascript:navigator.contacts.fail('None found!')");
|
||||
}
|
||||
}
|
||||
|
||||
private void getMethodData(Cursor cur)
|
||||
{
|
||||
ContactTriplet data = new ContactTriplet();
|
||||
String id;
|
||||
String email;
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public ContactManager() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
/**
|
||||
* 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 PhoneGap 1.0.
|
||||
*/
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
JSONObject res = null;
|
||||
try {
|
||||
res = new JSONObject();
|
||||
res.put("code", NOT_SUPPORTED_ERROR);
|
||||
res.put("message", "Contacts are not supported in Android 1.X devices");
|
||||
} catch (JSONException e) {
|
||||
// This should never happen
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return new PluginResult(PluginResult.Status.ERROR, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only create the contactAccessor after we check the Android version or the program will crash
|
||||
* older phones.
|
||||
*/
|
||||
if (this.contactAccessor == null) {
|
||||
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.ctx);
|
||||
}
|
||||
|
||||
if (cur.moveToFirst()) {
|
||||
|
||||
int idColumn = cur.getColumnIndex(ContactMethods._ID);
|
||||
int emailColumn = cur.getColumnIndex(ContactMethods.DATA);
|
||||
do {
|
||||
// Get the field values
|
||||
id = cur.getString(idColumn);
|
||||
email = cur.getString(emailColumn);
|
||||
|
||||
data = getContactData(id);
|
||||
if(data != null)
|
||||
{
|
||||
data.email = email;
|
||||
mView.loadUrl("javascript:navigator.Contacts.droidFoundContact('" + data.name + "','" + data.phone + "','" + data.email +"')");
|
||||
}
|
||||
} while (cur.moveToNext());
|
||||
mView.loadUrl("javascript:navigator.contacts.droidDoneContacts();");
|
||||
}
|
||||
}
|
||||
|
||||
private ContactTriplet getContactData(String id) {
|
||||
ContactTriplet data = null;
|
||||
String[] projection = new String[] {
|
||||
People._ID,
|
||||
People.NAME,
|
||||
People.NUMBER,
|
||||
People.PRIMARY_EMAIL_ID
|
||||
};
|
||||
|
||||
String[] variables = new String[] {
|
||||
id
|
||||
};
|
||||
|
||||
try{
|
||||
Cursor myCursor = mApp.managedQuery(mPeople, projection,
|
||||
People.PRIMARY_EMAIL_ID + " = ?", variables , People.NAME + " ASC");
|
||||
data = getTriplet(myCursor);
|
||||
try {
|
||||
if (action.equals("search")) {
|
||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
|
||||
return new PluginResult(status, res, "navigator.contacts.cast");
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
String id = contactAccessor.save(args.getJSONObject(0));
|
||||
if (id != null) {
|
||||
JSONObject res = contactAccessor.getContactById(id);
|
||||
if (res != null) {
|
||||
return new PluginResult(status, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("code", UNKNOWN_ERROR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, r);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
private ContactTriplet getTriplet(Cursor cur) {
|
||||
ContactTriplet data = new ContactTriplet();
|
||||
if (cur.moveToFirst()) {
|
||||
|
||||
int nameColumn = cur.getColumnIndex(People.NAME);
|
||||
int numberColumn = cur.getColumnIndex(People.NUMBER);
|
||||
do {
|
||||
|
||||
data.name = cur.getString(nameColumn);
|
||||
data.phone = cur.getString(numberColumn);
|
||||
|
||||
} while (cur.moveToNext());
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private String getEmailColumnData(Cursor cur)
|
||||
{
|
||||
String email = "";
|
||||
if (cur != null && cur.moveToFirst()) {
|
||||
int emailColumn = cur.getColumnIndex(ContactMethods.DATA);
|
||||
do {
|
||||
// Get the field values
|
||||
email = cur.getString(emailColumn);
|
||||
} while (cur.moveToNext());
|
||||
}
|
||||
return email;
|
||||
}
|
||||
|
||||
private String getEmail(String id)
|
||||
{
|
||||
String email = "";
|
||||
String[] projection = new String[] {
|
||||
ContactMethods._ID,
|
||||
ContactMethods.DATA,
|
||||
ContactMethods.KIND
|
||||
};
|
||||
String[] variables = new String[] {
|
||||
id
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Cursor myCursor = mApp.managedQuery(mEmail, projection,
|
||||
"contact_methods." + ContactMethods._ID + " = ?" + " AND contact_methods.kind = 1", variables , ContactMethods.DATA + " ASC");
|
||||
email = getEmailColumnData(myCursor);
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
}
|
||||
|
||||
return email;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
72
framework/src/com/phonegap/CryptoHandler.java
Normal file → Executable file
72
framework/src/com/phonegap/CryptoHandler.java
Normal file → Executable file
@@ -1,37 +1,71 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import android.webkit.WebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class CryptoHandler {
|
||||
|
||||
WebView mView;
|
||||
|
||||
CryptoHandler(WebView view)
|
||||
{
|
||||
mView = view;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
public class CryptoHandler extends Plugin {
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CryptoHandler() {
|
||||
}
|
||||
|
||||
public void encrypt(String pass, String text)
|
||||
{
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
try {
|
||||
if (action.equals("encrypt")) {
|
||||
this.encrypt(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("decrypt")) {
|
||||
this.decrypt(args.getString(0), args.getString(1));
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
public void encrypt(String pass, String text) {
|
||||
try {
|
||||
String encrypted = SimpleCrypto.encrypt(pass,text);
|
||||
mView.loadUrl("javascript:Crypto.gotCryptedString('" + text + "')");
|
||||
// TODO: Why not just return text now?
|
||||
this.sendJavascript("Crypto.gotCryptedString('" + text + "')");
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void decrypt(String pass, String text)
|
||||
{
|
||||
public void decrypt(String pass, String text) {
|
||||
try {
|
||||
String decrypted = SimpleCrypto.decrypt(pass,text);
|
||||
mView.loadUrl("javascript:Crypto.gotPlainString('" + text + "')");
|
||||
this.sendJavascript("Crypto.gotPlainString('" + text + "')");
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
150
framework/src/com/phonegap/Device.java
Normal file
150
framework/src/com/phonegap/Device.java
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.TimeZone;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.provider.Settings;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "1.0.0"; // PhoneGap version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Device() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
Device.uuid = getUuid();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
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("phonegap", Device.phonegapVersion);
|
||||
//JSONObject pg = new JSONObject();
|
||||
//pg.put("version", Device.phonegapVersion);
|
||||
//r.put("phonegap", 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")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the OS name.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPlatform() {
|
||||
return Device.platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the device's Universally Unique Identifier (UUID).
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getUuid() {
|
||||
String uuid = Settings.Secure.getString(this.ctx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the PhoneGap version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getPhonegapVersion() {
|
||||
return Device.phonegapVersion;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
String model = android.os.Build.MODEL;
|
||||
return model;
|
||||
}
|
||||
|
||||
public String getProductName() {
|
||||
String productname = android.os.Build.PRODUCT;
|
||||
return productname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the OS version.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getOSVersion() {
|
||||
String osversion = android.os.Build.VERSION.RELEASE;
|
||||
return osversion;
|
||||
}
|
||||
|
||||
public String getSDKVersion() {
|
||||
String sdkversion = android.os.Build.VERSION.SDK;
|
||||
return sdkversion;
|
||||
}
|
||||
|
||||
|
||||
public String getTimeZoneID() {
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
return(tz.getID());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,31 +1,60 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class provides file directory utilities.
|
||||
* All file operations are performed on the SD card.
|
||||
*
|
||||
* It is used by the FileUtils class.
|
||||
*/
|
||||
public class DirectoryManager {
|
||||
|
||||
protected boolean testFileExists (String name){
|
||||
private static final String LOG_TAG = "DirectoryManager";
|
||||
|
||||
/**
|
||||
* Determine if a file or directory exists.
|
||||
*
|
||||
* @param name The name of the file to check.
|
||||
* @return T=exists, F=not found
|
||||
*/
|
||||
protected static boolean testFileExists(String name) {
|
||||
boolean status;
|
||||
if ((testSaveLocationExists())&&(!name.equals(""))){
|
||||
|
||||
// If SD card exists
|
||||
if ((testSaveLocationExists()) && (!name.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), name);
|
||||
status = newPath.exists();
|
||||
}else{
|
||||
}
|
||||
// If no SD card
|
||||
else{
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected long getFreeDiskSpace(){
|
||||
/*
|
||||
* gets the available SD card free space or returns -1 if the SD card is not mounted.
|
||||
*/
|
||||
/**
|
||||
* Get the free disk space on the SD card
|
||||
*
|
||||
* @return Size in KB or -1 if not available
|
||||
*/
|
||||
protected static long getFreeDiskSpace() {
|
||||
String status = Environment.getExternalStorageState();
|
||||
long freeSpace = 0;
|
||||
|
||||
// If SD card exists
|
||||
if (status.equals(Environment.MEDIA_MOUNTED)) {
|
||||
try {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
@@ -34,93 +63,80 @@ public class DirectoryManager {
|
||||
long availableBlocks = stat.getAvailableBlocks();
|
||||
freeSpace = availableBlocks*blockSize/1024;
|
||||
} catch (Exception e) {e.printStackTrace(); }
|
||||
} else { return -1; }
|
||||
}
|
||||
|
||||
// If no SD card, then return -1
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (freeSpace);
|
||||
}
|
||||
|
||||
protected boolean createDirectory(String directoryName){
|
||||
boolean status;
|
||||
if ((testSaveLocationExists())&&(!directoryName.equals(""))){
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), directoryName);
|
||||
status = newPath.mkdir();
|
||||
status = true;
|
||||
}else
|
||||
status = false;
|
||||
return status;
|
||||
}
|
||||
|
||||
protected boolean testSaveLocationExists(){
|
||||
/**
|
||||
* Determine if SD card exists.
|
||||
*
|
||||
* @return T=exists, F=not found
|
||||
*/
|
||||
protected static boolean testSaveLocationExists() {
|
||||
String sDCardStatus = Environment.getExternalStorageState();
|
||||
boolean status;
|
||||
if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)){
|
||||
|
||||
// If SD card is mounted
|
||||
if (sDCardStatus.equals(Environment.MEDIA_MOUNTED)) {
|
||||
status = true;
|
||||
}else
|
||||
status = false;
|
||||
return status;
|
||||
}
|
||||
|
||||
protected boolean deleteDirectory(String fileName){
|
||||
boolean status;
|
||||
SecurityManager checker = new SecurityManager();
|
||||
|
||||
if ((testSaveLocationExists())&&(!fileName.equals(""))){
|
||||
}
|
||||
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), fileName);
|
||||
checker.checkDelete(newPath.toString());
|
||||
if(newPath.isDirectory()){
|
||||
String[] listfile = newPath.list();
|
||||
// delete all files within the specified directory and then delete the directory
|
||||
try{
|
||||
for (int i=0; i < listfile.length; i++){
|
||||
File deletedFile = new File (newPath.toString()+"/"+listfile[i].toString());
|
||||
deletedFile.delete();
|
||||
}
|
||||
newPath.delete();
|
||||
Log.i("DirectoryManager deleteDirectory", fileName);
|
||||
status = true;
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
status = false;
|
||||
}
|
||||
|
||||
}else
|
||||
status = false;
|
||||
}else
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected boolean deleteFile(String fileName){
|
||||
boolean status;
|
||||
SecurityManager checker = new SecurityManager();
|
||||
|
||||
if ((testSaveLocationExists())&&(!fileName.equals(""))){
|
||||
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), fileName);
|
||||
checker.checkDelete(newPath.toString());
|
||||
if (newPath.isFile()){
|
||||
try {
|
||||
Log.i("DirectoryManager deleteFile", fileName);
|
||||
newPath.delete();
|
||||
status = true;
|
||||
}catch (SecurityException se){
|
||||
se.printStackTrace();
|
||||
status = false;
|
||||
}
|
||||
}else
|
||||
status = false;
|
||||
}else
|
||||
status = false;
|
||||
return status;
|
||||
}
|
||||
|
||||
private File constructFilePaths (String file1, String file2){
|
||||
/**
|
||||
* Create a new file object from two file paths.
|
||||
*
|
||||
* @param file1 Base file path
|
||||
* @param file2 Remaining file path
|
||||
* @return File object
|
||||
*/
|
||||
private static File constructFilePaths (String file1, String file2) {
|
||||
File newPath;
|
||||
newPath = new File(file1+"/"+file2);
|
||||
if (file2.startsWith(file1)) {
|
||||
newPath = new File(file2);
|
||||
}
|
||||
else {
|
||||
newPath = new File(file1+"/"+file2);
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we can use the SD Card to store the temporary file. If not then use
|
||||
* the internal cache directory.
|
||||
*
|
||||
* @return the absolute path of where to store the file
|
||||
*/
|
||||
protected static String getTempDirectoryPath(Context ctx) {
|
||||
File cache = null;
|
||||
|
||||
// SD Card Mounted
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getPackageName() + "/cache/");
|
||||
}
|
||||
// Use internal storage
|
||||
else {
|
||||
cache = ctx.getCacheDir();
|
||||
}
|
||||
|
||||
// Create the cache directory if it doesn't exist
|
||||
if (!cache.exists()) {
|
||||
cache.mkdirs();
|
||||
}
|
||||
|
||||
return cache.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
365
framework/src/com/phonegap/FileTransfer.java
Normal file
365
framework/src/com/phonegap/FileTransfer.java
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.CookieManager;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
public class FileTransfer extends Plugin {
|
||||
|
||||
private static final String LOG_TAG = "FileUploader";
|
||||
private static final String LINE_START = "--";
|
||||
private static final String LINE_END = "\r\n";
|
||||
private static final String BOUNDRY = "*****";
|
||||
|
||||
public static int FILE_NOT_FOUND_ERR = 1;
|
||||
public static int INVALID_URL_ERR = 2;
|
||||
public static int CONNECTION_ERR = 3;
|
||||
|
||||
private SSLSocketFactory defaultSSLSocketFactory = null;
|
||||
private HostnameVerifier defaultHostnameVerifier = null;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
String file = null;
|
||||
String server = null;
|
||||
try {
|
||||
file = args.getString(0);
|
||||
server = args.getString(1);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Missing filename or server name");
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing filename or server name");
|
||||
}
|
||||
|
||||
// Setup the options
|
||||
String fileKey = null;
|
||||
String fileName = null;
|
||||
String mimeType = null;
|
||||
|
||||
fileKey = getArgument(args, 2, "file");
|
||||
fileName = getArgument(args, 3, "image.jpg");
|
||||
mimeType = getArgument(args, 4, "image/jpeg");
|
||||
|
||||
try {
|
||||
JSONObject params = args.optJSONObject(5);
|
||||
boolean trustEveryone = args.optBoolean(6);
|
||||
|
||||
if (action.equals("upload")) {
|
||||
FileUploadResult r = upload(file, server, fileKey, fileName, mimeType, params, trustEveryone);
|
||||
Log.d(LOG_TAG, "****** About to return a result from upload");
|
||||
return new PluginResult(PluginResult.Status.OK, r.toJSONObject());
|
||||
} else {
|
||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
JSONObject error = createFileUploadError(FILE_NOT_FOUND_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
JSONObject error = createFileUploadError(INVALID_URL_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (SSLException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
Log.d(LOG_TAG, "Got my ssl exception!!!");
|
||||
JSONObject error = createFileUploadError(CONNECTION_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
JSONObject error = createFileUploadError(CONNECTION_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
// always verify the host - don't check for certificate
|
||||
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will install a trust manager that will blindly trust all SSL
|
||||
* certificates. The reason this code is being added is to enable developers
|
||||
* to do development using self signed SSL certificates on their web server.
|
||||
*
|
||||
* The standard HttpsURLConnection class will throw an exception on self
|
||||
* signed certificates if this code is not run.
|
||||
*/
|
||||
private void trustAllHosts() {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[] {};
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
} };
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
try {
|
||||
// Backup the current SSL socket factory
|
||||
defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
|
||||
// Install our all trusting manager
|
||||
SSLContext sc = SSLContext.getInstance("TLS");
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error object based on the passed in errorCode
|
||||
* @param errorCode the error
|
||||
* @return JSONObject containing the error
|
||||
*/
|
||||
private JSONObject createFileUploadError(int errorCode) {
|
||||
JSONObject error = null;
|
||||
try {
|
||||
error = new JSONObject();
|
||||
error.put("code", errorCode);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to read a parameter from the list of JSON args.
|
||||
* @param args the args passed to the Plugin
|
||||
* @param position the position to retrieve the arg from
|
||||
* @param defaultString the default to be used if the arg does not exist
|
||||
* @return String with the retrieved value
|
||||
*/
|
||||
private String getArgument(JSONArray args, int position, String defaultString) {
|
||||
String arg = defaultString;
|
||||
if(args.length() >= position) {
|
||||
arg = args.optString(position);
|
||||
if (arg == null || "null".equals(arg)) {
|
||||
arg = defaultString;
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the specified file to the server URL provided using an HTTP
|
||||
* multipart request.
|
||||
* @param file Full path of the file on the file system
|
||||
* @param server URL of the server to receive the file
|
||||
* @param fileKey Name of file request parameter
|
||||
* @param fileName File name to be used on server
|
||||
* @param mimeType Describes file content type
|
||||
* @param params key:value pairs of user-defined parameters
|
||||
* @return FileUploadResult containing result of upload request
|
||||
*/
|
||||
public FileUploadResult upload(String file, String server, final String fileKey, final String fileName,
|
||||
final String mimeType, JSONObject params, boolean trustEveryone) throws IOException, SSLException {
|
||||
// Create return object
|
||||
FileUploadResult result = new FileUploadResult();
|
||||
|
||||
// Get a input stream of the file on the phone
|
||||
InputStream fileInputStream = getPathFromUri(file);
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
DataOutputStream dos = null;
|
||||
|
||||
int bytesRead, bytesAvailable, bufferSize;
|
||||
long totalBytes;
|
||||
byte[] buffer;
|
||||
int maxBufferSize = 8096;
|
||||
|
||||
//------------------ CLIENT REQUEST
|
||||
// open a URL connection to the server
|
||||
URL url = new URL(server);
|
||||
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (url.getProtocol().toLowerCase().equals("https")) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
conn = (HttpsURLConnection) url.openConnection();
|
||||
}
|
||||
// Use our HTTPS connection that blindly trusts everyone.
|
||||
// This should only be used in debug environments
|
||||
else {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
trustAllHosts();
|
||||
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
|
||||
// Save the current hostnameVerifier
|
||||
defaultHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
conn = https;
|
||||
}
|
||||
}
|
||||
// Return a standard HTTP conneciton
|
||||
else {
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
|
||||
// Allow Inputs
|
||||
conn.setDoInput(true);
|
||||
|
||||
// Allow Outputs
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// Don't use a cached copy.
|
||||
conn.setUseCaches(false);
|
||||
|
||||
// Use a post method.
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Connection", "Keep-Alive");
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY);
|
||||
|
||||
// Set the cookies on the response
|
||||
String cookie = CookieManager.getInstance().getCookie(server);
|
||||
if (cookie != null) {
|
||||
conn.setRequestProperty("Cookie", cookie);
|
||||
}
|
||||
|
||||
dos = new DataOutputStream( conn.getOutputStream() );
|
||||
|
||||
// Send any extra parameters
|
||||
try {
|
||||
for (Iterator iter = params.keys(); iter.hasNext();) {
|
||||
Object key = iter.next();
|
||||
dos.writeBytes(LINE_START + BOUNDRY + LINE_END);
|
||||
dos.writeBytes("Content-Disposition: form-data; name=\"" + key.toString() + "\";");
|
||||
dos.writeBytes(LINE_END + LINE_END);
|
||||
dos.writeBytes(params.getString(key.toString()));
|
||||
dos.writeBytes(LINE_END);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
dos.writeBytes(LINE_START + BOUNDRY + LINE_END);
|
||||
dos.writeBytes("Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"" + fileName +"\"" + LINE_END);
|
||||
dos.writeBytes("Content-Type: " + mimeType + LINE_END);
|
||||
dos.writeBytes(LINE_END);
|
||||
|
||||
// create a buffer of maximum size
|
||||
bytesAvailable = fileInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
buffer = new byte[bufferSize];
|
||||
|
||||
// read file and write it into form...
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
totalBytes = 0;
|
||||
|
||||
while (bytesRead > 0) {
|
||||
totalBytes += bytesRead;
|
||||
result.setBytesSent(totalBytes);
|
||||
dos.write(buffer, 0, bufferSize);
|
||||
bytesAvailable = fileInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
}
|
||||
|
||||
// send multipart form data necesssary after file data...
|
||||
dos.writeBytes(LINE_END);
|
||||
dos.writeBytes(LINE_START + BOUNDRY + LINE_START + LINE_END);
|
||||
|
||||
// close streams
|
||||
fileInputStream.close();
|
||||
dos.flush();
|
||||
dos.close();
|
||||
|
||||
//------------------ read the SERVER RESPONSE
|
||||
StringBuffer responseString = new StringBuffer("");
|
||||
DataInputStream inStream;
|
||||
try {
|
||||
inStream = new DataInputStream ( conn.getInputStream() );
|
||||
} catch(FileNotFoundException e) {
|
||||
throw new IOException("Received error from server");
|
||||
}
|
||||
|
||||
String line;
|
||||
while (( line = inStream.readLine()) != null) {
|
||||
responseString.append(line);
|
||||
}
|
||||
Log.d(LOG_TAG, "got response from server");
|
||||
Log.d(LOG_TAG, responseString.toString());
|
||||
|
||||
// send request and retrieve response
|
||||
result.setResponseCode(conn.getResponseCode());
|
||||
result.setResponse(responseString.toString());
|
||||
|
||||
inStream.close();
|
||||
conn.disconnect();
|
||||
|
||||
// Revert back to the proper verifier and socket factories
|
||||
if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier);
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input stream based on file path or content:// uri
|
||||
*
|
||||
* @param path
|
||||
* @return an input stream
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
||||
if (path.startsWith("content:")) {
|
||||
Uri uri = Uri.parse(path);
|
||||
return ctx.getContentResolver().openInputStream(uri);
|
||||
}
|
||||
else {
|
||||
return new FileInputStream(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
52
framework/src/com/phonegap/FileUploadResult.java
Normal file
52
framework/src/com/phonegap/FileUploadResult.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the result and/or status of uploading a file to a remote server.
|
||||
*/
|
||||
public class FileUploadResult {
|
||||
|
||||
private long bytesSent = 0; // bytes sent
|
||||
private int responseCode = -1; // HTTP response code
|
||||
private String response = null; // HTTP response
|
||||
|
||||
public long getBytesSent() {
|
||||
return bytesSent;
|
||||
}
|
||||
|
||||
public void setBytesSent(long bytes) {
|
||||
this.bytesSent = bytes;
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
public void setResponseCode(int responseCode) {
|
||||
this.responseCode = responseCode;
|
||||
}
|
||||
|
||||
public String getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(String response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{bytesSent:" + bytesSent +
|
||||
",responseCode:" + responseCode +
|
||||
",response:" + JSONObject.quote(response) + "}");
|
||||
}
|
||||
}
|
||||
1072
framework/src/com/phonegap/FileUtils.java
Normal file → Executable file
1072
framework/src/com/phonegap/FileUtils.java
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
164
framework/src/com/phonegap/GeoBroker.java
Normal file → Executable file
164
framework/src/com/phonegap/GeoBroker.java
Normal file → Executable file
@@ -1,10 +1,20 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.webkit.WebView;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
/*
|
||||
* This class is the interface to the Geolocation. It's bound to the geo object.
|
||||
@@ -12,37 +22,133 @@ import android.webkit.WebView;
|
||||
* This class only starts and stops various GeoListeners, which consist of a GPS and a Network Listener
|
||||
*/
|
||||
|
||||
public class GeoBroker {
|
||||
private WebView mAppView;
|
||||
private Context mCtx;
|
||||
private HashMap<String, GeoListener> geoListeners;
|
||||
public class GeoBroker extends Plugin {
|
||||
|
||||
// List of gGeolocation listeners
|
||||
private HashMap<String, GeoListener> geoListeners;
|
||||
private GeoListener global;
|
||||
|
||||
public GeoBroker(WebView view, Context ctx)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mAppView = view;
|
||||
geoListeners = new HashMap<String, GeoListener>();
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public GeoBroker() {
|
||||
this.geoListeners = new HashMap<String, GeoListener>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
try {
|
||||
if (action.equals("getCurrentLocation")) {
|
||||
this.getCurrentLocation(args.getBoolean(0), args.getInt(1), args.getInt(2));
|
||||
}
|
||||
else if (action.equals("start")) {
|
||||
String s = this.start(args.getString(0), args.getBoolean(1), args.getInt(2), args.getInt(3));
|
||||
return new PluginResult(status, s);
|
||||
}
|
||||
else if (action.equals("stop")) {
|
||||
this.stop(args.getString(0));
|
||||
}
|
||||
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) {
|
||||
// Starting listeners is easier to run on main thread, so don't run async.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity is to be shut down.
|
||||
* Stop listener.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
java.util.Set<Entry<String,GeoListener>> s = this.geoListeners.entrySet();
|
||||
java.util.Iterator<Entry<String,GeoListener>> it = s.iterator();
|
||||
while (it.hasNext()) {
|
||||
Entry<String,GeoListener> entry = it.next();
|
||||
GeoListener listener = entry.getValue();
|
||||
listener.destroy();
|
||||
}
|
||||
this.geoListeners.clear();
|
||||
if (this.global != null) {
|
||||
this.global.destroy();
|
||||
}
|
||||
this.global = null;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get current location.
|
||||
* The result is returned to JavaScript via a callback.
|
||||
*
|
||||
* @param enableHighAccuracy
|
||||
* @param timeout
|
||||
* @param maximumAge
|
||||
*/
|
||||
public void getCurrentLocation(boolean enableHighAccuracy, int timeout, int maximumAge) {
|
||||
|
||||
// Create a geolocation listener just for getCurrentLocation and call it "global"
|
||||
if (this.global == null) {
|
||||
this.global = new GeoListener(this, "global", maximumAge);
|
||||
}
|
||||
else {
|
||||
this.global.start(maximumAge);
|
||||
}
|
||||
}
|
||||
|
||||
public void getCurrentLocation()
|
||||
{
|
||||
GeoListener listener = new GeoListener("global", mCtx, 10000, mAppView);
|
||||
Location loc = listener.getCurrentLocation();
|
||||
String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + "," + loc.getAccuracy() + "," + loc.getBearing();
|
||||
params += "," + loc.getSpeed() + "," + loc.getTime();
|
||||
mAppView.loadUrl("javascript:navigator.geolocation.gotCurrentPosition(" + params + ")");
|
||||
listener.stop();
|
||||
}
|
||||
|
||||
public String start(int freq, String key)
|
||||
{
|
||||
GeoListener listener = new GeoListener(key, mCtx, freq, mAppView);
|
||||
geoListeners.put(key, listener);
|
||||
/**
|
||||
* Start geolocation listener and add to listener list.
|
||||
*
|
||||
* @param key The listener id
|
||||
* @param enableHighAccuracy
|
||||
* @param timeout
|
||||
* @param maximumAge
|
||||
* @return
|
||||
*/
|
||||
public String start(String key, boolean enableHighAccuracy, int timeout, int maximumAge) {
|
||||
|
||||
// Make sure this listener doesn't already exist
|
||||
GeoListener listener = geoListeners.get(key);
|
||||
if (listener == null) {
|
||||
listener = new GeoListener(this, key, maximumAge);
|
||||
geoListeners.put(key, listener);
|
||||
}
|
||||
|
||||
// Start it
|
||||
listener.start(maximumAge);
|
||||
return key;
|
||||
}
|
||||
|
||||
public void stop(String key)
|
||||
{
|
||||
GeoListener geo = geoListeners.get(key);
|
||||
/**
|
||||
* Stop geolocation listener and remove from listener list.
|
||||
*
|
||||
* @param key The listener id
|
||||
*/
|
||||
public void stop(String key) {
|
||||
GeoListener listener = geoListeners.remove(key);
|
||||
if (listener != null) {
|
||||
listener.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
169
framework/src/com/phonegap/GeoListener.java
Normal file → Executable file
169
framework/src/com/phonegap/GeoListener.java
Normal file → Executable file
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import android.content.Context;
|
||||
@@ -6,80 +13,110 @@ import android.location.LocationManager;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class GeoListener {
|
||||
String id;
|
||||
String successCallback;
|
||||
public static int PERMISSION_DENIED = 1;
|
||||
public static int POSITION_UNAVAILABLE = 2;
|
||||
public static int TIMEOUT = 3;
|
||||
|
||||
String id; // Listener ID
|
||||
String successCallback; //
|
||||
String failCallback;
|
||||
GpsListener mGps;
|
||||
NetworkListener mNetwork;
|
||||
LocationManager mLocMan;
|
||||
Context mCtx;
|
||||
private WebView mAppView;
|
||||
GpsListener mGps; // GPS listener
|
||||
NetworkListener mNetwork; // Network listener
|
||||
LocationManager mLocMan; // Location manager
|
||||
|
||||
private GeoBroker broker; // GeoBroker object
|
||||
|
||||
int interval;
|
||||
|
||||
GeoListener(String i, Context ctx, int time, WebView appView)
|
||||
{
|
||||
id = i;
|
||||
interval = time;
|
||||
mCtx = ctx;
|
||||
mGps = null;
|
||||
mNetwork = null;
|
||||
mLocMan = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
if (mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null)
|
||||
mGps = new GpsListener(mCtx, interval, this);
|
||||
if (mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null)
|
||||
mNetwork = new NetworkListener(mCtx, interval, this);
|
||||
mAppView = appView;
|
||||
}
|
||||
|
||||
void success(Location loc)
|
||||
{
|
||||
/*
|
||||
* We only need to figure out what we do when we succeed!
|
||||
*/
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param id Listener id
|
||||
* @param ctx
|
||||
* @param time Sampling period in msec
|
||||
* @param appView
|
||||
*/
|
||||
GeoListener(GeoBroker broker, String id, int time) {
|
||||
this.id = id;
|
||||
this.interval = time;
|
||||
this.broker = broker;
|
||||
this.mGps = null;
|
||||
this.mNetwork = null;
|
||||
this.mLocMan = (LocationManager) broker.ctx.getSystemService(Context.LOCATION_SERVICE);
|
||||
|
||||
// If GPS provider, then create and start GPS listener
|
||||
if (this.mLocMan.getProvider(LocationManager.GPS_PROVIDER) != null) {
|
||||
this.mGps = new GpsListener(broker.ctx, time, this);
|
||||
}
|
||||
|
||||
String params;
|
||||
/*
|
||||
* Build the giant string to send back to Javascript!
|
||||
*/
|
||||
params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() + "," + loc.getAccuracy() + "," + loc.getBearing();
|
||||
params += "," + loc.getSpeed() + "," + loc.getTime();
|
||||
if(id != "global")
|
||||
{
|
||||
mAppView.loadUrl("javascript:navigator._geo.success(" + id + "," + params + ")");
|
||||
}
|
||||
}
|
||||
|
||||
void fail()
|
||||
{
|
||||
// Do we need to know why? How would we handle this?
|
||||
if (id != "global") {
|
||||
mAppView.loadUrl("javascript:navigator._geo.fail(" + id + ")");
|
||||
}
|
||||
else
|
||||
{
|
||||
mAppView.loadUrl("javascript:navigator._geo.fail()");
|
||||
// If network provider, then create and start network listener
|
||||
if (this.mLocMan.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
|
||||
this.mNetwork = new NetworkListener(broker.ctx, time, this);
|
||||
}
|
||||
}
|
||||
|
||||
// This stops the listener
|
||||
void stop()
|
||||
{
|
||||
if(mGps != null)
|
||||
mGps.stop();
|
||||
if(mNetwork != null)
|
||||
mNetwork.stop();
|
||||
/**
|
||||
* Destroy listener.
|
||||
*/
|
||||
public void destroy() {
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Location found. Send location back to JavaScript.
|
||||
*
|
||||
* @param loc
|
||||
*/
|
||||
void success(Location loc) {
|
||||
|
||||
String params = loc.getLatitude() + "," + loc.getLongitude() + ", " + loc.getAltitude() +
|
||||
"," + loc.getAccuracy() + "," + loc.getBearing() +
|
||||
"," + loc.getSpeed() + "," + loc.getTime();
|
||||
|
||||
if (id == "global") {
|
||||
this.stop();
|
||||
}
|
||||
this.broker.sendJavascript("navigator._geo.success('" + id + "'," + params + ");");
|
||||
}
|
||||
|
||||
/**
|
||||
* Location failed. Send error back to JavaScript.
|
||||
*
|
||||
* @param code The error code
|
||||
* @param msg The error message
|
||||
*/
|
||||
void fail(int code, String msg) {
|
||||
this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');");
|
||||
this.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Start retrieving location.
|
||||
*
|
||||
* @param interval
|
||||
*/
|
||||
void start(int interval) {
|
||||
if (this.mGps != null) {
|
||||
this.mGps.start(interval);
|
||||
}
|
||||
if (this.mNetwork != null) {
|
||||
this.mNetwork.start(interval);
|
||||
}
|
||||
if (this.mNetwork == null && this.mGps == null) {
|
||||
this.fail(POSITION_UNAVAILABLE, "No location providers available.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening for location.
|
||||
*/
|
||||
void stop() {
|
||||
if (this.mGps != null) {
|
||||
this.mGps.stop();
|
||||
}
|
||||
if (this.mNetwork != null) {
|
||||
this.mNetwork.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public Location getCurrentLocation() {
|
||||
Location loc = null;
|
||||
if (mGps != null)
|
||||
loc = mGps.getLocation();
|
||||
if (loc == null && mNetwork != null)
|
||||
loc = mNetwork.getLocation();
|
||||
if(loc == null)
|
||||
loc = new Location(LocationManager.NETWORK_PROVIDER);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
180
framework/src/com/phonegap/GpsListener.java
Normal file → Executable file
180
framework/src/com/phonegap/GpsListener.java
Normal file → Executable file
@@ -1,101 +1,151 @@
|
||||
package com.phonegap;
|
||||
/* License (MIT)
|
||||
* Copyright (c) 2008 Nitobi
|
||||
* website: http://phonegap.com
|
||||
* 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.
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* 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.
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationListener;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class handles requests for GPS location services.
|
||||
*
|
||||
*/
|
||||
public class GpsListener implements LocationListener {
|
||||
|
||||
private Context mCtx;
|
||||
private Location cLoc;
|
||||
private LocationManager mLocMan;
|
||||
private static final String LOG_TAG = "PhoneGap";
|
||||
private GeoListener owner;
|
||||
private boolean hasData = false;
|
||||
private PhonegapActivity mCtx; // PhonegapActivity object
|
||||
|
||||
public GpsListener(Context ctx, int interval, GeoListener m)
|
||||
{
|
||||
owner = m;
|
||||
mCtx = ctx;
|
||||
mLocMan = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
|
||||
mLocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 0, this);
|
||||
cLoc = mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
private LocationManager mLocMan; // Location manager object
|
||||
private GeoListener owner; // Geolistener object (parent)
|
||||
private boolean hasData = false; // Flag indicates if location data is available in cLoc
|
||||
private Location cLoc; // Last recieved location
|
||||
private boolean running = false; // Flag indicates if listener is running
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Automatically starts listening.
|
||||
*
|
||||
* @param ctx
|
||||
* @param interval
|
||||
* @param m
|
||||
*/
|
||||
public GpsListener(PhonegapActivity ctx, int interval, GeoListener m) {
|
||||
this.owner = m;
|
||||
this.mCtx = ctx;
|
||||
this.mLocMan = (LocationManager) this.mCtx.getSystemService(Context.LOCATION_SERVICE);
|
||||
this.running = false;
|
||||
this.start(interval);
|
||||
}
|
||||
|
||||
public Location getLocation()
|
||||
{
|
||||
cLoc = mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
hasData = true;
|
||||
return cLoc;
|
||||
/**
|
||||
* Get last location.
|
||||
*
|
||||
* @return Location object
|
||||
*/
|
||||
public Location getLocation() {
|
||||
this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.GPS_PROVIDER);
|
||||
if (this.cLoc != null) {
|
||||
this.hasData = true;
|
||||
}
|
||||
return this.cLoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the provider is disabled by the user.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void onProviderDisabled(String provider) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d(LOG_TAG, "The provider " + provider + " is disabled");
|
||||
owner.fail();
|
||||
this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS provider disabled.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the provider is enabled by the user.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void onProviderEnabled(String provider) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d(LOG_TAG, "The provider "+ provider + " is enabled");
|
||||
System.out.println("GpsListener: The provider "+ provider + " is enabled");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the provider status changes. This method is called when a
|
||||
* provider is unable to fetch a location or if the provider has recently
|
||||
* become available after a period of unavailability.
|
||||
*
|
||||
* @param provider
|
||||
* @param status
|
||||
* @param extras
|
||||
*/
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d(LOG_TAG, "The status of the provider " + provider + " has changed");
|
||||
if(status == 0)
|
||||
{
|
||||
Log.d(LOG_TAG, provider + " is OUT OF SERVICE");
|
||||
owner.fail();
|
||||
System.out.println("GpsListener: The status of the provider " + provider + " has changed");
|
||||
if (status == 0) {
|
||||
System.out.println("GpsListener: " + provider + " is OUT OF SERVICE");
|
||||
this.owner.fail(GeoListener.POSITION_UNAVAILABLE, "GPS out of service.");
|
||||
}
|
||||
else if(status == 1)
|
||||
{
|
||||
Log.d(LOG_TAG, provider + " is TEMPORARILY_UNAVAILABLE");
|
||||
else if (status == 1) {
|
||||
System.out.println("GpsListener: " + provider + " is TEMPORARILY_UNAVAILABLE");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(LOG_TAG, provider + " is Available");
|
||||
else {
|
||||
System.out.println("GpsListener: " + provider + " is Available");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the location has changed.
|
||||
*
|
||||
* @param location
|
||||
*/
|
||||
public void onLocationChanged(Location location) {
|
||||
Log.d(LOG_TAG, "The location has been updated!");
|
||||
owner.success(location);
|
||||
System.out.println("GpsListener: The location has been updated!");
|
||||
this.hasData = true;
|
||||
this.cLoc = location;
|
||||
this.owner.success(location);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if location data is available.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean hasLocation() {
|
||||
return hasData;
|
||||
return this.hasData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start requesting location updates.
|
||||
*
|
||||
* @param interval
|
||||
*/
|
||||
public void start(int interval) {
|
||||
if (!this.running) {
|
||||
this.running = true;
|
||||
this.mLocMan.requestLocationUpdates(LocationManager.GPS_PROVIDER, interval, 0, this);
|
||||
this.getLocation();
|
||||
|
||||
// If GPS provider has data, then send now
|
||||
if (this.hasData) {
|
||||
this.owner.success(this.cLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
mLocMan.removeUpdates(this);
|
||||
/**
|
||||
* Stop receiving location updates.
|
||||
*/
|
||||
public void stop() {
|
||||
if (this.running) {
|
||||
this.mLocMan.removeUpdates(this);
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
8
framework/src/com/phonegap/HttpHandler.java
Normal file → Executable file
8
framework/src/com/phonegap/HttpHandler.java
Normal file → Executable file
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.EOFException;
|
||||
@@ -55,7 +62,6 @@ public class HttpHandler {
|
||||
if (numread <= 0)
|
||||
break;
|
||||
out.write(buff, 0, numread);
|
||||
System.out.println("numread" + numread);
|
||||
i++;
|
||||
} while (true);
|
||||
out.flush();
|
||||
|
||||
176
framework/src/com/phonegap/NetworkListener.java
Normal file → Executable file
176
framework/src/com/phonegap/NetworkListener.java
Normal file → Executable file
@@ -1,102 +1,142 @@
|
||||
package com.phonegap;
|
||||
/* License (MIT)
|
||||
* Copyright (c) 2008 Nitobi
|
||||
* website: http://phonegap.com
|
||||
* 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.
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* 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.
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.location.LocationListener;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
public class NetworkListener implements LocationListener {
|
||||
|
||||
private Context mCtx;
|
||||
private Location cLoc;
|
||||
private LocationManager mLocMan;
|
||||
private static final String LOG_TAG = "PhoneGap";
|
||||
GeoListener owner;
|
||||
|
||||
public NetworkListener(Context ctx, int interval, GeoListener m)
|
||||
{
|
||||
owner = m;
|
||||
mCtx = ctx;
|
||||
mLocMan = (LocationManager) mCtx.getSystemService(Context.LOCATION_SERVICE);
|
||||
mLocMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, 0, this);
|
||||
cLoc = mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
|
||||
private PhonegapActivity mCtx; // PhonegapActivity object
|
||||
|
||||
private LocationManager mLocMan; // Location manager object
|
||||
private GeoListener owner; // Geolistener object (parent)
|
||||
private boolean hasData = false; // Flag indicates if location data is available in cLoc
|
||||
private Location cLoc; // Last recieved location
|
||||
private boolean running = false; // Flag indicates if listener is running
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* Automatically starts listening.
|
||||
*
|
||||
* @param ctx
|
||||
* @param interval
|
||||
* @param m
|
||||
*/
|
||||
public NetworkListener(PhonegapActivity ctx, int interval, GeoListener m) {
|
||||
this.owner = m;
|
||||
this.mCtx = ctx;
|
||||
this.mLocMan = (LocationManager) this.mCtx.getSystemService(Context.LOCATION_SERVICE);
|
||||
this.running = false;
|
||||
this.start(interval);
|
||||
}
|
||||
|
||||
public Location getLocation()
|
||||
{
|
||||
cLoc = mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
|
||||
return cLoc;
|
||||
/**
|
||||
* Get last location.
|
||||
*
|
||||
* @return Location object
|
||||
*/
|
||||
public Location getLocation() {
|
||||
this.cLoc = this.mLocMan.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
|
||||
if (this.cLoc != null) {
|
||||
this.hasData = true;
|
||||
}
|
||||
return this.cLoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the provider is disabled by the user.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void onProviderDisabled(String provider) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d(LOG_TAG, "The provider " + provider + " is disabled");
|
||||
System.out.println("NetworkListener: The provider " + provider + " is disabled");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the provider is enabled by the user.
|
||||
*
|
||||
* @param provider
|
||||
*/
|
||||
public void onProviderEnabled(String provider) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d(LOG_TAG, "The provider "+ provider + " is enabled");
|
||||
System.out.println("NetworkListener: The provider "+ provider + " is enabled");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when the provider status changes. This method is called when a
|
||||
* provider is unable to fetch a location or if the provider has recently
|
||||
* become available after a period of unavailability.
|
||||
*
|
||||
* @param provider
|
||||
* @param status
|
||||
* @param extras
|
||||
*/
|
||||
public void onStatusChanged(String provider, int status, Bundle extras) {
|
||||
// TODO Auto-generated method stub
|
||||
Log.d(LOG_TAG, "The status of the provider " + provider + " has changed");
|
||||
if(status == 0)
|
||||
{
|
||||
Log.d(LOG_TAG, provider + " is OUT OF SERVICE");
|
||||
System.out.println("NetworkListener: The status of the provider " + provider + " has changed");
|
||||
if (status == 0) {
|
||||
System.out.println("NetworkListener: " + provider + " is OUT OF SERVICE");
|
||||
}
|
||||
else if(status == 1)
|
||||
{
|
||||
Log.d(LOG_TAG, provider + " is TEMPORARILY_UNAVAILABLE");
|
||||
else if (status == 1) {
|
||||
System.out.println("NetworkListener: " + provider + " is TEMPORARILY_UNAVAILABLE");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.d(LOG_TAG, provider + " is Available");
|
||||
else {
|
||||
System.out.println("NetworkListener: " + provider + " is Available");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The GPS is the primary form of Geolocation in PhoneGap. Only fire the success variables if the GPS is down
|
||||
* for some reason
|
||||
/**
|
||||
* Called when the location has changed.
|
||||
*
|
||||
* @param location
|
||||
*/
|
||||
public void onLocationChanged(Location location) {
|
||||
Log.d(LOG_TAG, "The location has been updated!");
|
||||
if (!owner.mGps.hasLocation())
|
||||
{
|
||||
owner.success(location);
|
||||
System.out.println("NetworkListener: The location has been updated!");
|
||||
this.hasData = true;
|
||||
this.cLoc = location;
|
||||
|
||||
// The GPS is the primary form of Geolocation in PhoneGap.
|
||||
// Only fire the success variables if the GPS is down for some reason.
|
||||
if (!this.owner.mGps.hasLocation()) {
|
||||
this.owner.success(location);
|
||||
}
|
||||
cLoc = location;
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
mLocMan.removeUpdates(this);
|
||||
/**
|
||||
* Start requesting location updates.
|
||||
*
|
||||
* @param interval
|
||||
*/
|
||||
public void start(int interval) {
|
||||
if (!this.running) {
|
||||
this.running = true;
|
||||
this.mLocMan.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, interval, 0, this);
|
||||
this.getLocation();
|
||||
|
||||
// If Network provider has data but GPS provider doesn't, then send ours
|
||||
if (this.hasData && !this.owner.mGps.hasLocation()) {
|
||||
this.owner.success(this.cLoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop receiving location updates.
|
||||
*/
|
||||
public void stop() {
|
||||
if (this.running) {
|
||||
this.mLocMan.removeUpdates(this);
|
||||
}
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
247
framework/src/com/phonegap/NetworkManager.java
Normal file → Executable file
247
framework/src/com/phonegap/NetworkManager.java
Normal file → Executable file
@@ -1,57 +1,222 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.net.*;
|
||||
import android.webkit.WebView;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class NetworkManager {
|
||||
public class NetworkManager extends Plugin {
|
||||
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
|
||||
public static final String WIFI = "wifi";
|
||||
public static final String WIMAX = "wimax";
|
||||
// mobile
|
||||
public static final String MOBILE = "mobile";
|
||||
// 2G network types
|
||||
public static final String GSM = "gsm";
|
||||
public static final String GPRS = "gprs";
|
||||
public static final String EDGE = "edge";
|
||||
// 3G network types
|
||||
public static final String CDMA = "cdma";
|
||||
public static final String UMTS = "umts";
|
||||
// 4G network types
|
||||
public static final String LTE = "lte";
|
||||
public static final String UMB = "umb";
|
||||
// return types
|
||||
public static final String TYPE_UNKNOWN = "unknown";
|
||||
public static final String TYPE_ETHERNET = "ethernet";
|
||||
public static final String TYPE_WIFI = "wifi";
|
||||
public static final String TYPE_2G = "2g";
|
||||
public static final String TYPE_3G = "3g";
|
||||
public static final String TYPE_4G = "4g";
|
||||
public static final String TYPE_NONE = "none";
|
||||
|
||||
private static final String LOG_TAG = "NetworkManager";
|
||||
|
||||
private String connectionCallbackId;
|
||||
|
||||
Context mCtx;
|
||||
WebView mView;
|
||||
ConnectivityManager sockMan;
|
||||
BroadcastReceiver receiver;
|
||||
|
||||
NetworkManager(Context ctx, WebView view)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mView = view;
|
||||
sockMan = (ConnectivityManager) mCtx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NetworkManager() {
|
||||
this.receiver = null;
|
||||
}
|
||||
|
||||
public boolean isAvailable()
|
||||
{
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
boolean conn = false;
|
||||
if(info != null)
|
||||
conn = info.isConnected();
|
||||
return conn;
|
||||
}
|
||||
|
||||
public boolean isWifiActive()
|
||||
{
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
if(info != null)
|
||||
{
|
||||
String type = info.getTypeName();
|
||||
return type.equals("WIFI");
|
||||
|
||||
/**
|
||||
* 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(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.connectionCallbackId = null;
|
||||
|
||||
// We need to listen to connectivity events to update navigator.connection
|
||||
IntentFilter intentFilter = new IntentFilter() ;
|
||||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
if (this.receiver == null) {
|
||||
this.receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
}
|
||||
};
|
||||
ctx.registerReceiver(this.receiver, intentFilter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.INVALID_ACTION;
|
||||
String result = "Unsupported Operation: " + action;
|
||||
|
||||
if (action.equals("getConnectionInfo")) {
|
||||
this.connectionCallbackId = callbackId;
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
|
||||
pluginResult.setKeepCallback(true);
|
||||
return pluginResult;
|
||||
}
|
||||
|
||||
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) {
|
||||
// All methods take a while, so always use async
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isReachable(String uri)
|
||||
{
|
||||
if (uri.indexOf("http://") == -1)
|
||||
uri = "http://" + uri;
|
||||
boolean reached = isAvailable();
|
||||
try {
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
HttpGet httpget = new HttpGet(uri);
|
||||
httpclient.execute(httpget);
|
||||
} catch (Exception e) {
|
||||
reached = false;
|
||||
/**
|
||||
* Stop network receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
if (this.receiver != null) {
|
||||
try {
|
||||
this.ctx.unregisterReceiver(this.receiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return reached;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Updates the JavaScript side whenever the connection changes
|
||||
*
|
||||
* @param info the current active network info
|
||||
* @return
|
||||
*/
|
||||
private void updateConnectionInfo(NetworkInfo info) {
|
||||
// send update to javascript "navigator.network.connection"
|
||||
sendUpdate(this.getConnectionInfo(info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest network connection information
|
||||
*
|
||||
* @param info the current active network info
|
||||
* @return a JSONObject that represents the network info
|
||||
*/
|
||||
private String getConnectionInfo(NetworkInfo info) {
|
||||
String type = TYPE_NONE;
|
||||
if (info != null) {
|
||||
// If we are not connected to any network set type to none
|
||||
if (!info.isConnected()) {
|
||||
type = TYPE_NONE;
|
||||
}
|
||||
else {
|
||||
type = getType(info);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
*
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(String type) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||
result.setKeepCallback(true);
|
||||
this.success(result, this.connectionCallbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of connection
|
||||
*
|
||||
* @param info the network info so we can determine connection type.
|
||||
* @return the type of mobile network we are on
|
||||
*/
|
||||
private String getType(NetworkInfo info) {
|
||||
if (info != null) {
|
||||
String type = info.getTypeName();
|
||||
|
||||
if (type.toLowerCase().equals(WIFI)) {
|
||||
return TYPE_WIFI;
|
||||
}
|
||||
else if (type.toLowerCase().equals(MOBILE)) {
|
||||
type = info.getSubtypeName();
|
||||
if (type.toLowerCase().equals(GSM) ||
|
||||
type.toLowerCase().equals(GPRS) ||
|
||||
type.toLowerCase().equals(EDGE)) {
|
||||
return TYPE_2G;
|
||||
}
|
||||
else if (type.toLowerCase().equals(CDMA) ||
|
||||
type.toLowerCase().equals(UMTS)) {
|
||||
return TYPE_3G;
|
||||
}
|
||||
else if (type.toLowerCase().equals(LTE) ||
|
||||
type.toLowerCase().equals(UMB)) {
|
||||
return TYPE_4G;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
355
framework/src/com/phonegap/Notification.java
Executable file
355
framework/src/com/phonegap/Notification.java
Executable file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Vibrator;
|
||||
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
*/
|
||||
public class Notification extends Plugin {
|
||||
|
||||
public int confirmResult = -1;
|
||||
public ProgressDialog spinnerDialog = null;
|
||||
public ProgressDialog progressDialog = null;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Notification() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("alert")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("confirm")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("activityStart")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("activityStop")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("progressStart")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("progressValue")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("progressStop")) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Beep plays the default notification ringtone.
|
||||
*
|
||||
* @param count Number of times to play notification
|
||||
*/
|
||||
public void beep(long count) {
|
||||
Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
Ringtone notification = RingtoneManager.getRingtone(this.ctx, ringtone);
|
||||
|
||||
// If phone is not set to silent mode
|
||||
if (notification != null) {
|
||||
for (long i = 0; i < count; ++i) {
|
||||
notification.play();
|
||||
long timeout = 5000;
|
||||
while (notification.isPlaying() && (timeout > 0)) {
|
||||
timeout = timeout - 100;
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vibrates the device for the specified amount of time.
|
||||
*
|
||||
* @param time Time to vibrate in ms.
|
||||
*/
|
||||
public void vibrate(long time){
|
||||
// Start the vibration, 0 defaults to half a second.
|
||||
if (time == 0) {
|
||||
time = 500;
|
||||
}
|
||||
Vibrator vibrator = (Vibrator) this.ctx.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vibrator.vibrate(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and shows a native Android alert with given Strings
|
||||
* @param message The message the alert should display
|
||||
* @param title The title of the alert
|
||||
* @param buttonLabel The label of the button
|
||||
* @param callbackId The callback id
|
||||
*/
|
||||
public synchronized void alert(final String message, final String title, final String buttonLabel, final String callbackId) {
|
||||
|
||||
final PhonegapActivity ctx = this.ctx;
|
||||
final Notification notification = this;
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(ctx);
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle(title);
|
||||
dlg.setCancelable(false);
|
||||
dlg.setPositiveButton(buttonLabel,
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 0), callbackId);
|
||||
}
|
||||
});
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
};
|
||||
};
|
||||
this.ctx.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and shows a native Android confirm dialog with given title, message, buttons.
|
||||
* This dialog only shows up to 3 buttons. Any labels after that will be ignored.
|
||||
* The index of the button pressed will be returned to the JavaScript callback identified by callbackId.
|
||||
*
|
||||
* @param message The message the dialog should display
|
||||
* @param title The title of the dialog
|
||||
* @param buttonLabels A comma separated list of button labels (Up to 3 buttons)
|
||||
* @param callbackId The callback id
|
||||
*/
|
||||
public synchronized void confirm(final String message, final String title, String buttonLabels, final String callbackId) {
|
||||
|
||||
final PhonegapActivity ctx = this.ctx;
|
||||
final Notification notification = this;
|
||||
final String[] fButtons = buttonLabels.split(",");
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(ctx);
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle(title);
|
||||
dlg.setCancelable(false);
|
||||
|
||||
// First button
|
||||
if (fButtons.length > 0) {
|
||||
dlg.setPositiveButton(fButtons[0],
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 1), callbackId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Second button
|
||||
if (fButtons.length > 1) {
|
||||
dlg.setNeutralButton(fButtons[1],
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 2), callbackId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Third button
|
||||
if (fButtons.length > 2) {
|
||||
dlg.setNegativeButton(fButtons[2],
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
notification.success(new PluginResult(PluginResult.Status.OK, 3), callbackId);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
};
|
||||
};
|
||||
this.ctx.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the spinner.
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
public synchronized void activityStart(final String title, final String message) {
|
||||
if (this.spinnerDialog != null) {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
final Notification notification = this;
|
||||
final PhonegapActivity ctx = this.ctx;
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
notification.spinnerDialog = ProgressDialog.show(ctx, title , message, true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
notification.spinnerDialog = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
this.ctx.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop spinner.
|
||||
*/
|
||||
public synchronized void activityStop() {
|
||||
if (this.spinnerDialog != null) {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the progress dialog.
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
public synchronized void progressStart(final String title, final String message) {
|
||||
if (this.progressDialog != null) {
|
||||
this.progressDialog.dismiss();
|
||||
this.progressDialog = null;
|
||||
}
|
||||
final Notification notification = this;
|
||||
final PhonegapActivity ctx = this.ctx;
|
||||
Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
notification.progressDialog = new ProgressDialog(ctx);
|
||||
notification.progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
notification.progressDialog.setTitle(title);
|
||||
notification.progressDialog.setMessage(message);
|
||||
notification.progressDialog.setCancelable(true);
|
||||
notification.progressDialog.setMax(100);
|
||||
notification.progressDialog.setProgress(0);
|
||||
notification.progressDialog.setOnCancelListener(
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
notification.progressDialog = null;
|
||||
}
|
||||
});
|
||||
notification.progressDialog.show();
|
||||
}
|
||||
};
|
||||
this.ctx.runOnUiThread(runnable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value of progress bar.
|
||||
*
|
||||
* @param value 0-100
|
||||
*/
|
||||
public synchronized void progressValue(int value) {
|
||||
if (this.progressDialog != null) {
|
||||
this.progressDialog.setProgress(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop progress dialog.
|
||||
*/
|
||||
public synchronized void progressStop() {
|
||||
if (this.progressDialog != null) {
|
||||
this.progressDialog.dismiss();
|
||||
this.progressDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,199 +0,0 @@
|
||||
package com.phonegap;
|
||||
/* License (MIT)
|
||||
* Copyright (c) 2008 Nitobi
|
||||
* website: http://phonegap.com
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.util.TimeZone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.hardware.SensorManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Vibrator;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.webkit.WebView;
|
||||
import android.media.Ringtone;
|
||||
import android.media.RingtoneManager;
|
||||
|
||||
public class PhoneGap{
|
||||
|
||||
private static final String LOG_TAG = "PhoneGap";
|
||||
/*
|
||||
* UUID, version and availability
|
||||
*/
|
||||
public boolean droid = true;
|
||||
public static String version = "0.9.99999";
|
||||
public static String platform = "Android";
|
||||
public static String uuid;
|
||||
private Context mCtx;
|
||||
private WebView mAppView;
|
||||
AudioHandler audio;
|
||||
|
||||
public PhoneGap(Context ctx, WebView appView) {
|
||||
this.mCtx = ctx;
|
||||
this.mAppView = appView;
|
||||
audio = new AudioHandler("/sdcard/tmprecording.mp3", ctx);
|
||||
uuid = getUuid();
|
||||
}
|
||||
|
||||
public void beep(long pattern)
|
||||
{
|
||||
Uri ringtone = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
Ringtone notification = RingtoneManager.getRingtone(mCtx, ringtone);
|
||||
for (long i = 0; i < pattern; ++i)
|
||||
{
|
||||
notification.play();
|
||||
}
|
||||
}
|
||||
|
||||
public void vibrate(long pattern){
|
||||
// Start the vibration, 0 defaults to half a second.
|
||||
if (pattern == 0)
|
||||
pattern = 500;
|
||||
Vibrator vibrator = (Vibrator) mCtx.getSystemService(Context.VIBRATOR_SERVICE);
|
||||
vibrator.vibrate(pattern);
|
||||
}
|
||||
|
||||
public String getPlatform()
|
||||
{
|
||||
return this.platform;
|
||||
}
|
||||
|
||||
public String getUuid()
|
||||
{
|
||||
//TelephonyManager operator = (TelephonyManager) mCtx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
//String uuid = operator.getDeviceId();
|
||||
String uuid = Settings.Secure.getString(mCtx.getContentResolver(), android.provider.Settings.Secure.ANDROID_ID);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public String getLine1Number(){
|
||||
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getLine1Number();
|
||||
}
|
||||
public String getDeviceId(){
|
||||
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getDeviceId();
|
||||
}
|
||||
public String getSimSerialNumber(){
|
||||
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSimSerialNumber();
|
||||
}
|
||||
public String getSubscriberId(){
|
||||
TelephonyManager operator = (TelephonyManager)mCtx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSubscriberId();
|
||||
}
|
||||
|
||||
public String getModel()
|
||||
{
|
||||
String model = android.os.Build.MODEL;
|
||||
return model;
|
||||
}
|
||||
public String getProductName()
|
||||
{
|
||||
String productname = android.os.Build.PRODUCT;
|
||||
return productname;
|
||||
}
|
||||
public String getOSVersion()
|
||||
{
|
||||
String osversion = android.os.Build.VERSION.RELEASE;
|
||||
return osversion;
|
||||
}
|
||||
public String getSDKVersion()
|
||||
{
|
||||
String sdkversion = android.os.Build.VERSION.SDK;
|
||||
return sdkversion;
|
||||
}
|
||||
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
public void httpGet(String url, String file)
|
||||
/**
|
||||
* grabs a file from specified url and saves it to a name and location
|
||||
* the base directory /sdcard is abstracted so that paths may be the same from one mobile OS to another
|
||||
* TODO: JavaScript call backs and error handling
|
||||
*/
|
||||
{
|
||||
HttpHandler http = new HttpHandler();
|
||||
http.get(url, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* AUDIO
|
||||
* TODO: Basic functions done but needs more work on error handling and call backs, remove record hack
|
||||
*/
|
||||
|
||||
public void startRecordingAudio(String file)
|
||||
{
|
||||
/* for this to work the recording needs to be specified in the constructor,
|
||||
* a hack to get around this, I'm moving the recording after it's complete
|
||||
*/
|
||||
audio.startRecording(file);
|
||||
}
|
||||
|
||||
public void stopRecordingAudio()
|
||||
{
|
||||
audio.stopRecording();
|
||||
}
|
||||
|
||||
public void startPlayingAudio(String file)
|
||||
{
|
||||
audio.startPlaying(file);
|
||||
}
|
||||
|
||||
public void stopPlayingAudio()
|
||||
{
|
||||
audio.stopPlaying();
|
||||
}
|
||||
|
||||
public long getCurrentPositionAudio()
|
||||
{
|
||||
System.out.println(audio.getCurrentPosition());
|
||||
return(audio.getCurrentPosition());
|
||||
}
|
||||
|
||||
public long getDurationAudio(String file)
|
||||
{
|
||||
System.out.println(audio.getDuration(file));
|
||||
return(audio.getDuration(file));
|
||||
}
|
||||
|
||||
public void setAudioOutputDevice(int output){
|
||||
audio.setAudioOutputDevice(output);
|
||||
}
|
||||
|
||||
public int getAudioOutputDevice(){
|
||||
return audio.getAudioOutputDevice();
|
||||
}
|
||||
|
||||
public String getTimeZoneID() {
|
||||
TimeZone tz = TimeZone.getDefault();
|
||||
return(tz.getID());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
298
framework/src/com/phonegap/Storage.java
Normal file → Executable file
298
framework/src/com/phonegap/Storage.java
Normal file → Executable file
@@ -1,78 +1,246 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import android.content.Context;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.*;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class Storage {
|
||||
/**
|
||||
* This class implements the HTML5 database support for Android 1.X devices. It
|
||||
* is not used for Android 2.X, since HTML5 database is built in to the browser.
|
||||
*/
|
||||
public class Storage extends Plugin {
|
||||
|
||||
// Data Definition Language
|
||||
private static final String ALTER = "alter";
|
||||
private static final String CREATE = "create";
|
||||
private static final String DROP = "drop";
|
||||
private static final String TRUNCATE = "truncate";
|
||||
|
||||
private static final String LOG_TAG = "SQLite Storage:";
|
||||
SQLiteDatabase myDb;
|
||||
String path;
|
||||
String txid = "";
|
||||
WebView appView;
|
||||
Context mCtx;
|
||||
|
||||
Storage(WebView view)
|
||||
{
|
||||
appView = view;
|
||||
SQLiteDatabase myDb = null; // Database object
|
||||
String path = null; // Database path
|
||||
String dbName = null; // Database name
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Storage() {
|
||||
}
|
||||
|
||||
public void setStorage(String appPackage)
|
||||
{
|
||||
path = "/data/data/" + appPackage + "/databases/";
|
||||
}
|
||||
|
||||
public void openDatabase(String db, String version, String display_name, long size)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
path += db + ".db";
|
||||
myDb = SQLiteDatabase.openOrCreateDatabase(path, null);
|
||||
|
||||
/**
|
||||
* 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 = "";
|
||||
|
||||
try {
|
||||
// TODO: Do we want to allow a user to do this, since they could get
|
||||
// to other app databases?
|
||||
if (action.equals("setStorage")) {
|
||||
this.setStorage(args.getString(0));
|
||||
} else if (action.equals("openDatabase")) {
|
||||
this.openDatabase(args.getString(0), args.getString(1),
|
||||
args.getString(2), args.getLong(3));
|
||||
} else if (action.equals("executeSql")) {
|
||||
String[] s = null;
|
||||
if (args.isNull(1)) {
|
||||
s = new String[0];
|
||||
} else {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
s = new String[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
s[i] = a.getString(i);
|
||||
}
|
||||
}
|
||||
this.executeSql(args.getString(0), s, args.getString(2));
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public void executeSql(String query, String[] params, String tx_id)
|
||||
{
|
||||
try{
|
||||
txid = tx_id;
|
||||
Cursor myCursor = myDb.rawQuery(query, params);
|
||||
processResults(myCursor);
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
txid = "";
|
||||
appView.loadUrl("droiddb.fail(" + ex.getMessage() + "," + txid + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public void processResults(Cursor cur)
|
||||
{
|
||||
String key = "";
|
||||
String value = "";
|
||||
String resultString = "";
|
||||
|
||||
/**
|
||||
* Clean up and close database.
|
||||
*/
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (this.myDb != null) {
|
||||
this.myDb.close();
|
||||
this.myDb = null;
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set the application package for the database. Each application saves its
|
||||
* database files in a directory with the application package as part of the
|
||||
* file name.
|
||||
*
|
||||
* For example, application "com.phonegap.demo.Demo" would save its database
|
||||
* files in "/data/data/com.phonegap.demo/databases/" directory.
|
||||
*
|
||||
* @param appPackage
|
||||
* The application package.
|
||||
*/
|
||||
public void setStorage(String appPackage) {
|
||||
this.path = "/data/data/" + appPackage + "/databases/";
|
||||
}
|
||||
|
||||
/**
|
||||
* Open database.
|
||||
*
|
||||
* @param db
|
||||
* The name of the database
|
||||
* @param version
|
||||
* The version
|
||||
* @param display_name
|
||||
* The display name
|
||||
* @param size
|
||||
* The size in bytes
|
||||
*/
|
||||
public void openDatabase(String db, String version, String display_name,
|
||||
long size) {
|
||||
|
||||
// If database is open, then close it
|
||||
if (this.myDb != null) {
|
||||
this.myDb.close();
|
||||
}
|
||||
|
||||
// If no database path, generate from application package
|
||||
if (this.path == null) {
|
||||
Package pack = this.ctx.getClass().getPackage();
|
||||
String appPackage = pack.getName();
|
||||
this.setStorage(appPackage);
|
||||
}
|
||||
|
||||
this.dbName = this.path + db + ".db";
|
||||
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute SQL statement.
|
||||
*
|
||||
* @param query
|
||||
* The SQL query
|
||||
* @param params
|
||||
* Parameters for the query
|
||||
* @param tx_id
|
||||
* Transaction id
|
||||
*/
|
||||
public void executeSql(String query, String[] params, String tx_id) {
|
||||
try {
|
||||
if (isDDL(query)) {
|
||||
this.myDb.execSQL(query);
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', '');");
|
||||
}
|
||||
else {
|
||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||
this.processResults(myCursor, tx_id);
|
||||
myCursor.close();
|
||||
}
|
||||
}
|
||||
catch (SQLiteException ex) {
|
||||
ex.printStackTrace();
|
||||
System.out.println("Storage.executeSql(): Error=" + ex.getMessage());
|
||||
|
||||
// Send error message back to JavaScript
|
||||
this.sendJavascript("droiddb.fail('" + ex.getMessage() + "','" + tx_id + "');");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see the the query is a Data Definintion command
|
||||
*
|
||||
* @param query to be executed
|
||||
* @return true if it is a DDL command, false otherwise
|
||||
*/
|
||||
private boolean isDDL(String query) {
|
||||
String cmd = query.toLowerCase();
|
||||
if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process query results.
|
||||
*
|
||||
* @param cur
|
||||
* Cursor into query results
|
||||
* @param tx_id
|
||||
* Transaction id
|
||||
*/
|
||||
public void processResults(Cursor cur, String tx_id) {
|
||||
|
||||
String result = "[]";
|
||||
// If query result has rows
|
||||
|
||||
if (cur.moveToFirst()) {
|
||||
int colCount = cur.getColumnCount();
|
||||
do {
|
||||
resultString = "{";
|
||||
for(int i = 0; i < colCount; ++i)
|
||||
{
|
||||
key = cur.getColumnName(i);
|
||||
value = cur.getString(i);
|
||||
resultString += " \"" + key + "\" : \"" + value + "\"";
|
||||
if (i != (colCount - 1))
|
||||
resultString += ",";
|
||||
}
|
||||
resultString += "}";
|
||||
appView.loadUrl("javascript:droiddb.addResult('" + resultString + "', " + txid + ")");
|
||||
} while (cur.moveToNext());
|
||||
appView.loadUrl("javascript:droiddb.completeQuery(" + txid + ")");
|
||||
txid = "";
|
||||
myDb.close();
|
||||
}
|
||||
JSONArray fullresult = new JSONArray();
|
||||
String key = "";
|
||||
String value = "";
|
||||
int colCount = cur.getColumnCount();
|
||||
|
||||
// Build up JSON result object for each row
|
||||
do {
|
||||
JSONObject row = new JSONObject();
|
||||
try {
|
||||
for (int i = 0; i < colCount; ++i) {
|
||||
key = cur.getColumnName(i);
|
||||
value = cur.getString(i);
|
||||
row.put(key, value);
|
||||
}
|
||||
fullresult.put(row);
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} while (cur.moveToNext());
|
||||
|
||||
result = fullresult.toString();
|
||||
}
|
||||
|
||||
// Let JavaScript know that there are no more rows
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', " + result
|
||||
+ ");");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
86
framework/src/com/phonegap/TempListener.java
Normal file → Executable file
86
framework/src/com/phonegap/TempListener.java
Normal file → Executable file
@@ -1,53 +1,101 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.content.Context;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class TempListener implements SensorEventListener {
|
||||
WebView mAppView;
|
||||
Context mCtx;
|
||||
Sensor mSensor;
|
||||
public class TempListener extends Plugin implements SensorEventListener {
|
||||
|
||||
Sensor mSensor;
|
||||
private SensorManager sensorManager;
|
||||
|
||||
TempListener(Context ctx, WebView appView)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mAppView = appView;
|
||||
sensorManager = (SensorManager) mCtx.getSystemService(Context.SENSOR_SERVICE);
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public TempListener() {
|
||||
}
|
||||
|
||||
public void start()
|
||||
{
|
||||
/**
|
||||
* 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(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sensorManager = (SensorManager) ctx.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() {
|
||||
List<Sensor> list = this.sensorManager.getSensorList(Sensor.TYPE_TEMPERATURE);
|
||||
if (list.size() > 0)
|
||||
{
|
||||
if (list.size() > 0) {
|
||||
this.mSensor = list.get(0);
|
||||
this.sensorManager.registerListener(this, this.mSensor, SensorManager.SENSOR_DELAY_NORMAL);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
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];
|
||||
mAppView.loadUrl("javascript:gotTemp(" + temp + ")");
|
||||
this.sendJavascript("gotTemp(" + temp + ");");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
90
framework/src/com/phonegap/api/IPlugin.java
Executable file
90
framework/src/com/phonegap/api/IPlugin.java
Executable file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import android.content.Intent;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* Plugin interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the PluginManager.
|
||||
*/
|
||||
public interface IPlugin {
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
PluginResult execute(String action, JSONArray args, String callbackId);
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action);
|
||||
|
||||
/**
|
||||
* Sets the context of the Plugin. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
*/
|
||||
void setContext(PhonegapActivity ctx);
|
||||
|
||||
/**
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* a PhoneGap app runs.
|
||||
*
|
||||
* @param webView The PhoneGap WebView
|
||||
*/
|
||||
void setView(WebView webView);
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onPause(boolean multitasking);
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onResume(boolean multitasking);
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
void onNewIntent(Intent intent);
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
void onDestroy();
|
||||
|
||||
/**
|
||||
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
||||
* the resultCode it returned, and any additional data from it.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
void onActivityResult(int requestCode, int resultCode, Intent intent);
|
||||
|
||||
}
|
||||
50
framework/src/com/phonegap/api/PhonegapActivity.java
Executable file
50
framework/src/com/phonegap/api/PhonegapActivity.java
Executable file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
/**
|
||||
* The Phonegap activity abstract class that is extended by DroidGap.
|
||||
* It is used to isolate plugin development, and remove dependency on entire Phonegap library.
|
||||
*/
|
||||
public abstract class PhonegapActivity extends Activity {
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
abstract public void addService(String serviceType, String className);
|
||||
|
||||
/**
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
abstract public void sendJavascript(String statement);
|
||||
|
||||
/**
|
||||
* Launch an activity for which you would like a result when it finished. When this activity exits,
|
||||
* your onActivityResult() method will be called.
|
||||
*
|
||||
* @param command The command object
|
||||
* @param intent The intent to start
|
||||
* @param requestCode The request code that is passed to callback to identify the activity
|
||||
*/
|
||||
abstract public void startActivityForResult(IPlugin command, Intent intent, int requestCode);
|
||||
|
||||
/**
|
||||
* Set the plugin to be called when a sub-activity exits.
|
||||
*
|
||||
* @param plugin The plugin on which onActivityResult is to be called
|
||||
*/
|
||||
abstract public void setActivityResultCallback(IPlugin plugin);
|
||||
}
|
||||
180
framework/src/com/phonegap/api/Plugin.java
Executable file
180
framework/src/com/phonegap/api/Plugin.java
Executable file
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* Plugin interface must be implemented by any plugin classes.
|
||||
*
|
||||
* The execute method is called by the PluginManager.
|
||||
*/
|
||||
public abstract class Plugin implements IPlugin {
|
||||
|
||||
public String id;
|
||||
public WebView webView; // WebView object
|
||||
public PhonegapActivity ctx; // PhonegapActivity object
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public abstract PluginResult execute(String action, JSONArray args, String callbackId);
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the context of the Plugin. This can then be used to do things like
|
||||
* get file paths associated with the Activity.
|
||||
*
|
||||
* @param ctx The context of the main Activity.
|
||||
*/
|
||||
public void setContext(PhonegapActivity ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the main View of the application, this is the WebView within which
|
||||
* a PhoneGap app runs.
|
||||
*
|
||||
* @param webView The PhoneGap WebView
|
||||
*/
|
||||
public void setView(WebView webView) {
|
||||
this.webView = webView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an activity you launched exits, giving you the requestCode you started it with,
|
||||
* the resultCode it returned, and any additional data from it.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param data An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Send generic JavaScript statement back to JavaScript.
|
||||
* success(...) and error(...) should be used instead where possible.
|
||||
*
|
||||
* @param statement
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
this.ctx.sendJavascript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript success callback for this plugin.
|
||||
*
|
||||
* This can be used if the execute code for the plugin is asynchronous meaning
|
||||
* that execute should return null and the callback from the async operation can
|
||||
* call success(...) or error(...)
|
||||
*
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(PluginResult pluginResult, String callbackId) {
|
||||
this.ctx.sendJavascript(pluginResult.toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(JSONObject message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(String message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript error callback for this plugin.
|
||||
*
|
||||
* @param pluginResult The result to return.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(PluginResult pluginResult, String callbackId) {
|
||||
this.ctx.sendJavascript(pluginResult.toErrorCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(JSONObject message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(String message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
|
||||
}
|
||||
}
|
||||
308
framework/src/com/phonegap/api/PluginManager.java
Executable file
308
framework/src/com/phonegap/api/PluginManager.java
Executable file
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* PluginManager is exposed to JavaScript in the PhoneGap WebView.
|
||||
*
|
||||
* Calling native plugin code can be done by calling PluginManager.exec(...)
|
||||
* from JavaScript.
|
||||
*/
|
||||
public final class PluginManager {
|
||||
|
||||
private HashMap<String, IPlugin> plugins = new HashMap<String,IPlugin>();
|
||||
private HashMap<String, String> services = new HashMap<String,String>();
|
||||
|
||||
private final PhonegapActivity ctx;
|
||||
private final WebView app;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param app
|
||||
* @param ctx
|
||||
*/
|
||||
public PluginManager(WebView app, PhonegapActivity ctx) {
|
||||
this.ctx = ctx;
|
||||
this.app = app;
|
||||
this.loadPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugins from res/xml/plugins.xml
|
||||
*/
|
||||
public void loadPlugins() {
|
||||
int id = ctx.getResources().getIdentifier("plugins", "xml", ctx.getPackageName());
|
||||
if (id == 0) { pluginConfigurationMissing(); }
|
||||
XmlResourceParser xml = ctx.getResources().getXml(id);
|
||||
int eventType = -1;
|
||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||
if (eventType == XmlResourceParser.START_TAG) {
|
||||
String strNode = xml.getName();
|
||||
if (strNode.equals("plugin")) {
|
||||
String name = xml.getAttributeValue(null, "name");
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
//System.out.println("Plugin: "+name+" => "+value);
|
||||
this.addService(name, value);
|
||||
}
|
||||
}
|
||||
try {
|
||||
eventType = xml.next();
|
||||
} catch (XmlPullParserException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Receives a request for execution and fulfills it by finding the appropriate
|
||||
* Java class and calling it's execute method.
|
||||
*
|
||||
* PluginManager.exec can be used either synchronously or async. In either case, a JSON encoded
|
||||
* string is returned that will indicate if any errors have occurred when trying to find
|
||||
* or execute the class denoted by the clazz argument.
|
||||
*
|
||||
* @param service String containing the service to run
|
||||
* @param action String containt the action that the class is supposed to perform. This is
|
||||
* passed to the plugin execute method and it is up to the plugin developer
|
||||
* how to deal with it.
|
||||
* @param callbackId String containing the id of the callback that is execute in JavaScript if
|
||||
* this is an async plugin call.
|
||||
* @param args An Array literal string containing any arguments needed in the
|
||||
* plugin execute method.
|
||||
* @param async Boolean indicating whether the calling JavaScript code is expecting an
|
||||
* immediate return value. If true, either PhoneGap.callbackSuccess(...) or
|
||||
* PhoneGap.callbackError(...) is called once the plugin code has executed.
|
||||
*
|
||||
* @return JSON encoded string with a response message and status.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public String exec(final String service, final String action, final String callbackId, final String jsonArgs, final boolean async) {
|
||||
PluginResult cr = null;
|
||||
boolean runAsync = async;
|
||||
try {
|
||||
final JSONArray args = new JSONArray(jsonArgs);
|
||||
String clazz = this.services.get(service);
|
||||
Class c = null;
|
||||
if (clazz != null) {
|
||||
c = getClassByName(clazz);
|
||||
}
|
||||
if (isPhoneGapPlugin(c)) {
|
||||
final IPlugin plugin = this.addPlugin(clazz, c);
|
||||
final PhonegapActivity ctx = this.ctx;
|
||||
runAsync = async && !plugin.isSynch(action);
|
||||
if (runAsync) {
|
||||
// Run this on a different thread so that this one can return back to JS
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
// Call execute on the plugin so that it can do it's thing
|
||||
PluginResult cr = plugin.execute(action, args, callbackId);
|
||||
int status = cr.getStatus();
|
||||
|
||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||
if ((status == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
||||
}
|
||||
|
||||
// Check the success (OK, NO_RESULT & !KEEP_CALLBACK)
|
||||
else if ((status == PluginResult.Status.OK.ordinal()) || (status == PluginResult.Status.NO_RESULT.ordinal())) {
|
||||
ctx.sendJavascript(cr.toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
// If error
|
||||
else {
|
||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
return "";
|
||||
} else {
|
||||
// Call execute on the plugin so that it can do it's thing
|
||||
cr = plugin.execute(action, args, callbackId);
|
||||
|
||||
// If no result to be sent and keeping callback, then no need to sent back to JavaScript
|
||||
if ((cr.getStatus() == PluginResult.Status.NO_RESULT.ordinal()) && cr.getKeepCallback()) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
|
||||
} catch (JSONException e) {
|
||||
System.out.println("ERROR: "+e.toString());
|
||||
cr = new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
// if async we have already returned at this point unless there was an error...
|
||||
if (runAsync) {
|
||||
if (cr == null) {
|
||||
cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION);
|
||||
}
|
||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
return ( cr != null ? cr.getJSONString() : "{ status: 0, message: 'all good' }" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class.
|
||||
*
|
||||
* @param clazz
|
||||
* @return
|
||||
* @throws ClassNotFoundException
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class getClassByName(final String clazz) throws ClassNotFoundException {
|
||||
return Class.forName(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the interfaces that a class implements and see if it implements the
|
||||
* com.phonegap.api.Plugin interface.
|
||||
*
|
||||
* @param c The class to check the interfaces of.
|
||||
* @return Boolean indicating if the class implements com.phonegap.api.Plugin
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private boolean isPhoneGapPlugin(Class c) {
|
||||
if (c != null) {
|
||||
return com.phonegap.api.Plugin.class.isAssignableFrom(c) || com.phonegap.api.IPlugin.class.isAssignableFrom(c);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plugin to be loaded and cached. This creates an instance of the plugin.
|
||||
* If plugin is already created, then just return it.
|
||||
*
|
||||
* @param className The class to load
|
||||
* @param clazz The class object (must be a class object of the className)
|
||||
* @param callbackId The callback id to use when calling back into JavaScript
|
||||
* @return The plugin
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private IPlugin addPlugin(String className, Class clazz) {
|
||||
if (this.plugins.containsKey(className)) {
|
||||
return this.getPlugin(className);
|
||||
}
|
||||
try {
|
||||
IPlugin plugin = (IPlugin)clazz.newInstance();
|
||||
this.plugins.put(className, plugin);
|
||||
plugin.setContext(this.ctx);
|
||||
plugin.setView(this.app);
|
||||
return plugin;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Error adding plugin "+className+".");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the loaded plugin.
|
||||
*
|
||||
* @param className The class of the loaded plugin.
|
||||
* @return
|
||||
*/
|
||||
private IPlugin getPlugin(String className) {
|
||||
IPlugin plugin = this.plugins.get(className);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
* This does not create the class instance. It just maps service name to class name.
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
public void addService(String serviceType, String className) {
|
||||
this.services.put(serviceType, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause(boolean multitasking) {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onPause(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume(boolean multitasking) {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onResume(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void pluginConfigurationMissing() {
|
||||
System.err.println("=====================================================================================");
|
||||
System.err.println("ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
||||
System.err.println("https://raw.github.com/phonegap/phonegap-android/master/framework/res/xml/plugins.xml");
|
||||
System.err.println("=====================================================================================");
|
||||
}
|
||||
}
|
||||
129
framework/src/com/phonegap/api/PluginResult.java
Executable file
129
framework/src/com/phonegap/api/PluginResult.java
Executable file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class PluginResult {
|
||||
private final int status;
|
||||
private final String message;
|
||||
private boolean keepCallback = false;
|
||||
private String cast = null;
|
||||
|
||||
public PluginResult(Status status) {
|
||||
this.status = status.ordinal();
|
||||
this.message = "'" + PluginResult.StatusMessages[this.status] + "'";
|
||||
}
|
||||
|
||||
public PluginResult(Status status, String message) {
|
||||
this.status = status.ordinal();
|
||||
this.message = JSONObject.quote(message);
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONArray message, String cast) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message.toString();
|
||||
this.cast = cast;
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONObject message, String cast) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message.toString();
|
||||
this.cast = cast;
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONArray message) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message.toString();
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONObject message) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message.toString();
|
||||
}
|
||||
|
||||
public PluginResult(Status status, int i) {
|
||||
this.status = status.ordinal();
|
||||
this.message = ""+i;
|
||||
}
|
||||
|
||||
public PluginResult(Status status, float f) {
|
||||
this.status = status.ordinal();
|
||||
this.message = ""+f;
|
||||
}
|
||||
|
||||
public PluginResult(Status status, boolean b) {
|
||||
this.status = status.ordinal();
|
||||
this.message = ""+b;
|
||||
}
|
||||
|
||||
public void setKeepCallback(boolean b) {
|
||||
this.keepCallback = b;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public boolean getKeepCallback() {
|
||||
return this.keepCallback;
|
||||
}
|
||||
|
||||
public String getJSONString() {
|
||||
return "{status:" + this.status + ",message:" + this.message + ",keepCallback:" + this.keepCallback + "}";
|
||||
}
|
||||
|
||||
public String toSuccessCallbackString(String callbackId) {
|
||||
StringBuffer buf = new StringBuffer("");
|
||||
if (cast != null) {
|
||||
buf.append("var temp = "+cast+"("+this.getJSONString() + ");\n");
|
||||
buf.append("PhoneGap.callbackSuccess('"+callbackId+"',temp);");
|
||||
}
|
||||
else {
|
||||
buf.append("PhoneGap.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");");
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public String toErrorCallbackString(String callbackId) {
|
||||
return "PhoneGap.callbackError('"+callbackId+"', " + this.getJSONString()+ ");";
|
||||
}
|
||||
|
||||
public static String[] StatusMessages = new String[] {
|
||||
"No result",
|
||||
"OK",
|
||||
"Class not found",
|
||||
"Illegal access",
|
||||
"Instantiation error",
|
||||
"Malformed url",
|
||||
"IO error",
|
||||
"Invalid action",
|
||||
"JSON error",
|
||||
"Error"
|
||||
};
|
||||
|
||||
public enum Status {
|
||||
NO_RESULT,
|
||||
OK,
|
||||
CLASS_NOT_FOUND_EXCEPTION,
|
||||
ILLEGAL_ACCESS_EXCEPTION,
|
||||
INSTANTIATION_EXCEPTION,
|
||||
MALFORMED_URL_EXCEPTION,
|
||||
IO_EXCEPTION,
|
||||
INVALID_ACTION,
|
||||
JSON_EXCEPTION,
|
||||
ERROR
|
||||
}
|
||||
}
|
||||
9
framework/src/com/phonegap/file/EncodingException.java
Normal file
9
framework/src/com/phonegap/file/EncodingException.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class EncodingException extends Exception {
|
||||
|
||||
public EncodingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
9
framework/src/com/phonegap/file/FileExistsException.java
Normal file
9
framework/src/com/phonegap/file/FileExistsException.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class FileExistsException extends Exception {
|
||||
|
||||
public FileExistsException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class InvalidModificationException extends Exception {
|
||||
|
||||
public InvalidModificationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class NoModificationAllowedException extends Exception {
|
||||
|
||||
public NoModificationAllowedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class TypeMismatchException extends Exception {
|
||||
|
||||
public TypeMismatchException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
176
lib/classic.rb
Executable file
176
lib/classic.rb
Executable file
@@ -0,0 +1,176 @@
|
||||
class Classic
|
||||
attr_reader :android_sdk_path, :name, :pkg, :www, :path
|
||||
|
||||
def initialize(a)
|
||||
@android_sdk_path, @name, @pkg, @www, @path = a
|
||||
build
|
||||
end
|
||||
|
||||
def build
|
||||
setup
|
||||
clobber
|
||||
build_jar
|
||||
create_android
|
||||
include_www
|
||||
generate_manifest
|
||||
copy_libs
|
||||
add_name_to_strings
|
||||
write_java
|
||||
end
|
||||
|
||||
def setup
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__).gsub(/lib$/,''))
|
||||
@framework_dir = File.join(@android_dir, "framework")
|
||||
@icon = File.join(@www, 'icon.png') unless !@icon.nil? && File.exists?(@icon)
|
||||
# Hash that stores the location of icons for each resolution type. Uses the default icon for all resolutions as a baseline.
|
||||
@icons = {
|
||||
:"drawable-ldpi" => @icon,
|
||||
:"drawable-mdpi" => @icon,
|
||||
:"drawable-hdpi" => @icon
|
||||
} if @icons.nil?
|
||||
@app_js_dir = ''
|
||||
@content = 'index.html'
|
||||
end
|
||||
|
||||
# replaces @path with new android project
|
||||
def clobber
|
||||
FileUtils.rm_r(@path) if File.exists? @path
|
||||
FileUtils.mkdir_p @path
|
||||
end
|
||||
|
||||
# removes local.properties and recreates based on android_sdk_path
|
||||
# then generates framework/phonegap.jar
|
||||
def build_jar
|
||||
%w(local.properties phonegap.js phonegap.jar).each do |f|
|
||||
FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f)
|
||||
end
|
||||
open(File.join(@framework_dir, "local.properties"), 'w') do |f|
|
||||
f.puts "sdk.dir=#{ @android_sdk_path }"
|
||||
end
|
||||
Dir.chdir(@framework_dir)
|
||||
`ant jar`
|
||||
Dir.chdir(@android_dir)
|
||||
end
|
||||
|
||||
# runs android create project
|
||||
# TODO need to allow more flexible SDK targetting via config.xml
|
||||
def create_android
|
||||
IO.popen("android list targets") { |f|
|
||||
targets = f.readlines(nil)[0].scan(/id\:.*$/)
|
||||
if (targets.length > 0)
|
||||
target_id = targets.last.match(/\d+/).to_a.first
|
||||
`android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }`
|
||||
else
|
||||
puts "No Android targets found. Please run 'android' and install at least one SDK package."
|
||||
puts "If that makes no sense then you need to go read the Android SDK documentation."
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# copies the project/www folder into tmp/android/www
|
||||
def include_www
|
||||
FileUtils.mkdir_p File.join(@path, "assets", "www")
|
||||
FileUtils.cp_r File.join(@www, "."), File.join(@path, "assets", "www")
|
||||
end
|
||||
|
||||
# creates an AndroidManifest.xml for the project
|
||||
def generate_manifest
|
||||
manifest = ""
|
||||
open(File.join(@framework_dir, "AndroidManifest.xml"), 'r') do |old|
|
||||
manifest = old.read
|
||||
manifest.gsub! 'android:versionCode="5"', 'android:versionCode="1"'
|
||||
manifest.gsub! 'package="com.phonegap"', "package=\"#{ @pkg }\""
|
||||
manifest.gsub! 'android:name=".StandAlone"', "android:name=\".#{ @name.gsub(' ','') }\""
|
||||
manifest.gsub! 'android:minSdkVersion="5"', 'android:minSdkVersion="3"'
|
||||
end
|
||||
open(File.join(@path, "AndroidManifest.xml"), 'w') { |x| x.puts manifest }
|
||||
end
|
||||
|
||||
# copies stuff from src directory into the android project directory (@path)
|
||||
def copy_libs
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION')).lstrip.rstrip
|
||||
framework_res_dir = File.join(@framework_dir, "res")
|
||||
app_res_dir = File.join(@path, "res")
|
||||
# copies in the jar
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap-#{ version }.jar"), File.join(@path, "libs")
|
||||
# copies in the strings.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "values")
|
||||
FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml")
|
||||
# copies in plugins.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "xml")
|
||||
FileUtils.cp File.join(framework_res_dir, "xml","plugins.xml"), File.join(app_res_dir, "xml", "plugins.xml")
|
||||
# drops in the layout files: main.xml and preview.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "layout")
|
||||
%w(main.xml).each do |f|
|
||||
FileUtils.cp File.join(framework_res_dir, "layout", f), File.join(app_res_dir, "layout", f)
|
||||
end
|
||||
# icon file copy
|
||||
%w(drawable-hdpi drawable-ldpi drawable-mdpi).each do |e|
|
||||
# if specific resolution icons are specified, use those. if not, see if a general purpose icon was defined.
|
||||
# finally, fall back to using the default PhoneGap one.
|
||||
currentIcon = ""
|
||||
if !@icons[e.to_sym].nil? && File.exists?(File.join(@www, @icons[e.to_sym]))
|
||||
currentIcon = File.join(@www, @icons[e.to_sym])
|
||||
elsif File.exists?(@icon)
|
||||
currentIcon = @icon
|
||||
else
|
||||
currentIcon = File.join(framework_res_dir, "drawable", "icon.png")
|
||||
end
|
||||
FileUtils.mkdir_p(File.join(app_res_dir, e))
|
||||
FileUtils.cp(currentIcon, File.join(app_res_dir, e, "icon.png"))
|
||||
end
|
||||
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
|
||||
js_dir = File.join(@framework_dir, "assets", "js")
|
||||
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
|
||||
Dir.new(js_dir).entries.each do |script|
|
||||
next if script[0].chr == "." or script == "phonegap.js.base"
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
|
||||
# puts app name in strings
|
||||
def add_name_to_strings
|
||||
x = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
<resources>
|
||||
<string name=\"app_name\">#{ @name }</string>
|
||||
<string name=\"go\">Snap</string>
|
||||
</resources>
|
||||
"
|
||||
open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f|
|
||||
f.puts x.gsub(' ','')
|
||||
end
|
||||
end
|
||||
|
||||
# create java source file
|
||||
def write_java
|
||||
j = "
|
||||
package #{ @pkg };
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import com.phonegap.*;
|
||||
|
||||
public class #{ @name.gsub(' ','') } extends DroidGap
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
super.loadUrl(\"file:///android_asset/www/#{ @content }\");
|
||||
}
|
||||
}
|
||||
"
|
||||
code_dir = File.join(@path, "src", @pkg.gsub('.', File::SEPARATOR))
|
||||
FileUtils.mkdir_p(code_dir)
|
||||
open(File.join(code_dir, "#{ @name }.java"),'w') { |f| f.puts j }
|
||||
end
|
||||
|
||||
# friendly output for now
|
||||
def msg
|
||||
puts "Created #{ @path }"
|
||||
end
|
||||
#
|
||||
end
|
||||
97
lib/create.rb
Normal file
97
lib/create.rb
Normal file
@@ -0,0 +1,97 @@
|
||||
# Create
|
||||
#
|
||||
# Generates an Android project from a valid WWW directory and puts it in ../[PROJECT NAME]_android
|
||||
#
|
||||
class Create < Classic
|
||||
def initialize(path)
|
||||
guess_paths(path)
|
||||
read_config
|
||||
build
|
||||
end
|
||||
|
||||
def guess_paths(path)
|
||||
# if no path is supplied uses current directory for project
|
||||
path = FileUtils.pwd if path.nil?
|
||||
|
||||
# if a www is found use it for the project
|
||||
path = File.join(path, 'www') if File.exists? File.join(path, 'www')
|
||||
|
||||
# defaults
|
||||
@name = path.split("/").last.gsub('-','').gsub(' ','') # no dashses nor spaces
|
||||
@path = File.join(path, '..', "#{ @name }_android")
|
||||
@www = path
|
||||
@pkg = "com.phonegap.#{ @name }"
|
||||
@android_sdk_path = Dir.getwd[0,1] != "/" ? `android-sdk-path.bat android.bat`.gsub('\\tools','').gsub('\\', '\\\\\\\\') : `which android`.gsub(/\/tools\/android$/,'').chomp
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__).gsub('lib',''))
|
||||
@framework_dir = File.join(@android_dir, "framework")
|
||||
@icon = File.join(@www, 'icon.png')
|
||||
@app_js_dir = ''
|
||||
@content = 'index.html'
|
||||
|
||||
# stop executation on errors
|
||||
raise "Expected index.html in the following folder #{ path }.\nThe path is expected to be the directory droidgap create is run from or specified as a command line arg like droidgap create my_path." unless File.exists? File.join(path, 'index.html')
|
||||
raise 'Could not find android in your PATH!' if @android_sdk_path.empty?
|
||||
end
|
||||
|
||||
# reads in a config.xml file
|
||||
def read_config
|
||||
config_file = File.join(@www, 'config.xml')
|
||||
|
||||
if File.exists?(config_file)
|
||||
require 'rexml/document'
|
||||
f = File.new config_file
|
||||
doc = REXML::Document.new(f)
|
||||
@config = {}
|
||||
@config[:id] = doc.root.attributes["id"]
|
||||
@config[:version] = doc.root.attributes["version"]
|
||||
@config[:icons] = {}
|
||||
defaultIconSize = 0
|
||||
doc.root.elements.each do |n|
|
||||
@config[:name] = n.text.gsub('-','').gsub(' ','') if n.name == 'name'
|
||||
@config[:description] = n.text if n.name == 'description'
|
||||
@config[:content] = n.attributes["src"] if n.name == 'content'
|
||||
if n.name == 'icon'
|
||||
if n.attributes["width"] == '72' && n.attributes["height"] == '72'
|
||||
@config[:icons]["drawable-hdpi".to_sym] = n.attributes["src"]
|
||||
if 72 > defaultIconSize
|
||||
@config[:icon] = n.attributes["src"]
|
||||
defaultIconSize = 72
|
||||
end
|
||||
elsif n.attributes["width"] == '48' && n.attributes["height"] == '48'
|
||||
@config[:icons]["drawable-mdpi".to_sym] = n.attributes["src"]
|
||||
if 48 > defaultIconSize
|
||||
@config[:icon] = n.attributes["src"]
|
||||
defaultIconSize = 48
|
||||
end
|
||||
elsif n.attributes["width"] == '36' && n.attributes["height"] == '36'
|
||||
@config[:icons]["drawable-ldpi".to_sym] = n.attributes["src"]
|
||||
if 36 > defaultIconSize
|
||||
@config[:icon] = n.attributes["src"]
|
||||
defaultIconSize = 36
|
||||
end
|
||||
else
|
||||
@config[:icon] = n.attributes["src"]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if n.name == "preference" && n.attributes["name"] == 'javascript_folder'
|
||||
@config[:js_dir] = n.attributes["value"]
|
||||
end
|
||||
end
|
||||
|
||||
# extract android specific stuff
|
||||
@config[:versionCode] = doc.elements["//android:versionCode"] ? doc.elements["//android:versionCode"].text : 3
|
||||
@config[:minSdkVersion] = doc.elements["//android:minSdkVersion"] ? doc.elements["//android:minSdkVersion"].text : 1
|
||||
# will change the name from the directory to the name element text
|
||||
@name = @config[:name] if @config[:name]
|
||||
# set the icon from the config
|
||||
@icon = File.join(@www, @config[:icon]) if @config[:icon]
|
||||
@icons = @config[:icons] if @config[:icons].length > 0
|
||||
# sets the app js dir where phonegap.js gets copied
|
||||
@app_js_dir = @config[:js_dir] ? @config[:js_dir] : ''
|
||||
# sets the start page
|
||||
@content = @config[:content] ? @config[:content] : 'index.html'
|
||||
end
|
||||
end
|
||||
end
|
||||
15
lib/generate.rb
Normal file
15
lib/generate.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# Creates a new PhoneGap/Android project from ./example
|
||||
class Generate
|
||||
def initialize(name)
|
||||
if name.nil?
|
||||
puts "You need to supply a name to generate a project. Try this:\n\ndroidgap gen MyApp\n\n"
|
||||
return
|
||||
end
|
||||
from = File.join ROOT, "example"
|
||||
to = File.join FileUtils.pwd, name
|
||||
FileUtils.cp_r from, to
|
||||
Create.new(to)
|
||||
FileUtils.rm_rf to
|
||||
FileUtils.mv "#{ to }_android", to
|
||||
end
|
||||
end
|
||||
BIN
lib/jruby-complete-1.4.0RC1.jar
Normal file
BIN
lib/jruby-complete-1.4.0RC1.jar
Normal file
Binary file not shown.
68
lib/run.rb
Normal file
68
lib/run.rb
Normal file
@@ -0,0 +1,68 @@
|
||||
#
|
||||
# Run
|
||||
# ---
|
||||
#
|
||||
# A handy machine that does the following:
|
||||
#
|
||||
# - runs ant_debug
|
||||
# - if there is no device attached it will start an emulator with the first avd found
|
||||
# - runs ant_install
|
||||
#
|
||||
class Run
|
||||
# if no path is supplied uses current directory for project
|
||||
def initialize
|
||||
@path = FileUtils.pwd
|
||||
build
|
||||
start_emulator if first_device.nil?
|
||||
install
|
||||
end
|
||||
|
||||
def build
|
||||
Dir.chdir(@path)
|
||||
`ant debug`
|
||||
end
|
||||
|
||||
def install
|
||||
Dir.chdir(@path)
|
||||
`ant install`
|
||||
end
|
||||
|
||||
def start_emulator
|
||||
puts "No devices attached. Starting emulator w/ first avd...\n"
|
||||
$stdout.sync = true
|
||||
avd = first_avd
|
||||
if (avd.nil? || avd == "")
|
||||
puts "No Android Virtual Device (AVD) could be found. Please create one with the Android SDK."
|
||||
return
|
||||
end
|
||||
IO.popen("emulator -avd #{ avd } -logcat all") do |f|
|
||||
until f.eof?
|
||||
puts f.gets
|
||||
if f.gets.include? 'Boot is finished'
|
||||
#IO.popen("cd #{ @pkg.path }; ant install;") do |f|
|
||||
# puts f.gets
|
||||
#end
|
||||
puts "\n\nEMULATOR IS NOW RUNNING!\n\n"
|
||||
puts "install your app by running: "
|
||||
puts "cd #{ @pkg.path }; ant install;"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# helpers
|
||||
def first_device
|
||||
fd = `adb devices`.split("\n").pop()
|
||||
if fd == 'List of devices attached '
|
||||
nil
|
||||
else
|
||||
fd.gsub('device','')
|
||||
end
|
||||
end
|
||||
|
||||
def first_avd
|
||||
`android list avd | grep "Name: "`.gsub('Name: ','').strip
|
||||
end
|
||||
|
||||
#
|
||||
end
|
||||
6
lib/test.rb
Normal file
6
lib/test.rb
Normal file
@@ -0,0 +1,6 @@
|
||||
# this is for implementors mostly
|
||||
class Test
|
||||
def initialize
|
||||
`git clone git@github.com:phonegap/mobile-spec.git && cd mobile-spec && droidgap create && cd ../mobilespec_android && ant debug install`
|
||||
end
|
||||
end
|
||||
51
lib/update.rb
Executable file
51
lib/update.rb
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env ruby
|
||||
require 'fileutils'
|
||||
|
||||
class Update
|
||||
attr_reader :android_sdk_path, :path
|
||||
|
||||
def initialize
|
||||
@path = FileUtils.pwd
|
||||
@android_sdk_path = Dir.getwd[0,1] != "/" ? `android-sdk-path.bat android.bat`.gsub('\\tools','').gsub('\\', '\\\\\\\\') : `which android`.gsub('/tools/android','')
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__))
|
||||
@framework_dir = File.join(@android_dir, "..", "framework")
|
||||
# puts "updating #{ @path } with phonegap from #{ @android_dir }"
|
||||
build_jar
|
||||
copy_libs
|
||||
end
|
||||
|
||||
# removes local.properties and recreates based on android_sdk_path
|
||||
# then generates framework/phonegap.jar & framework/assets/www/phonegap.js
|
||||
def build_jar
|
||||
puts "Building the JAR and combining JS files..."
|
||||
%w(local.properties phonegap.js phonegap.jar).each do |f|
|
||||
FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f)
|
||||
end
|
||||
open(File.join(@framework_dir, "local.properties"), 'w') do |f|
|
||||
f.puts "sdk.dir=#{ @android_sdk_path }"
|
||||
end
|
||||
Dir.chdir(@framework_dir)
|
||||
`ant jar`
|
||||
Dir.chdir(@android_dir)
|
||||
end
|
||||
|
||||
# copies stuff from framework into the project
|
||||
# TODO need to allow for www import inc icon
|
||||
def copy_libs
|
||||
puts "Copying over libraries and assets..."
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION'))
|
||||
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap-#{ version }.jar"), File.join(@path, "libs")
|
||||
|
||||
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
|
||||
js_dir = File.join(@framework_dir, "assets", "js")
|
||||
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
|
||||
Dir.new(js_dir).entries.each do |script|
|
||||
next if script[0].chr == "." or script == "phonegap.js.base"
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
#
|
||||
end
|
||||
BIN
util/js.jar
Executable file
BIN
util/js.jar
Executable file
Binary file not shown.
5544
util/jslint.js
Executable file
5544
util/jslint.js
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user