mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
259 Commits
accel_hack
...
0.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 |
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,6 +1,7 @@
|
||||
default.properties
|
||||
bin
|
||||
gen
|
||||
assets/www/phonegap.js
|
||||
local.properties
|
||||
framework/phonegap.jar
|
||||
framework/phonegap.jar
|
||||
framework/bin
|
||||
framework/assets/www/.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 an example PhoneGap application to current directory.
|
||||
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 example
|
||||
$ cd example
|
||||
$ 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
|
||||
171
bin/droidgap
Executable file
171
bin/droidgap
Executable file
@@ -0,0 +1,171 @@
|
||||
#!/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 an example PhoneGap application to current directory.
|
||||
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 example
|
||||
$ cd example
|
||||
$ ant debug install && adb logcat
|
||||
|
||||
EOF
|
||||
|
||||
gen = <<-EOF
|
||||
|
||||
DroidGap Generate
|
||||
-----------------
|
||||
|
||||
Generate an example PhoneGap application to path supplied or current working directory if none is supplied.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap gen [path]
|
||||
|
||||
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
|
||||
@@ -12,8 +12,6 @@
|
||||
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() {
|
||||
@@ -103,7 +101,11 @@
|
||||
|
||||
function get_contacts()
|
||||
{
|
||||
navigator.ContactManager.getAllContacts(count_contacts, fail, null);
|
||||
var obj = new ContactFindOptions();
|
||||
obj.filter="";
|
||||
obj.multiple=true;
|
||||
obj.limit=5;
|
||||
navigator.service.contacts.find(["displayName", "phoneNumbers", "emails"], count_contacts, fail, obj);
|
||||
}
|
||||
|
||||
function count_contacts(contacts)
|
||||
@@ -113,10 +115,8 @@
|
||||
|
||||
function init(){
|
||||
document.addEventListener("touchmove", preventBehavior, false);
|
||||
document.addEventListener("deviceReady", deviceInfo, true);
|
||||
}
|
||||
|
||||
|
||||
document.addEventListener("deviceready", deviceInfo, true);
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
1188
example/phonegap.js
1188
example/phonegap.js
File diff suppressed because it is too large
Load Diff
@@ -32,11 +32,6 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.phonegap.CameraPreview"
|
||||
android:label="@string/app_name" android:screenOrientation="landscape"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<action android:name="android.intent.action.PICK" />
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
|
||||
|
||||
157
framework/assets/js/accelerometer.js
Normal file → Executable file
157
framework/assets/js/accelerometer.js
Normal file → Executable file
@@ -1,101 +1,118 @@
|
||||
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, IBM Corporation
|
||||
*/
|
||||
|
||||
function Acceleration(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
102
framework/assets/js/camera.js
Normal file → Executable file
102
framework/assets/js/camera.js
Normal file → Executable file
@@ -1,40 +1,90 @@
|
||||
/**
|
||||
* 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, IBM Corporation
|
||||
*/
|
||||
function Camera() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* This class provides access to the device camera.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
}
|
||||
PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType]);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
|
||||
|
||||
141
framework/assets/js/compass.js
Normal file → Executable file
141
framework/assets/js/compass.js
Normal file → Executable file
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class provides access to device Compass data.
|
||||
* @constructor
|
||||
@@ -6,88 +14,97 @@ function Compass() {
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
314
framework/assets/js/contact.js
Normal file → Executable file
314
framework/assets/js/contact.js
Normal file → Executable file
@@ -1,81 +1,253 @@
|
||||
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, IBM Corporation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains information about a single contact.
|
||||
* @param {DOMString} id unique identifier
|
||||
* @param {DOMString} displayName
|
||||
* @param {ContactName} name
|
||||
* @param {DOMString} nickname
|
||||
* @param {ContactField[]} phoneNumbers array of phone numbers
|
||||
* @param {ContactField[]} emails array of email addresses
|
||||
* @param {ContactAddress[]} addresses array of addresses
|
||||
* @param {ContactField[]} ims instant messaging user ids
|
||||
* @param {ContactOrganization[]} organizations
|
||||
* @param {DOMString} published date contact was first created
|
||||
* @param {DOMString} updated date contact was last updated
|
||||
* @param {DOMString} birthday contact's birthday
|
||||
* @param (DOMString} anniversary contact's anniversary
|
||||
* @param {DOMString} gender contact's gender
|
||||
* @param {DOMString} note user notes about contact
|
||||
* @param {DOMString} preferredUsername
|
||||
* @param {ContactField[]} photos
|
||||
* @param {ContactField[]} tags
|
||||
* @param {ContactField[]} relationships
|
||||
* @param {ContactField[]} urls contact's web sites
|
||||
* @param {ContactAccounts[]} accounts contact's online accounts
|
||||
* @param {DOMString} utcOffset UTC time zone offset
|
||||
* @param {DOMString} connected
|
||||
*/
|
||||
var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, addresses,
|
||||
ims, organizations, published, updated, birthday, anniversary, gender, note,
|
||||
preferredUsername, photos, tags, relationships, urls, accounts, utcOffset, connected) {
|
||||
this.id = id || 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.published = published || null;
|
||||
this.updated = updated || null;
|
||||
this.birthday = birthday || null;
|
||||
this.anniversary = anniversary || null;
|
||||
this.gender = gender || null;
|
||||
this.note = note || null;
|
||||
this.preferredUsername = preferredUsername || null;
|
||||
this.photos = photos || null; // ContactField[]
|
||||
this.tags = tags || null; // ContactField[]
|
||||
this.relationships = relationships || null; // ContactField[]
|
||||
this.urls = urls || null; // ContactField[]
|
||||
this.accounts = accounts || null; // ContactAccount[]
|
||||
this.utcOffset = utcOffset || null;
|
||||
this.connected = connected || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.NOT_FOUND_ERROR;
|
||||
errorCB(errorObj);
|
||||
}
|
||||
|
||||
PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
clonedContact.id = null;
|
||||
return clonedContact;
|
||||
};
|
||||
|
||||
/**
|
||||
* Persists contact to device storage.
|
||||
* @param successCB success callback
|
||||
* @param errorCB error callback
|
||||
*/
|
||||
Contact.prototype.save = function(successCB, errorCB) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Contact name.
|
||||
* @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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic contact field.
|
||||
* @param type
|
||||
* @param value
|
||||
* @param primary
|
||||
*/
|
||||
var ContactField = function(type, value, primary) {
|
||||
this.type = type || null;
|
||||
this.value = value || null;
|
||||
this.primary = primary || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contact address.
|
||||
* @param formatted
|
||||
* @param streetAddress
|
||||
* @param locality
|
||||
* @param region
|
||||
* @param postalCode
|
||||
* @param country
|
||||
*/
|
||||
var ContactAddress = function(formatted, streetAddress, locality, region, postalCode, country) {
|
||||
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.
|
||||
* @param name
|
||||
* @param dept
|
||||
* @param title
|
||||
* @param startDate
|
||||
* @param endDate
|
||||
* @param location
|
||||
* @param desc
|
||||
*/
|
||||
var ContactOrganization = function(name, dept, title, startDate, endDate, location, desc) {
|
||||
this.name = name || null;
|
||||
this.department = dept || null;
|
||||
this.title = title || null;
|
||||
this.startDate = startDate || null;
|
||||
this.endDate = endDate || null;
|
||||
this.location = location || null;
|
||||
this.description = desc || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Contact account.
|
||||
* @param domain
|
||||
* @param username
|
||||
* @param userid
|
||||
*/
|
||||
var ContactAccount = function(domain, username, userid) {
|
||||
this.domain = domain || null;
|
||||
this.username = username || null;
|
||||
this.userid = userid || null;
|
||||
}
|
||||
|
||||
var ContactName = function()
|
||||
{
|
||||
this.formatted = "";
|
||||
this.familyName = "";
|
||||
this.givenName = "";
|
||||
this.additionalNames = [];
|
||||
this.prefixes = [];
|
||||
this.suffixes = [];
|
||||
/**
|
||||
* Represents a group of Contacts.
|
||||
*/
|
||||
var Contacts = function() {
|
||||
this.inProgress = false;
|
||||
this.records = new Array();
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
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 contact = new Contact();
|
||||
for (i in properties) {
|
||||
if (contact[i]!='undefined') {
|
||||
contact[i]=properties[i];
|
||||
}
|
||||
}
|
||||
return contact;
|
||||
};
|
||||
|
||||
var ContactEmail = function()
|
||||
{
|
||||
this.types = [];
|
||||
this.address = "";
|
||||
}
|
||||
/**
|
||||
* ContactFindOptions.
|
||||
* @param filter used to match contacts against
|
||||
* @param multiple boolean used to determine if more than one contact should be returned
|
||||
* @param limit maximum number of results to return from the contacts search
|
||||
* @param updatedSince return only contact records that have been updated on or after the given time
|
||||
*/
|
||||
var ContactFindOptions = function(filter, multiple, limit, updatedSince) {
|
||||
this.filter = filter || '';
|
||||
this.multiple = multiple || false;
|
||||
this.limit = limit || 1;
|
||||
this.updatedSince = updatedSince || '';
|
||||
};
|
||||
|
||||
var ContactPhoneNumber = function()
|
||||
{
|
||||
this.types = [];
|
||||
this.number = "";
|
||||
}
|
||||
/**
|
||||
* ContactError.
|
||||
* An error code assigned by an implementation when an error has occurred
|
||||
*/
|
||||
var ContactError = function() {
|
||||
this.code=null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Error codes
|
||||
*/
|
||||
ContactError.UNKNOWN_ERROR = 0;
|
||||
ContactError.INVALID_ARGUMENT_ERROR = 1;
|
||||
ContactError.NOT_FOUND_ERROR = 2;
|
||||
ContactError.TIMEOUT_ERROR = 3;
|
||||
ContactError.PENDING_OPERATION_ERROR = 4;
|
||||
ContactError.IO_ERROR = 5;
|
||||
ContactError.NOT_SUPPORTED_ERROR = 6;
|
||||
ContactError.PERMISSION_DENIED_ERROR = 20;
|
||||
|
||||
var Contacts = function()
|
||||
{
|
||||
this.records = [];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
Contacts.prototype.droidDone = function()
|
||||
{
|
||||
this.win(this.records);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the contact interface into the browser.
|
||||
*/
|
||||
PhoneGap.addConstructor(function() {
|
||||
if(typeof navigator.contacts == "undefined") navigator.contacts = new Contacts();
|
||||
if(typeof navigator.service == "undefined") navigator.service = new Object();
|
||||
if(typeof navigator.service.contacts == "undefined") navigator.service.contacts = new Contacts();
|
||||
});
|
||||
|
||||
52
framework/assets/js/crypto.js
Normal file → Executable file
52
framework/assets/js/crypto.js
Normal file → Executable file
@@ -1,33 +1,35 @@
|
||||
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, 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;
|
||||
}
|
||||
var Crypto = function() {
|
||||
};
|
||||
|
||||
Crypto.prototype.gotCryptedString = function(string)
|
||||
{
|
||||
this.encryptWin(string);
|
||||
}
|
||||
Crypto.prototype.encrypt = function(seed, string, callback) {
|
||||
this.encryptWin = callback;
|
||||
PhoneGap.exec(null, null, "Crypto", "encrypt", [seed, string]);
|
||||
};
|
||||
|
||||
Crypto.prototype.getPlainString = function(string)
|
||||
{
|
||||
this.decryptWin(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();
|
||||
});
|
||||
|
||||
|
||||
99
framework/assets/js/device.js
Normal file → Executable file
99
framework/assets/js/device.js
Normal file → Executable file
@@ -1,31 +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
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
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.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", []);
|
||||
};
|
||||
|
||||
/*
|
||||
* This is only for Android.
|
||||
*
|
||||
* You must explicitly override the back button.
|
||||
*/
|
||||
Device.prototype.overrideBackButton = function() {
|
||||
BackButton.override();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is only for Android.
|
||||
*
|
||||
* This resets the back button to the default behaviour
|
||||
*/
|
||||
Device.prototype.resetBackButton = function() {
|
||||
BackButton.reset();
|
||||
}
|
||||
|
||||
/*
|
||||
* This is only for Android.
|
||||
*
|
||||
* This terminates the activity!
|
||||
*/
|
||||
Device.prototype.exitApp = function() {
|
||||
BackButton.exitApp();
|
||||
}
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
|
||||
751
framework/assets/js/file.js
Normal file → Executable file
751
framework/assets/js/file.js
Normal file → Executable file
@@ -1,226 +1,567 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class provides generic read and write access to the mobile device file system.
|
||||
* They are not used to read files from a server.
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of files
|
||||
*/
|
||||
function FileList() {
|
||||
this.files = {};
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() { if (typeof navigator.fileMgr == "undefined") navigator.fileMgr = new FileMgr();});
|
||||
/**
|
||||
* Describes a single file in a FileList
|
||||
*/
|
||||
function File() {
|
||||
this.name = null;
|
||||
this.type = null;
|
||||
this.urn = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an event object since we can't set target on DOM event.
|
||||
*
|
||||
* @param type
|
||||
* @param target
|
||||
*
|
||||
*/
|
||||
File._createEvent = function(type, target) {
|
||||
// Can't create event object, since we can't set target (its readonly)
|
||||
//var evt = document.createEvent('Events');
|
||||
//evt.initEvent("onload", false, false);
|
||||
var evt = {"type": type};
|
||||
evt.target = target;
|
||||
return evt;
|
||||
};
|
||||
|
||||
function FileError() {
|
||||
// File error codes
|
||||
// Found in DOMException
|
||||
this.NOT_FOUND_ERR = 1;
|
||||
this.SECURITY_ERR = 2;
|
||||
this.ABORT_ERR = 3;
|
||||
|
||||
// Added by this specification
|
||||
this.NOT_READABLE_ERR = 4;
|
||||
this.ENCODING_ERR = 5;
|
||||
this.NO_MODIFICATION_ALLOWED_ERR = 6;
|
||||
this.INVALID_STATE_ERR = 7;
|
||||
this.SYNTAX_ERR = 8;
|
||||
|
||||
this.code = null;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File manager
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function FileMgr() {
|
||||
};
|
||||
|
||||
FileMgr.prototype.getFileBasePaths = function() {
|
||||
};
|
||||
|
||||
FileMgr.prototype.testSaveLocationExists = function(successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "testSaveLocationExists", []);
|
||||
};
|
||||
|
||||
FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "testFileExists", [fileName]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "testDirectoryExists", [dirName]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.createDirectory = function(dirName, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "createDirectory", [dirName]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.deleteDirectory = function(dirName, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "deleteDirectory", [dirName]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.deleteFile = function(fileName, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "deleteFile", [fileName]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "getFreeDiskSpace", []);
|
||||
};
|
||||
|
||||
FileMgr.prototype.writeAsText = function(fileName, data, append, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "writeAsText", [fileName, data, append]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.truncate = function(fileName, size, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "truncate", [fileName, size]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.readAsText = function(fileName, encoding, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "readAsText", [fileName, encoding]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.readAsDataURL = function(fileName, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "readAsDataURL", [fileName]);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.fileMgr == "undefined") navigator.fileMgr = new FileMgr();
|
||||
});
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File Reader
|
||||
//-----------------------------------------------------------------------------
|
||||
// TODO: All other FileMgr function operate on the SD card as root. However,
|
||||
// for FileReader & FileWriter the root is not SD card. Should this be changed?
|
||||
|
||||
/**
|
||||
* This class reads the mobile device file system.
|
||||
*
|
||||
* For Android:
|
||||
* The root directory is the root of the file system.
|
||||
* To read from the SD card, the file name is "sdcard/my_file.txt"
|
||||
*/
|
||||
function FileReader() {
|
||||
this.fileName = "";
|
||||
|
||||
this.readyState = 0;
|
||||
|
||||
// File data
|
||||
this.result = null;
|
||||
|
||||
// Error
|
||||
this.error = null;
|
||||
|
||||
// Event handlers
|
||||
this.onloadstart = null; // When the read starts.
|
||||
this.onprogress = null; // While reading (and decoding) file or fileBlob data, and reporting partial file data (progess.loaded/progress.total)
|
||||
this.onload = null; // When the read has successfully completed.
|
||||
this.onerror = null; // When the read has failed (see errors).
|
||||
this.onloadend = null; // When the request has completed (either in success or failure).
|
||||
this.onabort = null; // When the read has been aborted. For instance, by invoking the abort() method.
|
||||
};
|
||||
|
||||
// States
|
||||
FileReader.EMPTY = 0;
|
||||
FileReader.LOADING = 1;
|
||||
FileReader.DONE = 2;
|
||||
|
||||
/**
|
||||
* Abort reading file.
|
||||
*/
|
||||
FileReader.prototype.abort = function() {
|
||||
this.readyState = FileReader.DONE;
|
||||
|
||||
// If abort callback
|
||||
if (typeof this.onabort == "function") {
|
||||
var evt = File._createEvent("abort", this);
|
||||
this.onabort(evt);
|
||||
}
|
||||
|
||||
// TODO: Anything else to do? Maybe sent to native?
|
||||
};
|
||||
|
||||
/**
|
||||
* Read text file.
|
||||
*
|
||||
* @param file The name of the file
|
||||
* @param encoding [Optional] (see http://www.iana.org/assignments/character-sets)
|
||||
*/
|
||||
FileReader.prototype.readAsText = function(file, encoding) {
|
||||
this.fileName = file;
|
||||
|
||||
// LOADING state
|
||||
this.readyState = FileReader.LOADING;
|
||||
|
||||
// If loadstart callback
|
||||
if (typeof this.onloadstart == "function") {
|
||||
var evt = File._createEvent("loadstart", this);
|
||||
this.onloadstart(evt);
|
||||
}
|
||||
|
||||
// Default encoding is UTF-8
|
||||
var enc = encoding ? encoding : "UTF-8";
|
||||
|
||||
var me = this;
|
||||
|
||||
// Read file
|
||||
navigator.fileMgr.readAsText(file, enc,
|
||||
|
||||
// Success callback
|
||||
function(r) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save result
|
||||
me.result = r;
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileReader.DONE;
|
||||
|
||||
// If onload callback
|
||||
if (typeof me.onload == "function") {
|
||||
var evt = File._createEvent("load", me);
|
||||
me.onload(evt);
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend == "function") {
|
||||
var evt = File._createEvent("loadend", me);
|
||||
me.onloadend(evt);
|
||||
}
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(e) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save error
|
||||
me.error = e;
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileReader.DONE;
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror == "function") {
|
||||
var evt = File._createEvent("error", me);
|
||||
me.onerror(evt);
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend == "function") {
|
||||
var evt = File._createEvent("loadend", me);
|
||||
me.onloadend(evt);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This class provides iPhone read and write access to the mobile device file system.
|
||||
* Based loosely on http://www.w3.org/TR/2009/WD-FileAPI-20091117/#dfn-empty
|
||||
*/
|
||||
function FileMgr()
|
||||
{
|
||||
this.fileWriters = {}; // empty maps
|
||||
this.fileReaders = {};
|
||||
|
||||
this.docsFolderPath = "../../Documents";
|
||||
this.tempFolderPath = "../../tmp";
|
||||
this.freeDiskSpace = -1;
|
||||
this.getFileBasePaths();
|
||||
}
|
||||
|
||||
// private, called from Native Code
|
||||
FileMgr.prototype._setPaths = function(docs,temp)
|
||||
{
|
||||
this.docsFolderPath = docs;
|
||||
this.tempFolderPath = temp;
|
||||
}
|
||||
|
||||
// private, called from Native Code
|
||||
FileMgr.prototype._setFreeDiskSpace = function(val)
|
||||
{
|
||||
this.freeDiskSpace = val;
|
||||
}
|
||||
|
||||
|
||||
// FileWriters add/remove
|
||||
// called internally by writers
|
||||
FileMgr.prototype.addFileWriter = function(filePath,fileWriter)
|
||||
{
|
||||
this.fileWriters[filePath] = fileWriter;
|
||||
}
|
||||
|
||||
FileMgr.prototype.removeFileWriter = function(filePath)
|
||||
{
|
||||
this.fileWriters[filePath] = null;
|
||||
}
|
||||
|
||||
// File readers add/remove
|
||||
// called internally by readers
|
||||
FileMgr.prototype.addFileReader = function(filePath,fileReader)
|
||||
{
|
||||
this.fileReaders[filePath] = fileReader;
|
||||
}
|
||||
|
||||
FileMgr.prototype.removeFileReader = function(filePath)
|
||||
{
|
||||
this.fileReaders[filePath] = null;
|
||||
}
|
||||
|
||||
/*******************************************
|
||||
* Read file and return data as a base64 encoded data url.
|
||||
* A data url is of the form:
|
||||
* data:[<mediatype>][;base64],<data>
|
||||
*
|
||||
* private reader callback delegation
|
||||
* called from native code
|
||||
* @param file The name of the file
|
||||
*/
|
||||
FileMgr.prototype.reader_onloadstart = function(filePath,result)
|
||||
{
|
||||
this.fileReaders[filePath].onloadstart(result);
|
||||
}
|
||||
FileReader.prototype.readAsDataURL = function(file) {
|
||||
this.fileName = file;
|
||||
|
||||
FileMgr.prototype.reader_onprogress = function(filePath,result)
|
||||
{
|
||||
this.fileReaders[filePath].onprogress(result);
|
||||
}
|
||||
// LOADING state
|
||||
this.readyState = FileReader.LOADING;
|
||||
|
||||
FileMgr.prototype.reader_onload = function(filePath,result)
|
||||
{
|
||||
this.fileReaders[filePath].result = unescape(result);
|
||||
this.fileReaders[filePath].onload(this.fileReaders[filePath].result);
|
||||
}
|
||||
// If loadstart callback
|
||||
if (typeof this.onloadstart == "function") {
|
||||
var evt = File._createEvent("loadstart", this);
|
||||
this.onloadstart(evt);
|
||||
}
|
||||
|
||||
FileMgr.prototype.reader_onerror = function(filePath,err)
|
||||
{
|
||||
this.fileReaders[filePath].result = err;
|
||||
this.fileReaders[filePath].onerror(err);
|
||||
}
|
||||
var me = this;
|
||||
|
||||
FileMgr.prototype.reader_onloadend = function(filePath,result)
|
||||
{
|
||||
this.fileReaders[filePath].onloadend(result);
|
||||
}
|
||||
// Read file
|
||||
navigator.fileMgr.readAsDataURL(file,
|
||||
|
||||
/*******************************************
|
||||
// Success callback
|
||||
function(r) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save result
|
||||
me.result = r;
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileReader.DONE;
|
||||
|
||||
// If onload callback
|
||||
if (typeof me.onload == "function") {
|
||||
var evt = File._createEvent("load", me);
|
||||
me.onload(evt);
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend == "function") {
|
||||
var evt = File._createEvent("loadend", me);
|
||||
me.onloadend(evt);
|
||||
}
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(e) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileReader.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save error
|
||||
me.error = e;
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileReader.DONE;
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror == "function") {
|
||||
var evt = File._createEvent("error", me);
|
||||
me.onerror(evt);
|
||||
}
|
||||
|
||||
// If onloadend callback
|
||||
if (typeof me.onloadend == "function") {
|
||||
var evt = File._createEvent("loadend", me);
|
||||
me.onloadend(evt);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Read file and return data as a binary data.
|
||||
*
|
||||
* private writer callback delegation
|
||||
* called from native code
|
||||
*/
|
||||
FileMgr.prototype.writer_onerror = function(filePath,err)
|
||||
{
|
||||
this.fileWriters[filePath].onerror(err);
|
||||
}
|
||||
* @param file The name of the file
|
||||
*/
|
||||
FileReader.prototype.readAsBinaryString = function(file) {
|
||||
// TODO - Can't return binary data to browser.
|
||||
this.fileName = file;
|
||||
};
|
||||
|
||||
FileMgr.prototype.writer_oncomplete = function(filePath,result)
|
||||
{
|
||||
this.fileWriters[filePath].oncomplete(result); // result contains bytes written
|
||||
}
|
||||
|
||||
|
||||
FileMgr.prototype.getFileBasePaths = function()
|
||||
{
|
||||
//PhoneGap.exec("File.getFileBasePaths");
|
||||
}
|
||||
|
||||
FileMgr.prototype.testFileExists = function(fileName, successCallback, errorCallback)
|
||||
{
|
||||
var test = FileUtil.testFileExists(fileName);
|
||||
test ? successCallback() : errorCallback();
|
||||
}
|
||||
|
||||
FileMgr.prototype.testDirectoryExists = function(dirName, successCallback, errorCallback)
|
||||
{
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
var test = FileUtil.testDirectoryExists(dirName);
|
||||
test ? successCallback() : errorCallback();
|
||||
}
|
||||
|
||||
FileMgr.prototype.createDirectory = function(dirName, successCallback, errorCallback)
|
||||
{
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
var test = FileUtils.createDirectory(dirName);
|
||||
test ? successCallback() : errorCallback();
|
||||
}
|
||||
|
||||
FileMgr.prototype.deleteDirectory = function(dirName, successCallback, errorCallback)
|
||||
{
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
var test = FileUtils.deleteDirectory(dirName);
|
||||
test ? successCallback() : errorCallback();
|
||||
}
|
||||
|
||||
FileMgr.prototype.deleteFile = function(fileName, successCallback, errorCallback)
|
||||
{
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
FileUtils.deleteFile(fileName);
|
||||
test ? successCallback() : errorCallback();
|
||||
}
|
||||
|
||||
FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback)
|
||||
{
|
||||
if(this.freeDiskSpace > 0)
|
||||
{
|
||||
return this.freeDiskSpace;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.successCallback = successCallback;
|
||||
this.errorCallback = errorCallback;
|
||||
this.freeDiskSpace = FileUtils.getFreeDiskSpace();
|
||||
(this.freeDiskSpace > 0) ? successCallback() : errorCallback();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// File Reader
|
||||
|
||||
|
||||
function FileReader()
|
||||
{
|
||||
this.fileName = "";
|
||||
this.result = null;
|
||||
this.onloadstart = null;
|
||||
this.onprogress = null;
|
||||
this.onload = null;
|
||||
this.onerror = null;
|
||||
this.onloadend = null;
|
||||
}
|
||||
|
||||
|
||||
FileReader.prototype.abort = function()
|
||||
{
|
||||
// Not Implemented
|
||||
}
|
||||
|
||||
FileReader.prototype.readAsText = function(file)
|
||||
{
|
||||
if(this.fileName && this.fileName.length > 0)
|
||||
{
|
||||
navigator.fileMgr.removeFileReader(this.fileName,this);
|
||||
}
|
||||
this.fileName = file;
|
||||
navigator.fileMgr.addFileReader(this.fileName,this);
|
||||
|
||||
return FileUtil.read(this.fileName);
|
||||
}
|
||||
/**
|
||||
* Read file and return data as a binary data.
|
||||
*
|
||||
* @param file The name of the file
|
||||
*/
|
||||
FileReader.prototype.readAsArrayBuffer = function(file) {
|
||||
// TODO - Can't return binary data to browser.
|
||||
this.fileName = file;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// File Writer
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function FileWriter()
|
||||
{
|
||||
this.fileName = "";
|
||||
this.result = null;
|
||||
this.readyState = 0; // EMPTY
|
||||
this.result = null;
|
||||
this.onerror = null;
|
||||
this.oncomplete = null;
|
||||
}
|
||||
/**
|
||||
* This class writes to the mobile device file system.
|
||||
*
|
||||
* For Android:
|
||||
* The root directory is the root of the file system.
|
||||
* To write to the SD card, the file name is "sdcard/my_file.txt"
|
||||
*/
|
||||
function FileWriter() {
|
||||
this.fileName = "";
|
||||
|
||||
FileWriter.prototype.writeAsText = function(file,text,bAppend)
|
||||
{
|
||||
if(this.fileName && this.fileName.length > 0)
|
||||
{
|
||||
navigator.fileMgr.removeFileWriter(this.fileName,this);
|
||||
this.readyState = 0; // EMPTY
|
||||
|
||||
this.result = null;
|
||||
|
||||
// Error
|
||||
this.error = null;
|
||||
|
||||
// Event handlers
|
||||
this.onwritestart = null; // When writing starts
|
||||
this.onprogress = null; // While writing the file, and reporting partial file data
|
||||
this.onwrite = null; // When the write has successfully completed.
|
||||
this.onwriteend = null; // When the request has completed (either in success or failure).
|
||||
this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
|
||||
this.onerror = null; // When the write has failed (see errors).
|
||||
};
|
||||
|
||||
// States
|
||||
FileWriter.INIT = 0;
|
||||
FileWriter.WRITING = 1;
|
||||
FileWriter.DONE = 2;
|
||||
|
||||
/**
|
||||
* Abort writing file.
|
||||
*/
|
||||
FileWriter.prototype.abort = function() {
|
||||
this.readyState = FileWriter.DONE;
|
||||
|
||||
// If abort callback
|
||||
if (typeof this.onabort == "function") {
|
||||
var evt = File._createEvent("abort", this);
|
||||
this.onabort(evt);
|
||||
}
|
||||
|
||||
// TODO: Anything else to do? Maybe sent to native?
|
||||
};
|
||||
|
||||
FileWriter.prototype.writeAsText = function(file, text, bAppend) {
|
||||
// Throw an exception if we are already writing a file
|
||||
if (this.readyState == FileWriter.WRITING) {
|
||||
throw FileError.INVALID_STATE_ERR;
|
||||
}
|
||||
this.fileName = file;
|
||||
if(bAppend != true)
|
||||
{
|
||||
bAppend = false; // for null values
|
||||
|
||||
if (bAppend != true) {
|
||||
bAppend = false; // for null values
|
||||
}
|
||||
|
||||
this.fileName = file;
|
||||
|
||||
// WRITING state
|
||||
this.readyState = FileWriter.WRITING;
|
||||
|
||||
var me = this;
|
||||
|
||||
// If onwritestart callback
|
||||
if (typeof me.onwritestart == "function") {
|
||||
var evt = File._createEvent("writestart", me);
|
||||
me.onwritestart(evt);
|
||||
}
|
||||
|
||||
// Write file
|
||||
navigator.fileMgr.writeAsText(file, text, bAppend,
|
||||
|
||||
// Success callback
|
||||
function(r) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileWriter.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save result
|
||||
me.result = r;
|
||||
|
||||
// If onwrite callback
|
||||
if (typeof me.onwrite == "function") {
|
||||
var evt = File._createEvent("write", me);
|
||||
me.onwrite(evt);
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileWriter.DONE;
|
||||
|
||||
// If onwriteend callback
|
||||
if (typeof me.onwriteend == "function") {
|
||||
var evt = File._createEvent("writeend", me);
|
||||
me.onwriteend(evt);
|
||||
}
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(e) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileWriter.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save error
|
||||
me.error = e;
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror == "function") {
|
||||
var evt = File._createEvent("error", me);
|
||||
me.onerror(evt);
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileWriter.DONE;
|
||||
|
||||
// If onwriteend callback
|
||||
if (typeof me.onwriteend == "function") {
|
||||
var evt = File._createEvent("writeend", me);
|
||||
me.onwriteend(evt);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
FileWriter.prototype.truncate = function(file, size) {
|
||||
// Throw an exception if we are already writing a file
|
||||
if (this.readyState == FileWriter.WRITING) {
|
||||
throw FileError.INVALID_STATE_ERR;
|
||||
}
|
||||
navigator.fileMgr.addFileWriter(file,this);
|
||||
this.readyState = 0; // EMPTY
|
||||
var call = FileUtil.write(file, text, bAppend);
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
this.fileName = file;
|
||||
|
||||
// WRITING state
|
||||
this.readyState = FileWriter.WRITING;
|
||||
|
||||
var me = this;
|
||||
|
||||
// If onwritestart callback
|
||||
if (typeof me.onwritestart == "function") {
|
||||
var evt = File._createEvent("writestart", me);
|
||||
me.onwritestart(evt);
|
||||
}
|
||||
|
||||
// Write file
|
||||
navigator.fileMgr.truncate(file, size,
|
||||
|
||||
// Success callback
|
||||
function(r) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileWriter.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save result
|
||||
me.result = r;
|
||||
|
||||
// If onwrite callback
|
||||
if (typeof me.onwrite == "function") {
|
||||
var evt = File._createEvent("write", me);
|
||||
me.onwrite(evt);
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileWriter.DONE;
|
||||
|
||||
// If onwriteend callback
|
||||
if (typeof me.onwriteend == "function") {
|
||||
var evt = File._createEvent("writeend", me);
|
||||
me.onwriteend(evt);
|
||||
}
|
||||
},
|
||||
|
||||
// Error callback
|
||||
function(e) {
|
||||
|
||||
// If DONE (cancelled), then don't do anything
|
||||
if (me.readyState == FileWriter.DONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Save error
|
||||
me.error = e;
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror == "function") {
|
||||
var evt = File._createEvent("error", me);
|
||||
me.onerror(evt);
|
||||
}
|
||||
|
||||
// DONE state
|
||||
me.readyState = FileWriter.DONE;
|
||||
|
||||
// If onwriteend callback
|
||||
if (typeof me.onwriteend == "function") {
|
||||
var evt = File._createEvent("writeend", me);
|
||||
me.onwriteend(evt);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
256
framework/assets/js/geolocation.js
Normal file → Executable file
256
framework/assets/js/geolocation.js
Normal file → Executable file
@@ -1,90 +1,194 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class provides access to device GPS data.
|
||||
* @constructor
|
||||
*/
|
||||
function Geolocation() {
|
||||
/**
|
||||
* The last known GPS position.
|
||||
*/
|
||||
|
||||
// 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
|
||||
*
|
||||
* @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);
|
||||
function PositionError(code, message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
};
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
Geolocation.prototype.fail = function(key)
|
||||
{
|
||||
geoListeners[key].fail();
|
||||
}
|
||||
|
||||
Geolocation.prototype.clearWatch = function(watchId)
|
||||
{
|
||||
Geo.stop(watchId);
|
||||
}
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
19
framework/assets/js/keyevent.js
Normal file → Executable file
19
framework/assets/js/keyevent.js
Normal file → Executable file
@@ -1,18 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
function KeyEvent()
|
||||
{
|
||||
}
|
||||
|
||||
KeyEvent.prototype.menuTrigger = function()
|
||||
KeyEvent.prototype.backTrigger = function()
|
||||
{
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('menuKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
|
||||
KeyEvent.prototype.searchTrigger= function()
|
||||
{
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('searchKeyDown');
|
||||
e.initEvent('backKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
|
||||
|
||||
206
framework/assets/js/media.js
Normal file → Executable file
206
framework/assets/js/media.js
Normal file → Executable file
@@ -1,62 +1,194 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* List of media objects.
|
||||
* PRIVATE
|
||||
*/
|
||||
PhoneGap.mediaObjects = {};
|
||||
|
||||
/**
|
||||
* Object that receives native callbacks.
|
||||
* PRIVATE
|
||||
*/
|
||||
PhoneGap.Media = function() {};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
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_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 = "";
|
||||
}
|
||||
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
|
||||
* @constructor
|
||||
* Stop playing audio file.
|
||||
*/
|
||||
|
||||
Media.prototype.play = function() {
|
||||
DroidGap.startPlayingAudio(this.src);
|
||||
}
|
||||
|
||||
Media.prototype.stop = function() {
|
||||
DroidGap.stopPlayingAudio();
|
||||
}
|
||||
return PhoneGap.exec(null, null, "Media", "stopPlayingAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Media.prototype.getCurrentPosition = function(success, fail) {
|
||||
PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Start recording audio file.
|
||||
*/
|
||||
Media.prototype.startRecord = function() {
|
||||
DroidGap.startRecordingAudio(this.src);
|
||||
}
|
||||
|
||||
Media.prototype.stopRecordingAudio = function() {
|
||||
DroidGap.stopRecordingAudio();
|
||||
}
|
||||
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]);
|
||||
};
|
||||
|
||||
|
||||
54
framework/assets/js/network.js
Normal file → Executable file
54
framework/assets/js/network.js
Normal file → Executable file
@@ -1,14 +1,24 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class contains information about any NetworkStatus.
|
||||
* @constructor
|
||||
*/
|
||||
function NetworkStatus() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
}
|
||||
//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
|
||||
@@ -16,39 +26,37 @@ NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
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) }
|
||||
* { hostName: string, ipAddress: string,
|
||||
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
|
||||
*/
|
||||
this.lastReachability = null;
|
||||
this.lastReachability = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when the reachability status has changed.
|
||||
* @param {Reachibility} reachability The current reachability status.
|
||||
*/
|
||||
// TODO: Callback from native code not implemented for Android
|
||||
Network.prototype.updateReachability = function(reachability) {
|
||||
this.lastReachability = reachability;
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* Determine if a URI is reachable over the network.
|
||||
|
||||
* @param {Object} uri
|
||||
* @param {Function} win
|
||||
* @param {Function} callback
|
||||
* @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);
|
||||
Network.prototype.isReachable = function(uri, callback, options) {
|
||||
var isIpAddress = false;
|
||||
if (options && options.isIpAddress) {
|
||||
isIpAddress = options.isIpAddress;
|
||||
}
|
||||
PhoneGap.exec(callback, null, "Network Status", "isReachable", [uri, isIpAddress]);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.network == "undefined") navigator.network = new Network();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
105
framework/assets/js/notification.js
Normal file → Executable file
105
framework/assets/js/notification.js
Normal file → Executable file
@@ -1,77 +1,116 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
*/
|
||||
function Notification() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
681
framework/assets/js/phonegap.js.base
Normal file → Executable file
681
framework/assets/js/phonegap.js.base
Normal file → Executable file
@@ -1,3 +1,33 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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);
|
||||
*/
|
||||
|
||||
if (typeof(DeviceInfo) != 'object')
|
||||
DeviceInfo = {};
|
||||
|
||||
@@ -6,13 +36,110 @@ if (typeof(DeviceInfo) != 'object')
|
||||
* information about the state of PhoneGap.
|
||||
* @class
|
||||
*/
|
||||
PhoneGap = {
|
||||
var PhoneGap = {
|
||||
queue: {
|
||||
ready: true,
|
||||
commands: [],
|
||||
timer: null
|
||||
},
|
||||
_constructors: []
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Custom pub-sub channel that can have functions subscribed to it
|
||||
*/
|
||||
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" && f instanceof 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" && f instanceof 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 (g instanceof 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;
|
||||
for (var item in this.handlers) {
|
||||
var handler = this.handlers[item];
|
||||
if (handler instanceof Function) {
|
||||
var 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();
|
||||
}
|
||||
for (var j=0; j<i; j++) {
|
||||
(!c[j].fired?c[j].subscribeOnce(f):i--);
|
||||
}
|
||||
if (!i) h();
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -26,69 +153,385 @@ 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) {
|
||||
if (typeof(debug['log']) == 'function') {
|
||||
debug.log("Failed to run constructor: " + debug.processMessage(e));
|
||||
} else {
|
||||
alert("Failed to run constructor: " + e.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
// all constructors run, now fire the deviceready event
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('deviceready');
|
||||
document.dispatchEvent(e);
|
||||
}
|
||||
}, 5);
|
||||
})();
|
||||
/**
|
||||
* Adds a plugin object to window.plugins
|
||||
*/
|
||||
PhoneGap.addPlugin = function(name, obj) {
|
||||
if ( !window.plugins ) {
|
||||
window.plugins = {};
|
||||
}
|
||||
|
||||
if ( !window.plugins[name] ) {
|
||||
window.plugins[name] = obj;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
/**
|
||||
* 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');
|
||||
|
||||
// _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');
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Create all PhoneGap objects once page has fully loaded and native side is ready.
|
||||
*/
|
||||
PhoneGap.exec = function() {
|
||||
PhoneGap.queue.commands.push(arguments);
|
||||
if (PhoneGap.queue.timer == null)
|
||||
PhoneGap.queue.timer = setInterval(PhoneGap.run_command, 10);
|
||||
PhoneGap.Channel.join(function() {
|
||||
|
||||
// Start listening for XHR callbacks
|
||||
setTimeout(function() {
|
||||
if (CallbackServer.usePolling()) {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
else {
|
||||
PhoneGap.JSCallback();
|
||||
}
|
||||
}, 1);
|
||||
|
||||
// Run PhoneGap constructors
|
||||
PhoneGap.onPhoneGapInit.fire();
|
||||
|
||||
// Fire event to notify that all objects are created
|
||||
PhoneGap.onPhoneGapReady.fire();
|
||||
|
||||
}, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]);
|
||||
|
||||
/**
|
||||
* Fire onDeviceReady event once all constructors have run and PhoneGap info has been
|
||||
* received from native side.
|
||||
*/
|
||||
PhoneGap.Channel.join(function() {
|
||||
// Turn off app loading dialog
|
||||
navigator.notification.activityStop();
|
||||
|
||||
PhoneGap.onDeviceReady.fire();
|
||||
|
||||
// Fire the onresume event, since first one happens before JavaScript is loaded
|
||||
PhoneGap.onResume.fire();
|
||||
}, [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady]);
|
||||
|
||||
// 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);
|
||||
} else if (e == 'pause') {
|
||||
PhoneGap.onPause.subscribe(handler);
|
||||
} else {
|
||||
PhoneGap.m_document_addEventListener.call(document, evt, handler);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
PhoneGap.stringify = function(args) {
|
||||
if (typeof JSON == "undefined") {
|
||||
var s = "[";
|
||||
for (var i=0; i<args.length; i++) {
|
||||
if (i > 0) {
|
||||
s = s + ",";
|
||||
}
|
||||
var 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) {
|
||||
var start = true;
|
||||
s = s + '{';
|
||||
for (var name in args[i]) {
|
||||
if (!start) {
|
||||
s = s + ',';
|
||||
}
|
||||
s = s + '"' + name + '":';
|
||||
var nameType = typeof args[i][name];
|
||||
if ((nameType == "number") || (nameType == "boolean")) {
|
||||
s = s + args[i][name];
|
||||
}
|
||||
else {
|
||||
s = s + '"' + args[i][name] + '"';
|
||||
}
|
||||
start=false;
|
||||
}
|
||||
s = s + '}';
|
||||
}
|
||||
else {
|
||||
var 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
|
||||
*/
|
||||
PhoneGap.clone = function(obj) {
|
||||
if(!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(obj instanceof Array){
|
||||
var retVal = new Array();
|
||||
for(var i = 0; i < obj.length; ++i){
|
||||
retVal.push(PhoneGap.clone(obj[i]));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (obj instanceof Function) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(!(obj instanceof Object)){
|
||||
return obj;
|
||||
}
|
||||
|
||||
retVal = new Object();
|
||||
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. 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 {String[]} [args] Zero or more arguments to pass to the method
|
||||
*/
|
||||
PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
try {
|
||||
var callbackId = service + PhoneGap.callbackId++;
|
||||
if (success || fail) {
|
||||
PhoneGap.callbacks[callbackId] = {success:success, fail:fail};
|
||||
}
|
||||
|
||||
// Note: Device returns string, but for some reason emulator returns object - so convert to string.
|
||||
var r = ""+PluginManager.exec(service, action, callbackId, this.stringify(args), 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="+r.status+" Message="+v.message);
|
||||
|
||||
// If there is a fail callback, then call it now with returned value
|
||||
if (fail) {
|
||||
try {
|
||||
fail(v.message);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Error: "+e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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,6 +539,7 @@ 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)
|
||||
return;
|
||||
@@ -133,3 +577,124 @@ PhoneGap.run_command = function() {
|
||||
document.location = url;
|
||||
|
||||
};
|
||||
|
||||
PhoneGap.JSCallbackPort = CallbackServer.getPort();
|
||||
PhoneGap.JSCallbackToken = CallbackServer.getToken();
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
|
||||
// Callback function when XMLHttpRequest is ready
|
||||
xmlhttp.onreadystatechange=function(){
|
||||
if(xmlhttp.readyState == 4){
|
||||
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status == 200) {
|
||||
|
||||
var msg = 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("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 error, restart callback server
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed.");
|
||||
CallbackServer.restartServer();
|
||||
setTimeout(PhoneGap.JSCallback, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
var msg = CallbackServer.getJavascript();
|
||||
if (msg) {
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(""+msg);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("JSCallbackPolling Error: "+e);
|
||||
}
|
||||
}, 1);
|
||||
setTimeout(PhoneGap.JSCallbackPolling, 1);
|
||||
}
|
||||
else {
|
||||
setTimeout(PhoneGap.JSCallbackPolling, PhoneGap.JSCallbackPollingPeriod);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
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 = "";
|
||||
for (var i=0; i<length; i++) {
|
||||
var uuidchar = parseInt((Math.random() * 256)).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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
16
framework/assets/js/position.js
Normal file → Executable file
16
framework/assets/js/position.js
Normal file → Executable file
@@ -1,3 +1,11 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class contains position information.
|
||||
* @param {Object} lat
|
||||
@@ -11,10 +19,10 @@
|
||||
*/
|
||||
function Position(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) {
|
||||
function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/**
|
||||
* The latitude of the position.
|
||||
*/
|
||||
@@ -39,6 +47,10 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
13
framework/assets/js/splashscreen.js
Executable file
13
framework/assets/js/splashscreen.js
Executable file
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.splashScreen == "undefined") {
|
||||
navigator.splashScreen = SplashScreen; // SplashScreen object come from native side through addJavaScriptInterface
|
||||
}
|
||||
});
|
||||
369
framework/assets/js/storage.js
Normal file → Executable file
369
framework/assets/js/storage.js
Normal file → Executable file
@@ -1,89 +1,316 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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 = [];
|
||||
/**
|
||||
* Storage object that is called by native code when performing queries.
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
var DroidDB = function() {
|
||||
this.queryQueue = {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback from native code when result from a query is available.
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param rawdata JSON string of the row data
|
||||
* @param id Query id
|
||||
*/
|
||||
DroidDB.prototype.addResult = function(rawdata, id) {
|
||||
try {
|
||||
eval("var data = " + rawdata + ";");
|
||||
var query = this.queryQueue[id];
|
||||
query.resultSet.push(data);
|
||||
} catch (e) {
|
||||
console.log("DroidDB.addResult(): Error="+e);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback from native code when query is complete.
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param id Query id
|
||||
*/
|
||||
DroidDB.prototype.completeQuery = function(id) {
|
||||
var query = this.queryQueue[id];
|
||||
if (query) {
|
||||
try {
|
||||
delete this.queryQueue[id];
|
||||
|
||||
// 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]) {
|
||||
|
||||
// Save query results
|
||||
var r = new DroidDB_Result();
|
||||
r.rows.resultSet = query.resultSet;
|
||||
r.rows.length = query.resultSet.length;
|
||||
try {
|
||||
if (typeof query.successCallback == 'function') {
|
||||
query.successCallback(query.tx, r);
|
||||
}
|
||||
} catch (ex) {
|
||||
console.log("executeSql error calling user success callback: "+ex);
|
||||
}
|
||||
|
||||
tx.queryComplete(id);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("executeSql error: "+e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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];
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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, successCallback, errorCallback) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction object
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
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;
|
||||
for (var i in this.queryList) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL query object
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @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;
|
||||
|
||||
}
|
||||
|
||||
DroidDB.prototype.addResult = function(rawdata, tx_id)
|
||||
{
|
||||
eval("var data = " + rawdata);
|
||||
var tx = this.txQueue[tx_id];
|
||||
tx.resultSet.push(data);
|
||||
}
|
||||
/**
|
||||
* 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) {
|
||||
|
||||
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);
|
||||
}
|
||||
// Init params array
|
||||
if (typeof params == 'undefined') {
|
||||
params = [];
|
||||
}
|
||||
|
||||
DroidDB.prototype.fail = function(reason, tx_id)
|
||||
{
|
||||
var tx = this.txQueue[tx_id];
|
||||
tx.fail(reason);
|
||||
}
|
||||
// Create query and add to queue
|
||||
var query = new DroidDB_Query(this);
|
||||
droiddb.queryQueue[query.id] = query;
|
||||
|
||||
var DatabaseShell = function()
|
||||
{
|
||||
|
||||
}
|
||||
// Save callbacks
|
||||
query.successCallback = successCallback;
|
||||
query.errorCallback = errorCallback;
|
||||
|
||||
DatabaseShell.prototype.transaction = function(process)
|
||||
{
|
||||
tx = new Tx();
|
||||
process(tx);
|
||||
}
|
||||
// Call native code
|
||||
PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
|
||||
};
|
||||
|
||||
var Tx = function()
|
||||
{
|
||||
droiddb.txQueue.push(this);
|
||||
this.id = droiddb.txQueue.length - 1;
|
||||
this.resultSet = [];
|
||||
}
|
||||
/**
|
||||
* SQL result set that is returned to user.
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
DroidDB_Result = function() {
|
||||
this.rows = new DroidDB_Rows();
|
||||
};
|
||||
|
||||
Tx.prototype.executeSql = function(query, params, win, fail)
|
||||
{
|
||||
droidStorage.executeSql(query, params, this.id);
|
||||
tx.win = win;
|
||||
tx.fail = fail;
|
||||
}
|
||||
/**
|
||||
* SQL result set object
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
DroidDB_Rows = function() {
|
||||
this.resultSet = []; // results array
|
||||
this.length = 0; // number of rows
|
||||
};
|
||||
|
||||
var result = function()
|
||||
{
|
||||
this.rows = new Rows();
|
||||
}
|
||||
/**
|
||||
* 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];
|
||||
};
|
||||
|
||||
var Rows = function()
|
||||
{
|
||||
this.resultSet = [];
|
||||
this.length = 0;
|
||||
}
|
||||
|
||||
Rows.prototype.item = function(row_id)
|
||||
{
|
||||
return this.resultSet[id];
|
||||
}
|
||||
|
||||
var dbSetup = function(name, version, display_name, size)
|
||||
{
|
||||
droidStorage.openDatabase(name, version, display_name, size)
|
||||
db_object = new DatabaseShell();
|
||||
return db_object;
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof window.openDatabase == "undefined")
|
||||
{
|
||||
navigator.openDatabase = window.openDatabase = dbSetup;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
if (typeof window.openDatabase == "undefined") {
|
||||
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
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
@@ -64,21 +64,53 @@
|
||||
-->
|
||||
<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.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>
|
||||
|
||||
<target name="build-javascript">
|
||||
<delete file="assets/www/phonegap.js"/>
|
||||
<concat destfile="assets/www/phonegap.js">
|
||||
<fileset dir="assets/js" includes="phonegap.js.base" />
|
||||
<fileset dir="assets/js" includes="*.js" />
|
||||
</concat>
|
||||
</target>
|
||||
|
||||
<target name="jar" depends="move_files, compile">
|
||||
<target name="jar" depends="build-javascript, compile">
|
||||
<jar jarfile="phonegap.jar" basedir="bin/classes" excludes="**/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>
|
||||
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -12,15 +12,13 @@ public final class R {
|
||||
}
|
||||
public static final class drawable {
|
||||
public static final int icon=0x7f020000;
|
||||
public static final int splash=0x7f020001;
|
||||
}
|
||||
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;
|
||||
|
||||
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>
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
332
framework/src/com/phonegap/AccelListener.java
Normal file → Executable file
332
framework/src/com/phonegap/AccelListener.java
Normal file → Executable file
@@ -1,80 +1,296 @@
|
||||
/*
|
||||
* 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.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(DroidGap 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
408
framework/src/com/phonegap/AudioHandler.java
Normal file → Executable file
408
framework/src/com/phonegap/AudioHandler.java
Normal file → Executable file
@@ -1,193 +1,263 @@
|
||||
/*
|
||||
* 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 DroidGap 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("pausePlayingAudio")) {
|
||||
this.pausePlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("stopPlayingAudio")) {
|
||||
this.stopPlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("getCurrentPositionAudio")) {
|
||||
long l = this.getCurrentPositionAudio(args.getString(0));
|
||||
return new PluginResult(status, l);
|
||||
}
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
long l = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, l);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
/**
|
||||
* 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
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 long getCurrentPositionAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return(audio.getCurrentPosition());
|
||||
}
|
||||
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 long 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
397
framework/src/com/phonegap/AudioPlayer.java
Executable file
397
framework/src/com/phonegap/AudioPlayer.java
Executable file
@@ -0,0 +1,397 @@
|
||||
/*
|
||||
* 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.OnErrorListener;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
// 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_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 long 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;
|
||||
|
||||
// YES, I know this is bad, but I can't do it the right way because Google didn't have the
|
||||
// foresight to add android.os.environment.getExternalDataDirectory until Android 2.2
|
||||
this.tempFile = "/sdcard/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) {
|
||||
System.out.println("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 {
|
||||
System.out.println("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) {
|
||||
System.out.println("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 = this.mPlayer.getDuration();
|
||||
}
|
||||
}
|
||||
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 {
|
||||
System.out.println("AudioPlayer Error: startPlaying() called during invalid state: "+this.state);
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RESUME_STATE+");");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
*/
|
||||
public void pausePlaying() {
|
||||
|
||||
// If playing, then pause
|
||||
if (this.state == MEDIA_RUNNING) {
|
||||
this.mPlayer.pause();
|
||||
this.setState(MEDIA_PAUSED);
|
||||
}
|
||||
else {
|
||||
System.out.println("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 {
|
||||
System.out.println("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)) {
|
||||
return this.mPlayer.getCurrentPosition();
|
||||
}
|
||||
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 long 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 = this.mPlayer.getDuration();
|
||||
this.prepareOnly = false;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_DURATION+","+this.duration+");");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
System.out.println("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;
|
||||
}
|
||||
|
||||
}
|
||||
51
framework/src/com/phonegap/BrowserKey.java
Executable file
51
framework/src/com/phonegap/BrowserKey.java
Executable file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 android.app.Activity;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/*
|
||||
* This class literally exists to protect DroidGap from Javascript directly.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
public class BrowserKey {
|
||||
|
||||
DroidGap mAction;
|
||||
boolean bound;
|
||||
WebView mView;
|
||||
|
||||
BrowserKey(WebView view, DroidGap action)
|
||||
{
|
||||
bound = false;
|
||||
mAction = action;
|
||||
}
|
||||
|
||||
public void override()
|
||||
{
|
||||
Log.d("PhoneGap", "WARNING: Back Button Default Behaviour will be overridden. The backKeyDown event will be fired!");
|
||||
bound = true;
|
||||
}
|
||||
|
||||
public boolean isBound()
|
||||
{
|
||||
return bound;
|
||||
}
|
||||
|
||||
public void reset()
|
||||
{
|
||||
bound = false;
|
||||
}
|
||||
|
||||
public void exitApp()
|
||||
{
|
||||
mAction.finish();
|
||||
}
|
||||
}
|
||||
281
framework/src/com/phonegap/CallbackServer.java
Executable file
281
framework/src/com/phonegap/CallbackServer.java
Executable file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* 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.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* This class provides a way for Java to run JavaScript in the web page that has loaded 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 {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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>();
|
||||
|
||||
if (android.net.Proxy.getDefaultHost() != null) {
|
||||
this.usePolling = true;
|
||||
}
|
||||
else {
|
||||
this.usePolling = false;
|
||||
this.startServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if polling should be 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(" -- using port " +this.port);
|
||||
this.token = java.util.UUID.randomUUID().toString();
|
||||
//System.out.println(" -- 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();
|
||||
//System.out.println("Request="+request);
|
||||
if (request.contains("GET")) {
|
||||
|
||||
// Must have security token
|
||||
if (request.substring(5,41).equals(this.token)) {
|
||||
//System.out.println(" -- Processing GET request");
|
||||
|
||||
// Wait until there is some data to send, or send empty data every 30 sec
|
||||
// to prevent XHR timeout on the client
|
||||
synchronized (this) {
|
||||
while (this.empty) {
|
||||
try {
|
||||
this.wait(30000); // prevent timeout from happening
|
||||
//System.out.println(">>> 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(" -- sending data 0");
|
||||
output.writeBytes("HTTP/1.1 404 NO DATA\r\n\r\n");
|
||||
}
|
||||
else {
|
||||
//System.out.println(" -- sending item");
|
||||
output.writeBytes("HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//System.out.println("CallbackServer: closing output");
|
||||
output.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()");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
281
framework/src/com/phonegap/CameraLauncher.java
Normal file → Executable file
281
framework/src/com/phonegap/CameraLauncher.java
Normal file → Executable file
@@ -1,34 +1,275 @@
|
||||
/*
|
||||
* 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.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
|
||||
/**
|
||||
* 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 int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
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 (srcType == CAMERA) {
|
||||
this.takePicture(args.getInt(0), destType);
|
||||
}
|
||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||
this.getImage(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 DroidGap.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) {
|
||||
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 = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
|
||||
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);
|
||||
}
|
||||
|
||||
/* Return Base64 Encoded String to Javascript */
|
||||
public void processPicture( String js_out )
|
||||
{
|
||||
mAppView.loadUrl("javascript:navigator.camera.win('" + js_out + "');");
|
||||
/**
|
||||
* Get image from photo library.
|
||||
*
|
||||
* @param returnType
|
||||
*/
|
||||
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
|
||||
public void getImage(int srcType, int returnType) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
|
||||
// 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);
|
||||
}
|
||||
} 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));
|
||||
this.processPicture(bitmap);
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
this.failPicture("Error compressing image.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
}
|
||||
263
framework/src/com/phonegap/CompassListener.java
Normal file → Executable file
263
framework/src/com/phonegap/CompassListener.java
Normal file → Executable file
@@ -1,54 +1,269 @@
|
||||
/*
|
||||
* 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.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(DroidGap 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
200
framework/src/com/phonegap/ContactAccessor.java
Normal file
200
framework/src/com/phonegap/ContactAccessor.java
Normal file
@@ -0,0 +1,200 @@
|
||||
// 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.lang.reflect.Constructor;
|
||||
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 {
|
||||
|
||||
/**
|
||||
* Static singleton instance of {@link ContactAccessor} holding the
|
||||
* SDK-specific implementation of the class.
|
||||
*/
|
||||
private static ContactAccessor sInstance;
|
||||
protected final String LOG_TAG = "ContactsAccessor";
|
||||
protected Activity mApp;
|
||||
protected WebView mView;
|
||||
|
||||
public static ContactAccessor getInstance(WebView view, Activity app) {
|
||||
if (sInstance == null) {
|
||||
String className;
|
||||
|
||||
/*
|
||||
* Check the version of the SDK we are running on. Choose an
|
||||
* implementation class designed for that version of the SDK.
|
||||
*
|
||||
* Unfortunately we have to use strings to represent the class
|
||||
* names. If we used the conventional ContactAccessorSdk5.class.getName()
|
||||
* syntax, we would get a ClassNotFoundException at runtime on pre-Eclair SDKs.
|
||||
* Using the above syntax would force Dalvik to load the class and try to
|
||||
* resolve references to all other classes it uses. Since the pre-Eclair
|
||||
* does not have those classes, the loading of ContactAccessorSdk5 would fail.
|
||||
*/
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
className = "com.phonegap.ContactAccessorSdk3_4";
|
||||
} else {
|
||||
className = "com.phonegap.ContactAccessorSdk5";
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the required class by name and instantiate it.
|
||||
*/
|
||||
try {
|
||||
Class<? extends ContactAccessor> clazz =
|
||||
Class.forName(className).asSubclass(ContactAccessor.class);
|
||||
// Grab constructor for contactsmanager class dynamically.
|
||||
Constructor<? extends ContactAccessor> classConstructor = clazz.getConstructor(Class.forName("android.webkit.WebView"), Class.forName("android.app.Activity"));
|
||||
sInstance = classConstructor.newInstance(view, app);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
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("anniversary")) {
|
||||
map.put("anniversary", true);
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
map.put("note", true);
|
||||
}
|
||||
else if (key.startsWith("relationships")) {
|
||||
map.put("relationships", true);
|
||||
}
|
||||
else if (key.startsWith("urls")) {
|
||||
map.put("urls", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding a JSON Contact object into the database.
|
||||
*/
|
||||
public abstract void save(JSONObject contact);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
*/
|
||||
public abstract JSONArray search(JSONArray filter, JSONObject options);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
455
framework/src/com/phonegap/ContactAccessorSdk3_4.java
Normal file
455
framework/src/com/phonegap/ContactAccessorSdk3_4.java
Normal file
@@ -0,0 +1,455 @@
|
||||
// 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 java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.Contacts;
|
||||
import android.provider.Contacts.ContactMethods;
|
||||
import android.provider.Contacts.ContactMethodsColumns;
|
||||
import android.provider.Contacts.Organizations;
|
||||
import android.provider.Contacts.People;
|
||||
import android.provider.Contacts.Phones;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ContactAccessor} that uses legacy Contacts API.
|
||||
* These APIs are deprecated and should not be used unless we are running on a
|
||||
* pre-Eclair SDK.
|
||||
* <p>
|
||||
* There are several reasons why we wouldn't want to use this class on an Eclair device:
|
||||
* <ul>
|
||||
* <li>It would see at most one account, namely the first Google account created on the device.
|
||||
* <li>It would work through a compatibility layer, which would make it inherently less efficient.
|
||||
* <li>Not relevant to this particular example, but it would not have access to new kinds
|
||||
* of data available through current APIs.
|
||||
* </ul>
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
/**
|
||||
* A static map that converts the JavaScript property name to Android database column name.
|
||||
*/
|
||||
private static final Map<String, String> dbMap = new HashMap<String, String>();
|
||||
static {
|
||||
dbMap.put("id", People._ID);
|
||||
dbMap.put("displayName", People.DISPLAY_NAME);
|
||||
dbMap.put("phoneNumbers", Phones.NUMBER);
|
||||
dbMap.put("phoneNumbers.value", Phones.NUMBER);
|
||||
dbMap.put("emails", ContactMethods.DATA);
|
||||
dbMap.put("emails.value", ContactMethods.DATA);
|
||||
dbMap.put("addresses", ContactMethodsColumns.DATA);
|
||||
dbMap.put("addresses.formatted", ContactMethodsColumns.DATA);
|
||||
dbMap.put("ims", ContactMethodsColumns.DATA);
|
||||
dbMap.put("ims.value", ContactMethodsColumns.DATA);
|
||||
dbMap.put("organizations", Organizations.COMPANY);
|
||||
dbMap.put("organizations.name", Organizations.COMPANY);
|
||||
dbMap.put("organizations.title", Organizations.TITLE);
|
||||
dbMap.put("note", People.NOTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an contact accessor.
|
||||
*/
|
||||
public ContactAccessorSdk3_4(WebView view, Activity app)
|
||||
{
|
||||
mApp = app;
|
||||
mView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* This method takes the fields required and search options in order to produce an
|
||||
* array of contacts that matches the criteria provided.
|
||||
* @param fields an array of items to be used as search criteria
|
||||
* @param options that can be applied to contact searching
|
||||
* @return an array of contacts
|
||||
*/
|
||||
public JSONArray search(JSONArray fields, JSONObject options) {
|
||||
String searchTerm = "";
|
||||
int limit = 1;
|
||||
boolean multiple = false;
|
||||
try {
|
||||
searchTerm = options.getString("filter");
|
||||
if (searchTerm.length()==0) {
|
||||
searchTerm = "%";
|
||||
}
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (multiple) {
|
||||
limit = options.getInt("limit");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
ContentResolver cr = mApp.getContentResolver();
|
||||
|
||||
Set<String> contactIds = buildSetOfContactIds(fields, searchTerm);
|
||||
HashMap<String,Boolean> populate = buildPopulationSet(fields);
|
||||
|
||||
Iterator<String> it = contactIds.iterator();
|
||||
|
||||
JSONArray contacts = new JSONArray();
|
||||
JSONObject contact;
|
||||
String contactId;
|
||||
int pos = 0;
|
||||
while (it.hasNext() && (pos < limit)) {
|
||||
contact = new JSONObject();
|
||||
try {
|
||||
contactId = it.next();
|
||||
contact.put("id", contactId);
|
||||
|
||||
// Do query for name and note
|
||||
Cursor cur = cr.query(People.CONTENT_URI,
|
||||
new String[] {People.DISPLAY_NAME, People.NOTES},
|
||||
"people._id = ?",
|
||||
new String[] {contactId},
|
||||
null);
|
||||
cur.moveToFirst();
|
||||
|
||||
if (isRequired("displayName",populate)) {
|
||||
contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME)));
|
||||
}
|
||||
if (isRequired("phoneNumbers",populate)) {
|
||||
contact.put("phoneNumbers", phoneQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("emails",populate)) {
|
||||
contact.put("emails", emailQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("addresses",populate)) {
|
||||
contact.put("addresses", addressQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("organizations",populate)) {
|
||||
contact.put("organizations", organizationQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("ims",populate)) {
|
||||
contact.put("ims", imQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("note",populate)) {
|
||||
contact.put("note", cur.getString(cur.getColumnIndex(People.NOTES)));
|
||||
}
|
||||
// nickname
|
||||
// urls
|
||||
// relationship
|
||||
// birthdays
|
||||
// anniversary
|
||||
|
||||
pos++;
|
||||
cur.close();
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
contacts.put(contact);
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the database using the search term to build up a list of contact ID's
|
||||
* matching the search term
|
||||
* @param fields
|
||||
* @param searchTerm
|
||||
* @return a set of contact ID's
|
||||
*/
|
||||
private Set<String> buildSetOfContactIds(JSONArray fields, String searchTerm) {
|
||||
Set<String> contactIds = new HashSet<String>();
|
||||
|
||||
String key;
|
||||
try {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
if (key.startsWith("displayName")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
People.CONTENT_URI,
|
||||
People._ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
// else if (key.startsWith("name")) {
|
||||
// Log.d(LOG_TAG, "Doing " + key + " query");
|
||||
// doQuery(searchTerm, contactIds,
|
||||
// ContactsContract.Data.CONTENT_URI,
|
||||
// ContactsContract.Data.CONTACT_ID,
|
||||
// dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?",
|
||||
// new String[] {searchTerm, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE});
|
||||
// }
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
Phones.CONTENT_URI,
|
||||
Phones.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
ContactMethods.CONTENT_EMAIL_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
|
||||
new String[] {searchTerm, ContactMethods.CONTENT_EMAIL_ITEM_TYPE});
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
ContactMethods.CONTENT_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
|
||||
new String[] {searchTerm, ContactMethods.CONTENT_POSTAL_ITEM_TYPE});
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
ContactMethods.CONTENT_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
|
||||
new String[] {searchTerm, ContactMethods.CONTENT_IM_ITEM_TYPE});
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
Organizations.CONTENT_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
People.CONTENT_URI,
|
||||
People._ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return contactIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method so we don't duplicate code in doQuery
|
||||
* @param searchTerm
|
||||
* @param contactIds
|
||||
* @param uri
|
||||
* @param projection
|
||||
* @param selection
|
||||
* @param selectionArgs
|
||||
*/
|
||||
private void doQuery(String searchTerm, Set<String> contactIds,
|
||||
Uri uri, String projection, String selection, String[] selectionArgs) {
|
||||
ContentResolver cr = mApp.getContentResolver();
|
||||
|
||||
Cursor cursor = cr.query(
|
||||
uri,
|
||||
null,
|
||||
selection,
|
||||
selectionArgs,
|
||||
null);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
contactIds.add(cursor.getString(cursor.getColumnIndex(projection)));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactFields
|
||||
*/
|
||||
private JSONArray imQuery(ContentResolver cr, String contactId) {
|
||||
String imWhere = ContactMethods.PERSON_ID
|
||||
+ " = ? AND " + ContactMethods.KIND + " = ?";
|
||||
String[] imWhereParams = new String[]{contactId, ContactMethods.CONTENT_IM_ITEM_TYPE};
|
||||
Cursor cursor = cr.query(ContactMethods.CONTENT_URI,
|
||||
null, imWhere, imWhereParams, null);
|
||||
JSONArray ims = new JSONArray();
|
||||
JSONObject im;
|
||||
while (cursor.moveToNext()) {
|
||||
im = new JSONObject();
|
||||
try{
|
||||
im.put("primary", false);
|
||||
im.put("value", cursor.getString(
|
||||
cursor.getColumnIndex(ContactMethodsColumns.DATA)));
|
||||
im.put("type", cursor.getString(
|
||||
cursor.getColumnIndex(ContactMethodsColumns.TYPE)));
|
||||
ims.put(im);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactOrganization JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactOrganization
|
||||
*/
|
||||
private JSONArray organizationQuery(ContentResolver cr, String contactId) {
|
||||
String orgWhere = ContactMethods.PERSON_ID + " = ?";
|
||||
String[] orgWhereParams = new String[]{contactId};
|
||||
Cursor cursor = cr.query(Organizations.CONTENT_URI,
|
||||
null, orgWhere, orgWhereParams, null);
|
||||
JSONArray organizations = new JSONArray();
|
||||
JSONObject organization;
|
||||
while (cursor.moveToNext()) {
|
||||
organization = new JSONObject();
|
||||
try{
|
||||
organization.put("name", cursor.getString(cursor.getColumnIndex(Organizations.COMPANY)));
|
||||
organization.put("title", cursor.getString(cursor.getColumnIndex(Organizations.TITLE)));
|
||||
// organization.put("department", cursor.getString(cursor.getColumnIndex(Organizations)));
|
||||
// organization.put("description", cursor.getString(cursor.getColumnIndex(Organizations)));
|
||||
// organization.put("endDate", cursor.getString(cursor.getColumnIndex(Organizations)));
|
||||
// organization.put("location", cursor.getString(cursor.getColumnIndex(Organizations)));
|
||||
// organization.put("startDate", cursor.getString(cursor.getColumnIndex(Organizations)));
|
||||
organizations.put(organization);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return organizations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactAddress JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactAddress
|
||||
*/
|
||||
private JSONArray addressQuery(ContentResolver cr, String contactId) {
|
||||
String addrWhere = ContactMethods.PERSON_ID
|
||||
+ " = ? AND " + ContactMethods.KIND + " = ?";
|
||||
String[] addrWhereParams = new String[]{contactId,
|
||||
ContactMethods.CONTENT_POSTAL_ITEM_TYPE};
|
||||
Cursor cursor = cr.query(ContactMethods.CONTENT_URI,
|
||||
null, addrWhere, addrWhereParams, null);
|
||||
JSONArray addresses = new JSONArray();
|
||||
JSONObject address;
|
||||
while (cursor.moveToNext()) {
|
||||
address = new JSONObject();
|
||||
try{
|
||||
address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactMethodsColumns.DATA)));
|
||||
addresses.put(address);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactFields
|
||||
*/
|
||||
private JSONArray phoneQuery(ContentResolver cr, String contactId) {
|
||||
Cursor cursor = cr.query(
|
||||
Phones.CONTENT_URI,
|
||||
null,
|
||||
Phones.PERSON_ID +" = ?",
|
||||
new String[]{contactId}, null);
|
||||
JSONArray phones = new JSONArray();
|
||||
JSONObject phone;
|
||||
while (cursor.moveToNext()) {
|
||||
phone = new JSONObject();
|
||||
try{
|
||||
phone.put("primary", false);
|
||||
phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER)));
|
||||
phone.put("type", cursor.getString(cursor.getColumnIndex(Phones.TYPE)));
|
||||
phones.put(phone);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return phones;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactFields
|
||||
*/
|
||||
private JSONArray emailQuery(ContentResolver cr, String contactId) {
|
||||
Cursor cursor = cr.query(
|
||||
ContactMethods.CONTENT_EMAIL_URI,
|
||||
null,
|
||||
ContactMethods.PERSON_ID +" = ?",
|
||||
new String[]{contactId}, null);
|
||||
JSONArray emails = new JSONArray();
|
||||
JSONObject email;
|
||||
while (cursor.moveToNext()) {
|
||||
email = new JSONObject();
|
||||
try{
|
||||
email.put("primary", false);
|
||||
email.put("value", cursor.getString(cursor.getColumnIndex(ContactMethods.DATA)));
|
||||
// TODO Find out why adding an email type throws and exception
|
||||
//email.put("type", cursor.getString(cursor.getColumnIndex(ContactMethods.TYPE)));
|
||||
emails.put(email);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return emails;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(JSONObject contact) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* This method will remove a Contact from the database based on ID.
|
||||
* @param id the unique ID of the contact to remove
|
||||
*/
|
||||
public boolean remove(String id) {
|
||||
int result = mApp.getContentResolver().delete(People.CONTENT_URI,
|
||||
"people._id = ?",
|
||||
new String[] {id});
|
||||
|
||||
return (result > 0) ? true : false;
|
||||
}
|
||||
}
|
||||
632
framework/src/com/phonegap/ContactAccessorSdk5.java
Normal file
632
framework/src/com/phonegap/ContactAccessorSdk5.java
Normal file
@@ -0,0 +1,632 @@
|
||||
// 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.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.database.Cursor;
|
||||
import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ContactAccessor} that uses current Contacts API.
|
||||
* This class should be used on Eclair or beyond, but would not work on any earlier
|
||||
* release of Android. As a matter of fact, it could not even be loaded.
|
||||
* <p>
|
||||
* This implementation has several advantages:
|
||||
* <ul>
|
||||
* <li>It sees contacts from multiple accounts.
|
||||
* <li>It works with aggregated contacts. So for example, if the contact is the result
|
||||
* of aggregation of two raw contacts from different accounts, it may return the name from
|
||||
* one and the phone number from the other.
|
||||
* <li>It is efficient because it uses the more efficient current API.
|
||||
* <li>Not obvious in this particular example, but it has access to new kinds
|
||||
* of data available exclusively through the new APIs. Exercise for the reader: add support
|
||||
* for nickname (see {@link android.provider.ContactsContract.CommonDataKinds.Nickname}) or
|
||||
* social status updates (see {@link android.provider.ContactsContract.StatusUpdates}).
|
||||
* </ul>
|
||||
*/
|
||||
public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
|
||||
/**
|
||||
* A static map that converts the JavaScript property name to Android database column name.
|
||||
*/
|
||||
private static final Map<String, String> dbMap = new HashMap<String, String>();
|
||||
static {
|
||||
dbMap.put("id", ContactsContract.Contacts._ID);
|
||||
dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME);
|
||||
dbMap.put("name", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
|
||||
dbMap.put("name.formatted", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
|
||||
dbMap.put("name.familyName", ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
|
||||
dbMap.put("name.givenName", ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
|
||||
dbMap.put("name.middleName", ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME);
|
||||
dbMap.put("name.honorificPrefix", ContactsContract.CommonDataKinds.StructuredName.PREFIX);
|
||||
dbMap.put("name.honorificSuffix", ContactsContract.CommonDataKinds.StructuredName.SUFFIX);
|
||||
dbMap.put("nickname", ContactsContract.CommonDataKinds.Nickname.NAME);
|
||||
dbMap.put("phoneNumbers", ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
dbMap.put("phoneNumbers.value", ContactsContract.CommonDataKinds.Phone.NUMBER);
|
||||
dbMap.put("emails", ContactsContract.CommonDataKinds.Email.DATA);
|
||||
dbMap.put("emails.value", ContactsContract.CommonDataKinds.Email.DATA);
|
||||
dbMap.put("addresses", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
|
||||
dbMap.put("addresses.formatted", ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
|
||||
dbMap.put("addresses.streetAddress", ContactsContract.CommonDataKinds.StructuredPostal.STREET);
|
||||
dbMap.put("addresses.locality", ContactsContract.CommonDataKinds.StructuredPostal.CITY);
|
||||
dbMap.put("addresses.region", ContactsContract.CommonDataKinds.StructuredPostal.REGION);
|
||||
dbMap.put("addresses.postalCode", ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE);
|
||||
dbMap.put("addresses.country", ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY);
|
||||
dbMap.put("ims", ContactsContract.CommonDataKinds.Im.DATA);
|
||||
dbMap.put("ims.value", ContactsContract.CommonDataKinds.Im.DATA);
|
||||
dbMap.put("organizations", ContactsContract.CommonDataKinds.Organization.COMPANY);
|
||||
dbMap.put("organizations.name", ContactsContract.CommonDataKinds.Organization.COMPANY);
|
||||
dbMap.put("organizations.department", ContactsContract.CommonDataKinds.Organization.DEPARTMENT);
|
||||
dbMap.put("organizations.title", ContactsContract.CommonDataKinds.Organization.TITLE);
|
||||
dbMap.put("organizations.location", ContactsContract.CommonDataKinds.Organization.OFFICE_LOCATION);
|
||||
dbMap.put("organizations.description", ContactsContract.CommonDataKinds.Organization.JOB_DESCRIPTION);
|
||||
//dbMap.put("published", null);
|
||||
//dbMap.put("updated", null);
|
||||
dbMap.put("birthday", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
|
||||
dbMap.put("anniversary", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
|
||||
//dbMap.put("gender", null);
|
||||
dbMap.put("note", ContactsContract.CommonDataKinds.Note.NOTE);
|
||||
//dbMap.put("preferredUsername", null);
|
||||
//dbMap.put("photos.value", null);
|
||||
//dbMap.put("tags.value", null);
|
||||
dbMap.put("relationships", ContactsContract.CommonDataKinds.Relation.NAME);
|
||||
dbMap.put("relationships.value", ContactsContract.CommonDataKinds.Relation.NAME);
|
||||
dbMap.put("urls", ContactsContract.CommonDataKinds.Website.URL);
|
||||
dbMap.put("urls.value", ContactsContract.CommonDataKinds.Website.URL);
|
||||
//dbMap.put("accounts.domain", null);
|
||||
//dbMap.put("accounts.username", null);
|
||||
//dbMap.put("accounts.userid", null);
|
||||
//dbMap.put("utcOffset", null);
|
||||
//dbMap.put("connected", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an contact accessor.
|
||||
*/
|
||||
public ContactAccessorSdk5(WebView view, Activity app) {
|
||||
mApp = app;
|
||||
mView = view;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes the fields required and search options in order to produce an
|
||||
* array of contacts that matches the criteria provided.
|
||||
* @param fields an array of items to be used as search criteria
|
||||
* @param options that can be applied to contact searching
|
||||
* @return an array of contacts
|
||||
*/
|
||||
@Override
|
||||
public JSONArray search(JSONArray fields, JSONObject options) {
|
||||
long totalEnd;
|
||||
long totalStart = System.currentTimeMillis();
|
||||
|
||||
// Get the find options
|
||||
String searchTerm = "";
|
||||
int limit = 1;
|
||||
boolean multiple = false;
|
||||
try {
|
||||
searchTerm = options.getString("filter");
|
||||
if (searchTerm.length()==0) {
|
||||
searchTerm = "%";
|
||||
}
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (multiple) {
|
||||
limit = options.getInt("limit");
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Loop through the fields the user provided to see what data should be returned.
|
||||
HashMap<String,Boolean> populate = buildPopulationSet(fields);
|
||||
|
||||
// Build the ugly where clause and where arguments for one big query.
|
||||
WhereOptions whereOptions = buildWhereClause(fields, searchTerm);
|
||||
|
||||
// Get all the rows where the search term matches the fields passed in.
|
||||
Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
null,
|
||||
whereOptions.getWhere(),
|
||||
whereOptions.getWhereArgs(),
|
||||
ContactsContract.Data.CONTACT_ID + " ASC");
|
||||
|
||||
String contactId = "";
|
||||
String oldContactId = "";
|
||||
boolean newContact = true;
|
||||
String mimetype = "";
|
||||
|
||||
JSONArray contacts = new JSONArray();
|
||||
JSONObject contact = new JSONObject();
|
||||
JSONArray organizations = new JSONArray();
|
||||
JSONArray addresses = new JSONArray();
|
||||
JSONArray phones = new JSONArray();
|
||||
JSONArray emails = new JSONArray();
|
||||
JSONArray ims = new JSONArray();
|
||||
JSONArray websites = new JSONArray();
|
||||
JSONArray relationships = new JSONArray();
|
||||
|
||||
while (c.moveToNext() && (contacts.length() < (limit-1))) {
|
||||
try {
|
||||
contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
|
||||
|
||||
// If we are in the first row set the oldContactId
|
||||
if (c.getPosition() == 0) {
|
||||
oldContactId = contactId;
|
||||
}
|
||||
|
||||
// When the contact ID changes we need to push the Contact object
|
||||
// to the array of contacts and create new objects.
|
||||
if (!oldContactId.equals(contactId)) {
|
||||
// Populate the Contact object with it's arrays
|
||||
// and push the contact into the contacts array
|
||||
contacts.put(populateContact(contact, organizations, addresses, phones,
|
||||
emails, ims, websites, relationships));
|
||||
|
||||
// Clean up the objects
|
||||
contact = new JSONObject();
|
||||
organizations = new JSONArray();
|
||||
addresses = new JSONArray();
|
||||
phones = new JSONArray();
|
||||
emails = new JSONArray();
|
||||
ims = new JSONArray();
|
||||
websites = new JSONArray();
|
||||
relationships = new JSONArray();
|
||||
|
||||
// Set newContact to true as we are starting to populate a new contact
|
||||
newContact = true;
|
||||
}
|
||||
|
||||
// When we detect a new contact set the ID and display name.
|
||||
// These fields are available in every row in the result set returned.
|
||||
if (newContact) {
|
||||
newContact = false;
|
||||
contact.put("id", contactId);
|
||||
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
|
||||
}
|
||||
|
||||
// Grab the mimetype of the current row as it will be used in a lot of comparisons
|
||||
mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE));
|
||||
|
||||
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("name",populate)) {
|
||||
contact.put("name", nameQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("phoneNumbers",populate)) {
|
||||
phones.put(phoneQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("emails",populate)) {
|
||||
emails.put(emailQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("addresses",populate)) {
|
||||
addresses.put(addressQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("organizations",populate)) {
|
||||
organizations.put(organizationQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("ims",populate)) {
|
||||
ims.put(imQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("note",populate)) {
|
||||
contact.put("note",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE)));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("nickname",populate)) {
|
||||
contact.put("nickname",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("urls",populate)) {
|
||||
websites.put(websiteQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("relationships",populate)) {
|
||||
relationships.put(relationshipQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) {
|
||||
if (ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
|
||||
&& isRequired("anniversary",populate)) {
|
||||
contact.put("anniversary", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
|
||||
}
|
||||
else if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
|
||||
&& isRequired("birthday",populate)) {
|
||||
contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(),e);
|
||||
}
|
||||
|
||||
// Set the old contact ID
|
||||
oldContactId = contactId;
|
||||
}
|
||||
c.close();
|
||||
|
||||
// Push the last contact into the contacts array
|
||||
contacts.put(populateContact(contact, organizations, addresses, phones,
|
||||
emails, ims, websites, relationships));
|
||||
|
||||
totalEnd = System.currentTimeMillis();
|
||||
Log.d(LOG_TAG,"Total time = " + (totalEnd-totalStart));
|
||||
return contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new contact using a JSONObject to hold all the data.
|
||||
* @param contact
|
||||
* @param organizations array of organizations
|
||||
* @param addresses array of addresses
|
||||
* @param phones array of phones
|
||||
* @param emails array of emails
|
||||
* @param ims array of instant messenger addresses
|
||||
* @param websites array of websites
|
||||
* @param relationships array of relationships
|
||||
* @return
|
||||
*/
|
||||
private JSONObject populateContact(JSONObject contact, JSONArray organizations,
|
||||
JSONArray addresses, JSONArray phones, JSONArray emails,
|
||||
JSONArray ims, JSONArray websites, JSONArray relationships) {
|
||||
try {
|
||||
contact.put("organizations", organizations);
|
||||
contact.put("addresses", addresses);
|
||||
contact.put("phoneNumbers", phones);
|
||||
contact.put("emails", emails);
|
||||
contact.put("ims", ims);
|
||||
contact.put("websites", websites);
|
||||
contact.put("relationships", relationships);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG,e.getMessage(),e);
|
||||
}
|
||||
return contact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take the search criteria passed into the method and create a SQL WHERE clause.
|
||||
* @param fields the properties to search against
|
||||
* @param searchTerm the string to search for
|
||||
* @return an object containing the selection and selection args
|
||||
*/
|
||||
private WhereOptions buildWhereClause(JSONArray fields, String searchTerm) {
|
||||
|
||||
ArrayList<String> where = new ArrayList<String>();
|
||||
ArrayList<String> whereArgs = new ArrayList<String>();
|
||||
|
||||
WhereOptions options = new WhereOptions();
|
||||
|
||||
/*
|
||||
* Special case for when the user wants all the contacts
|
||||
*/
|
||||
if ("%".equals(searchTerm)) {
|
||||
options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )");
|
||||
options.setWhereArgs(new String[] {searchTerm});
|
||||
return options;
|
||||
}
|
||||
|
||||
String key;
|
||||
try {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
|
||||
if (key.startsWith("displayName")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
}
|
||||
else if (key.startsWith("name")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("nickname")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
// else if (key.startsWith("birthday")) {
|
||||
// where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
// + ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
// }
|
||||
// else if (key.startsWith("anniversary")) {
|
||||
// where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
// + ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
// whereArgs.add(searchTerm);
|
||||
// whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
|
||||
// }
|
||||
else if (key.startsWith("note")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("relationships")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
else if (key.startsWith("urls")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Creating the where string
|
||||
StringBuffer selection = new StringBuffer();
|
||||
for (int i=0; i<where.size(); i++) {
|
||||
selection.append(where.get(i));
|
||||
if (i != (where.size()-1)) {
|
||||
selection.append(" OR ");
|
||||
}
|
||||
}
|
||||
options.setWhere(selection.toString());
|
||||
|
||||
// Creating the where args array
|
||||
String[] selectionArgs = new String[whereArgs.size()];
|
||||
for (int i=0; i<whereArgs.size(); i++) {
|
||||
selectionArgs[i] = whereArgs.get(i);
|
||||
}
|
||||
options.setWhereArgs(selectionArgs);
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactOrganization JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactOrganization
|
||||
*/
|
||||
private JSONObject organizationQuery(Cursor cursor) {
|
||||
JSONObject organization = new JSONObject();
|
||||
try {
|
||||
organization.put("department", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.DEPARTMENT)));
|
||||
organization.put("description", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.JOB_DESCRIPTION)));
|
||||
// TODO No endDate
|
||||
// organization.put("endDate", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization)));
|
||||
organization.put("location", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.OFFICE_LOCATION)));
|
||||
organization.put("name", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY)));
|
||||
// TODO no startDate
|
||||
// organization.put("startDate", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization)));
|
||||
organization.put("title", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TITLE)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return organization;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactAddress JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactAddress
|
||||
*/
|
||||
private JSONObject addressQuery(Cursor cursor) {
|
||||
JSONObject address = new JSONObject();
|
||||
try {
|
||||
address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS)));
|
||||
address.put("streetAddress", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.STREET)));
|
||||
address.put("locality", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY)));
|
||||
address.put("region", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.REGION)));
|
||||
address.put("postalCode", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE)));
|
||||
address.put("country", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactName JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactName
|
||||
*/
|
||||
private JSONObject nameQuery(Cursor cursor) {
|
||||
JSONObject contactName = new JSONObject();
|
||||
try {
|
||||
String familyName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME));
|
||||
String givenName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME));
|
||||
String middleName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME));
|
||||
String honorificPrefix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.PREFIX));
|
||||
String honorificSuffix = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.SUFFIX));
|
||||
|
||||
// Create the formatted name
|
||||
StringBuffer formatted = new StringBuffer("");
|
||||
if (honorificPrefix != null) { formatted.append(honorificPrefix + " "); }
|
||||
if (givenName != null) { formatted.append(givenName + " "); }
|
||||
if (middleName != null) { formatted.append(middleName + " "); }
|
||||
if (familyName != null) { formatted.append(familyName + " "); }
|
||||
if (honorificSuffix != null) { formatted.append(honorificSuffix + " "); }
|
||||
|
||||
contactName.put("familyName", familyName);
|
||||
contactName.put("givenName", givenName);
|
||||
contactName.put("middleName", middleName);
|
||||
contactName.put("honorificPrefix", honorificPrefix);
|
||||
contactName.put("honorificSuffix", honorificSuffix);
|
||||
contactName.put("formatted", formatted);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return contactName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactField
|
||||
*/
|
||||
private JSONObject phoneQuery(Cursor cursor) {
|
||||
JSONObject phoneNumber = new JSONObject();
|
||||
try {
|
||||
phoneNumber.put("primary", false); // Android does not store primary attribute
|
||||
phoneNumber.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
|
||||
phoneNumber.put("type", cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
catch (Exception excp) {
|
||||
Log.e(LOG_TAG, excp.getMessage(), excp);
|
||||
}
|
||||
return phoneNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactField
|
||||
*/
|
||||
private JSONObject emailQuery(Cursor cursor) {
|
||||
JSONObject email = new JSONObject();
|
||||
try {
|
||||
email.put("primary", false); // Android does not store primary attribute
|
||||
email.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.DATA)));
|
||||
email.put("type", cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.TYPE)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactField
|
||||
*/
|
||||
private JSONObject imQuery(Cursor cursor) {
|
||||
JSONObject im = new JSONObject();
|
||||
try {
|
||||
im.put("primary", false); // Android does not store primary attribute
|
||||
im.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.DATA)));
|
||||
im.put("type", cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Im.TYPE)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return im;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactField
|
||||
*/
|
||||
private JSONObject websiteQuery(Cursor cursor) {
|
||||
JSONObject website = new JSONObject();
|
||||
try {
|
||||
website.put("primary", false); // Android does not store primary attribute
|
||||
website.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.URL)));
|
||||
website.put("type", cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Website.TYPE)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return website;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactField
|
||||
*/
|
||||
private JSONObject relationshipQuery(Cursor cursor) {
|
||||
JSONObject relationship = new JSONObject();
|
||||
try {
|
||||
relationship.put("primary", false); // Android does not store primary attribute
|
||||
relationship.put("value", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Relation.NAME)));
|
||||
relationship.put("type", cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Relation.TYPE)));
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return relationship;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(JSONObject contact) {
|
||||
// TODO Auto-generated method stub
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* This method will remove a Contact from the database based on ID.
|
||||
* @param id the unique ID of the contact to remove
|
||||
*/
|
||||
public boolean remove(String id) {
|
||||
int result = mApp.getContentResolver().delete(ContactsContract.Data.CONTENT_URI,
|
||||
ContactsContract.Data.CONTACT_ID + " = ?",
|
||||
new String[] {id});
|
||||
return (result > 0) ? true : false;
|
||||
}
|
||||
}
|
||||
324
framework/src/com/phonegap/ContactManager.java
Normal file → Executable file
324
framework/src/com/phonegap/ContactManager.java
Normal file → Executable file
@@ -1,291 +1,67 @@
|
||||
/*
|
||||
* 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 static 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());
|
||||
}
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public ContactManager() {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
if (contactAccessor == null) {
|
||||
contactAccessor = ContactAccessor.getInstance(webView, ctx);
|
||||
}
|
||||
catch (SQLiteException ex)
|
||||
{
|
||||
Log.d(LOG_TAG, ex.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void searchPeople(String name, String number)
|
||||
{
|
||||
String conditions = "";
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
if (name.length() == 0)
|
||||
{
|
||||
name = "%";
|
||||
conditions += People.NAME + " LIKE ? AND ";
|
||||
try {
|
||||
if (action.equals("search")) {
|
||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.getJSONObject(1));
|
||||
return new PluginResult(status, res);
|
||||
}
|
||||
else
|
||||
{
|
||||
conditions += People.NAME + " = ? AND ";
|
||||
else if (action.equals("save")) {
|
||||
// TODO Coming soon!
|
||||
}
|
||||
|
||||
if (number.length() == 0)
|
||||
number = "%";
|
||||
else
|
||||
{
|
||||
number = number.replace('+', '%');
|
||||
number = number.replace('.', '%');
|
||||
number = number.replace('-', '%');
|
||||
else if (action.equals("remove")) {
|
||||
if (contactAccessor.remove(args.getString(0))) {
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
else {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("code", 2);
|
||||
return new PluginResult(PluginResult.Status.ERROR, r);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
return new PluginResult(status, result);
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
173
framework/src/com/phonegap/Device.java
Executable file
173
framework/src/com/phonegap/Device.java
Executable file
@@ -0,0 +1,173 @@
|
||||
/*
|
||||
* 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.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "0.9.2"; // 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(DroidGap 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 getLine1Number(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getLine1Number();
|
||||
}
|
||||
|
||||
public String getDeviceId(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getDeviceId();
|
||||
}
|
||||
|
||||
public String getSimSerialNumber(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSimSerialNumber();
|
||||
}
|
||||
|
||||
public String getSubscriberId(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,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.File;
|
||||
@@ -6,26 +13,47 @@ 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){
|
||||
/**
|
||||
* 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,44 +62,82 @@ 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){
|
||||
/**
|
||||
* Create directory on SD card.
|
||||
*
|
||||
* @param directoryName The name of the directory to create.
|
||||
* @return T=successful, F=failed
|
||||
*/
|
||||
protected static boolean createDirectory(String directoryName) {
|
||||
boolean status;
|
||||
if ((testSaveLocationExists())&&(!directoryName.equals(""))){
|
||||
|
||||
// Make sure SD card exists
|
||||
if ((testSaveLocationExists()) && (!directoryName.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), directoryName);
|
||||
status = newPath.mkdir();
|
||||
status = true;
|
||||
}else
|
||||
}
|
||||
|
||||
// If no SD card or invalid dir name
|
||||
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
|
||||
}
|
||||
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected boolean deleteDirectory(String fileName){
|
||||
/**
|
||||
* Delete directory.
|
||||
*
|
||||
* @param fileName The name of the directory to delete
|
||||
* @return T=deleted, F=could not delete
|
||||
*/
|
||||
protected static boolean deleteDirectory(String fileName) {
|
||||
boolean status;
|
||||
SecurityManager checker = new SecurityManager();
|
||||
|
||||
if ((testSaveLocationExists())&&(!fileName.equals(""))){
|
||||
|
||||
// Make sure SD card exists
|
||||
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), fileName);
|
||||
checker.checkDelete(newPath.toString());
|
||||
if(newPath.isDirectory()){
|
||||
|
||||
// If dir to delete is really a directory
|
||||
if (newPath.isDirectory()) {
|
||||
String[] listfile = newPath.list();
|
||||
// delete all files within the specified directory and then delete the directory
|
||||
|
||||
// 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());
|
||||
@@ -80,27 +146,43 @@ public class DirectoryManager {
|
||||
newPath.delete();
|
||||
Log.i("DirectoryManager deleteDirectory", fileName);
|
||||
status = true;
|
||||
}catch (Exception e){
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
status = false;
|
||||
}
|
||||
|
||||
}else
|
||||
}
|
||||
|
||||
// If dir not a directory, then error
|
||||
else {
|
||||
status = false;
|
||||
}else
|
||||
}
|
||||
}
|
||||
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
protected boolean deleteFile(String fileName){
|
||||
/**
|
||||
* Delete file.
|
||||
*
|
||||
* @param fileName The name of the file to delete
|
||||
* @return T=deleted, F=not deleted
|
||||
*/
|
||||
protected static boolean deleteFile(String fileName) {
|
||||
boolean status;
|
||||
SecurityManager checker = new SecurityManager();
|
||||
|
||||
if ((testSaveLocationExists())&&(!fileName.equals(""))){
|
||||
|
||||
// Make sure SD card exists
|
||||
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), fileName);
|
||||
checker.checkDelete(newPath.toString());
|
||||
|
||||
// If file to delete is really a file
|
||||
if (newPath.isFile()){
|
||||
try {
|
||||
Log.i("DirectoryManager deleteFile", fileName);
|
||||
@@ -110,14 +192,28 @@ public class DirectoryManager {
|
||||
se.printStackTrace();
|
||||
status = false;
|
||||
}
|
||||
}else
|
||||
}
|
||||
// If not a file, then error
|
||||
else {
|
||||
status = false;
|
||||
}else
|
||||
}
|
||||
}
|
||||
|
||||
// If no SD card
|
||||
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);
|
||||
return newPath;
|
||||
|
||||
843
framework/src/com/phonegap/DroidGap.java
Normal file → Executable file
843
framework/src/com/phonegap/DroidGap.java
Normal file → Executable file
@@ -1,34 +1,24 @@
|
||||
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.Plugin;
|
||||
import com.phonegap.api.PluginManager;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -43,254 +33,404 @@ import android.webkit.WebSettings;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.os.Build.*;
|
||||
|
||||
/**
|
||||
* This class is the main Android activity that represents the PhoneGap
|
||||
* application. It should be extended by the user to load the specific
|
||||
* html file that contains the application.
|
||||
*
|
||||
* As an example:
|
||||
*
|
||||
* package com.phonegap.examples;
|
||||
* import android.app.Activity;
|
||||
* import android.os.Bundle;
|
||||
* import com.phonegap.*;
|
||||
*
|
||||
* public class Examples extends DroidGap {
|
||||
* @Override
|
||||
* public void onCreate(Bundle savedInstanceState) {
|
||||
* super.onCreate(savedInstanceState);
|
||||
* super.loadUrl("file:///android_asset/www/index.html");
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
public class DroidGap extends Activity {
|
||||
|
||||
private static final String LOG_TAG = "DroidGap";
|
||||
protected WebView appView;
|
||||
private LinearLayout root;
|
||||
|
||||
private PhoneGap gap;
|
||||
private GeoBroker geo;
|
||||
private AccelBroker accel;
|
||||
private CameraLauncher launcher;
|
||||
private ContactManager mContacts;
|
||||
private FileUtils fs;
|
||||
private NetworkManager netMan;
|
||||
private CompassListener mCompass;
|
||||
private Storage cupcakeStorage;
|
||||
private CryptoHandler crypto;
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
|
||||
private static final String LOG_TAG = "DroidGap";
|
||||
|
||||
protected WebView appView; // The webview for our app
|
||||
protected ImageView splashScreen;
|
||||
protected Boolean loadInWebView = false;
|
||||
private LinearLayout root;
|
||||
|
||||
private BrowserKey mKey;
|
||||
public CallbackServer callbackServer;
|
||||
protected PluginManager pluginManager;
|
||||
|
||||
private String url; // The initial URL for our app
|
||||
private String baseUrl; // The base of the initial URL for our app
|
||||
|
||||
private Plugin activityResultCallback = null; // Plugin to call when activity result is received
|
||||
|
||||
/**
|
||||
* Called when the activity is first created.
|
||||
*
|
||||
* @param savedInstanceState
|
||||
*/
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
|
||||
|
||||
LinearLayout.LayoutParams containerParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F);
|
||||
|
||||
LinearLayout.LayoutParams webviewParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 1.0F);
|
||||
|
||||
|
||||
root = new LinearLayout(this);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(Color.BLACK);
|
||||
root.setLayoutParams(containerParams);
|
||||
|
||||
appView = new WebView(this);
|
||||
appView.setLayoutParams(webviewParams);
|
||||
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT, 0.0F));
|
||||
|
||||
/*
|
||||
splashScreen = new ImageView(this);
|
||||
splashScreen.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
1.0F));
|
||||
splashScreen.setImageResource(R.drawable.splash);
|
||||
|
||||
root.addView(splashScreen);
|
||||
*/
|
||||
|
||||
initWebView();
|
||||
root.addView(this.appView);
|
||||
setContentView(root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and initialize web container.
|
||||
*/
|
||||
private void initWebView() {
|
||||
|
||||
// Create web container
|
||||
this.appView = new WebView(DroidGap.this);
|
||||
|
||||
this.appView.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
ViewGroup.LayoutParams.FILL_PARENT,
|
||||
1.0F));
|
||||
|
||||
WebViewReflect.checkCompatibility();
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("2."))
|
||||
appView.setWebChromeClient(new EclairClient(this));
|
||||
else
|
||||
{
|
||||
appView.setWebChromeClient(new GapClient(this));
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("2.")) {
|
||||
this.appView.setWebChromeClient(new EclairClient(DroidGap.this));
|
||||
}
|
||||
|
||||
appView.setWebViewClient(new GapViewClient(this));
|
||||
|
||||
appView.setInitialScale(100);
|
||||
appView.setVerticalScrollBarEnabled(false);
|
||||
|
||||
WebSettings settings = appView.getSettings();
|
||||
else {
|
||||
this.appView.setWebChromeClient(new GapClient(DroidGap.this));
|
||||
}
|
||||
|
||||
this.appView.setWebViewClient(new GapViewClient(this));
|
||||
|
||||
this.appView.setInitialScale(100);
|
||||
this.appView.setVerticalScrollBarEnabled(false);
|
||||
this.appView.requestFocusFromTouch();
|
||||
|
||||
// Enable JavaScript
|
||||
WebSettings settings = this.appView.getSettings();
|
||||
settings.setJavaScriptEnabled(true);
|
||||
settings.setJavaScriptCanOpenWindowsAutomatically(true);
|
||||
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
|
||||
|
||||
Package pack = this.getClass().getPackage();
|
||||
String appPackage = pack.getName();
|
||||
|
||||
// Enable database
|
||||
Package pack = this.getClass().getPackage();
|
||||
String appPackage = pack.getName();
|
||||
WebViewReflect.setStorage(settings, true, "/data/data/" + appPackage + "/app_database/");
|
||||
|
||||
// Turn on DOM storage!
|
||||
|
||||
// Enable DOM storage
|
||||
WebViewReflect.setDomStorage(settings);
|
||||
// Turn off native geolocation object in browser - we use our own :)
|
||||
WebViewReflect.setGeolocationEnabled(settings, false);
|
||||
/* Bind the appView object to the gap class methods */
|
||||
bindBrowser(appView);
|
||||
if(cupcakeStorage != null)
|
||||
cupcakeStorage.setStorage(appPackage);
|
||||
|
||||
root.addView(appView);
|
||||
|
||||
setContentView(root);
|
||||
}
|
||||
// Enable built-in geolocation
|
||||
WebViewReflect.setGeolocationEnabled(settings, true);
|
||||
|
||||
// Bind PhoneGap objects to JavaScript
|
||||
this.bindBrowser(this.appView);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called by the system when the device configuration changes while your activity is running.
|
||||
*
|
||||
* @param Configuration newConfig
|
||||
*/
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
//don't reload the current page when the orientation is changed
|
||||
super.onConfigurationChanged(newConfig);
|
||||
//don't reload the current page when the orientation is changed
|
||||
super.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*/
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onPause();
|
||||
|
||||
// Send pause event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.appView.pauseTimers();
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*/
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onResume();
|
||||
|
||||
// Send resume event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};");
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
|
||||
private void bindBrowser(WebView appView)
|
||||
{
|
||||
gap = new PhoneGap(this, appView);
|
||||
geo = new GeoBroker(appView, this);
|
||||
accel = new AccelBroker(appView, this);
|
||||
launcher = new CameraLauncher(appView, this);
|
||||
mContacts = new ContactManager(this, appView);
|
||||
fs = new FileUtils(appView);
|
||||
netMan = new NetworkManager(this, appView);
|
||||
mCompass = new CompassListener(this, appView);
|
||||
crypto = new CryptoHandler(appView);
|
||||
@Override
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// This creates the new javascript interfaces for PhoneGap
|
||||
appView.addJavascriptInterface(gap, "DroidGap");
|
||||
appView.addJavascriptInterface(geo, "Geo");
|
||||
appView.addJavascriptInterface(accel, "Accel");
|
||||
appView.addJavascriptInterface(launcher, "GapCam");
|
||||
appView.addJavascriptInterface(mContacts, "ContactHook");
|
||||
appView.addJavascriptInterface(fs, "FileUtil");
|
||||
appView.addJavascriptInterface(netMan, "NetworkManager");
|
||||
appView.addJavascriptInterface(mCompass, "CompassHook");
|
||||
appView.addJavascriptInterface(crypto, "GapCrypto");
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
// Clean up objects
|
||||
if (this.mKey != null) {
|
||||
}
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1."))
|
||||
{
|
||||
cupcakeStorage = new Storage(appView);
|
||||
appView.addJavascriptInterface(cupcakeStorage, "droidStorage");
|
||||
// Forward to plugins
|
||||
this.pluginManager.onDestroy();
|
||||
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void loadUrl(String url)
|
||||
{
|
||||
appView.loadUrl(url);
|
||||
}
|
||||
|
||||
public class GapViewClient extends WebViewClient {
|
||||
|
||||
Context mCtx;
|
||||
|
||||
public GapViewClient(Context ctx)
|
||||
{
|
||||
mCtx = ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see android.webkit.WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView, java.lang.String)
|
||||
*
|
||||
* Note: Since we override it to make sure that we are using PhoneGap and not some other bullshit
|
||||
* viewer that may or may not exist, we need to make sure that http:// and tel:// still work.
|
||||
*
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
// TODO: See about using a switch statement
|
||||
if (url.startsWith("http://"))
|
||||
{
|
||||
Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(browse);
|
||||
return true;
|
||||
}
|
||||
else if(url.startsWith("tel://"))
|
||||
{
|
||||
Intent dial = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
|
||||
startActivity(dial);
|
||||
return true;
|
||||
}
|
||||
else if(url.startsWith("sms:"))
|
||||
{
|
||||
Uri smsUri = Uri.parse(url);
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, smsUri);
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
view.loadUrl(url);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
public void addService(String serviceType, String className) {
|
||||
this.pluginManager.addService(serviceType, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind PhoneGap objects to JavaScript.
|
||||
*
|
||||
* @param appView
|
||||
*/
|
||||
private void bindBrowser(WebView appView) {
|
||||
this.callbackServer = new CallbackServer();
|
||||
this.pluginManager = new PluginManager(appView, this);
|
||||
this.mKey = new BrowserKey(appView, this);
|
||||
|
||||
// This creates the new javascript interfaces for PhoneGap
|
||||
appView.addJavascriptInterface(this.pluginManager, "PluginManager");
|
||||
|
||||
appView.addJavascriptInterface(this.mKey, "BackButton");
|
||||
|
||||
appView.addJavascriptInterface(this.callbackServer, "CallbackServer");
|
||||
appView.addJavascriptInterface(new SplashScreen(this), "SplashScreen");
|
||||
|
||||
|
||||
this.addService("Geolocation", "com.phonegap.GeoBroker");
|
||||
this.addService("Device", "com.phonegap.Device");
|
||||
this.addService("Accelerometer", "com.phonegap.AccelListener");
|
||||
this.addService("Compass", "com.phonegap.CompassListener");
|
||||
this.addService("Media", "com.phonegap.AudioHandler");
|
||||
this.addService("Camera", "com.phonegap.CameraLauncher");
|
||||
this.addService("Contacts", "com.phonegap.ContactManager");
|
||||
this.addService("Crypto", "com.phonegap.CryptoHandler");
|
||||
this.addService("File", "com.phonegap.FileUtils");
|
||||
this.addService("Location", "com.phonegap.GeoBroker"); // Always add Location, even though it is built-in on 2.x devices. Let JavaScript decide which one to use.
|
||||
this.addService("Network Status", "com.phonegap.NetworkManager");
|
||||
this.addService("Notification", "com.phonegap.Notification");
|
||||
this.addService("Storage", "com.phonegap.Storage");
|
||||
this.addService("Temperature", "com.phonegap.TempListener");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url
|
||||
*/
|
||||
public void loadUrl(final String url) {
|
||||
this.url = url;
|
||||
int i = url.lastIndexOf('/');
|
||||
if (i > 0) {
|
||||
this.baseUrl = url.substring(0, i);
|
||||
}
|
||||
else {
|
||||
this.baseUrl = this.url;
|
||||
}
|
||||
|
||||
this.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
DroidGap.this.appView.loadUrl(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send JavaScript statement back to JavaScript.
|
||||
*
|
||||
* @param message
|
||||
*/
|
||||
public void sendJavascript(String statement) {
|
||||
this.callbackServer.sendJavascript(statement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the port that the callback server is listening on.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public int getPort() {
|
||||
return this.callbackServer.getPort();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides a hook for calling "alert" from javascript. Useful for
|
||||
* debugging your javascript.
|
||||
*/
|
||||
public class GapClient extends WebChromeClient {
|
||||
|
||||
Context mCtx;
|
||||
public GapClient(Context ctx)
|
||||
{
|
||||
mCtx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
|
||||
Log.d(LOG_TAG, message);
|
||||
// This shows the dialog box. This can be commented out for dev
|
||||
AlertDialog.Builder alertBldr = new AlertDialog.Builder(mCtx);
|
||||
GapOKDialog okHook = new GapOKDialog();
|
||||
GapCancelDialog cancelHook = new GapCancelDialog();
|
||||
alertBldr.setMessage(message);
|
||||
alertBldr.setTitle("Alert");
|
||||
alertBldr.setCancelable(true);
|
||||
alertBldr.setPositiveButton("OK", okHook);
|
||||
alertBldr.setNegativeButton("Cancel", cancelHook);
|
||||
alertBldr.show();
|
||||
result.confirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the Code for the OK Button
|
||||
*/
|
||||
|
||||
public class GapOKDialog implements DialogInterface.OnClickListener {
|
||||
/**
|
||||
* Provides a hook for calling "alert" from javascript. Useful for
|
||||
* debugging your javascript.
|
||||
*/
|
||||
public class GapClient extends WebChromeClient {
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// TODO Auto-generated method stub
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class GapCancelDialog implements DialogInterface.OnClickListener {
|
||||
private Context ctx;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
*/
|
||||
public GapClient(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
// TODO Auto-generated method stub
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to display a javascript alert dialog.
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param result
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
|
||||
Log.d(LOG_TAG, message);
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle("Alert");
|
||||
dlg.setCancelable(false);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new AlertDialog.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.confirm();
|
||||
}
|
||||
});
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public final class EclairClient extends GapClient
|
||||
{
|
||||
/**
|
||||
* Tell the client to display a confirm dialog to the user.
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param result
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
|
||||
dlg.setMessage(message);
|
||||
dlg.setTitle("Confirm");
|
||||
dlg.setCancelable(false);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.confirm();
|
||||
}
|
||||
});
|
||||
dlg.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
result.cancel();
|
||||
}
|
||||
});
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WebChromeClient that extends GapClient with additional support for Android 2.X
|
||||
*/
|
||||
public final class EclairClient extends GapClient {
|
||||
|
||||
private String TAG = "PhoneGapLog";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
|
||||
public EclairClient(Context ctx) {
|
||||
super(ctx);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
*/
|
||||
public EclairClient(Context ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle database quota exceeded notification.
|
||||
*
|
||||
* @param url
|
||||
* @param databaseIdentifier
|
||||
* @param currentQuota
|
||||
* @param estimatedSize
|
||||
* @param totalUsedQuota
|
||||
* @param quotaUpdater
|
||||
*/
|
||||
@Override
|
||||
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long currentQuota, long estimatedSize,
|
||||
long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater)
|
||||
{
|
||||
Log.d(TAG, "event raised onExceededDatabaseQuota estimatedSize: " + Long.toString(estimatedSize) + " currentQuota: " + Long.toString(currentQuota) + " totalUsedQuota: " + Long.toString(totalUsedQuota));
|
||||
|
||||
|
||||
if( estimatedSize < MAX_QUOTA)
|
||||
{
|
||||
//increase for 1Mb
|
||||
long newQuota = currentQuota + 1024*1024;
|
||||
long newQuota = estimatedSize;
|
||||
Log.d(TAG, "calling quotaUpdater.updateQuota newQuota: " + Long.toString(newQuota) );
|
||||
quotaUpdater.updateQuota(newQuota);
|
||||
}
|
||||
@@ -301,74 +441,237 @@ public class DroidGap extends Activity {
|
||||
quotaUpdater.updateQuota(currentQuota);
|
||||
}
|
||||
}
|
||||
|
||||
// This is a test of console.log, because we don't have this in Android 2.01
|
||||
public void addMessageToConsole(String message, int lineNumber, String sourceID)
|
||||
{
|
||||
Log.d(TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
|
||||
}
|
||||
|
||||
// console.log in api level 7: http://developer.android.com/guide/developing/debug-tasks.html
|
||||
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
||||
{
|
||||
Log.d(TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
|
||||
}
|
||||
@Override
|
||||
public void onConsoleMessage(String message, int lineNumber, String sourceID)
|
||||
{
|
||||
// This is a kludgy hack!!!!
|
||||
Log.d(TAG, sourceID + ": Line " + Integer.toString(lineNumber) + " : " + message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
||||
// TODO Auto-generated method stub
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
callback.invoke(origin, true, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The webview client receives notifications about appView
|
||||
*/
|
||||
public class GapViewClient extends WebViewClient {
|
||||
|
||||
// TODO: hide splash screen here
|
||||
|
||||
DroidGap ctx;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param ctx
|
||||
*/
|
||||
public GapViewClient(DroidGap ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Give the host application a chance to take over the control when a new url
|
||||
* is about to be loaded in the current WebView.
|
||||
*
|
||||
* @param view The WebView that is initiating the callback.
|
||||
* @param url The url to be loaded.
|
||||
* @return true to override, false for default behavior
|
||||
*/
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
|
||||
// If dialing phone (tel:5551212)
|
||||
if (url.startsWith(WebView.SCHEME_TEL)) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_DIAL);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error dialing "+url+": "+ e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If displaying map (geo:0,0?q=address)
|
||||
else if (url.startsWith(WebView.SCHEME_GEO)) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error showing map "+url+": "+ e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If sending email (mailto:abc@corp.com)
|
||||
else if (url.startsWith(WebView.SCHEME_MAILTO)) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error sending email "+url+": "+ e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If sms:5551212
|
||||
else if (url.startsWith("sms:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
intent.putExtra("address", url.substring(4));
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error sending sms "+url+":"+ e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// If http, https or file
|
||||
else if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://")) {
|
||||
|
||||
int i = url.lastIndexOf('/');
|
||||
String newBaseUrl = url;
|
||||
if (i > 0) {
|
||||
newBaseUrl = url.substring(0, i);
|
||||
}
|
||||
|
||||
// If our app or file:, then load into our webview
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || this.ctx.baseUrl.equals(newBaseUrl)) {
|
||||
this.ctx.appView.loadUrl(url);
|
||||
}
|
||||
|
||||
// If not our application, let default viewer handle
|
||||
else {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error loading url "+url+":"+ e.toString());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the host application that a page has finished loading.
|
||||
*
|
||||
* @param view The webview initiating the callback.
|
||||
* @param url The url of the page.
|
||||
*/
|
||||
@Override
|
||||
public void onPageFinished (WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
// Try firing the onNativeReady event in JS. If it fails because the JS is
|
||||
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
||||
// from the JS side when the JS gets to that code.
|
||||
appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event)
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
String testUrl = appView.getUrl();
|
||||
appView.goBack();
|
||||
if(appView.getUrl() == testUrl)
|
||||
{
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
{
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
if (mKey.isBound())
|
||||
{
|
||||
//We fire an event here!
|
||||
appView.loadUrl("javascript:document.keyEvent.backTrigger()");
|
||||
}
|
||||
else
|
||||
{
|
||||
// only go back if the webview tells you that it is possible to go back
|
||||
if(appView.canGoBack())
|
||||
{
|
||||
appView.goBack();
|
||||
}
|
||||
else // if you can't go back, invoke behavior of super class
|
||||
{
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_MENU)
|
||||
{
|
||||
// This is where we launch the menu
|
||||
appView.loadUrl("javascript:keyEvent.menuTrigger()");
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_SEARCH)
|
||||
{
|
||||
appView.loadUrl("javascript:keyEvent.searchTrigger()");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is required to start the camera activity! It has to come from the previous activity
|
||||
public void startCamera(int quality)
|
||||
{
|
||||
Intent i = new Intent(this, CameraPreview.class);
|
||||
i.setAction("android.intent.action.PICK");
|
||||
i.putExtra("quality", quality);
|
||||
startActivityForResult(i, 0);
|
||||
/**
|
||||
* Removes the splash screen from root view and adds the WebView
|
||||
*/
|
||||
public void hideSplashScreen() {
|
||||
root.removeView(splashScreen);
|
||||
root.addView(this.appView);
|
||||
}
|
||||
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
|
||||
{
|
||||
String data;
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
if (resultCode == RESULT_OK)
|
||||
{
|
||||
data = intent.getStringExtra("picture");
|
||||
// Send the graphic back to the class that needs it
|
||||
launcher.processPicture(data);
|
||||
/**
|
||||
* Any calls to Activity.startActivityForResult must use method below, so
|
||||
* the result can be routed to them correctly.
|
||||
*
|
||||
* This is done to eliminate the need to modify DroidGap.java to receive activity results.
|
||||
*
|
||||
* @param intent The intent to start
|
||||
* @param requestCode Identifies who to send the result to
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException {
|
||||
System.out.println("startActivityForResult(intent,"+requestCode+")");
|
||||
if (requestCode == -1) {
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
launcher.failPicture("Did not complete!");
|
||||
else {
|
||||
throw new RuntimeException("PhoneGap Exception: Call startActivityForResult(Command, Intent) instead.");
|
||||
}
|
||||
}
|
||||
|
||||
public WebView getView()
|
||||
{
|
||||
return this.appView;
|
||||
/**
|
||||
* Launch an activity for which you would like a result when it finished. When this activity exits,
|
||||
* your onActivityResult() method will be called.
|
||||
*
|
||||
* @param command The command object
|
||||
* @param intent The intent to start
|
||||
* @param requestCode The request code that is passed to callback to identify the activity
|
||||
*/
|
||||
public void startActivityForResult(Plugin command, Intent intent, int requestCode) {
|
||||
this.activityResultCallback = command;
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 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").
|
||||
*/
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
Plugin callback = this.activityResultCallback;
|
||||
if (callback != null) {
|
||||
callback.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
328
framework/src/com/phonegap/FileUtils.java
Normal file → Executable file
328
framework/src/com/phonegap/FileUtils.java
Normal file → Executable file
@@ -1,126 +1,244 @@
|
||||
/*
|
||||
* 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.*;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import android.webkit.WebView;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class FileUtils {
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
/**
|
||||
* This class provides SD card file and directory services to JavaScript.
|
||||
* Only files on the SD card can be accessed.
|
||||
*/
|
||||
public class FileUtils extends Plugin {
|
||||
public static int NOT_FOUND_ERR = 1;
|
||||
public static int SECURITY_ERR = 2;
|
||||
public static int ABORT_ERR = 3;
|
||||
|
||||
public static int NOT_READABLE_ERR = 4;
|
||||
public static int ENCODING_ERR = 5;
|
||||
public static int NO_MODIFICATION_ALLOWED_ERR = 6;
|
||||
public static int INVALID_STATE_ERR = 7;
|
||||
public static int SYNTAX_ERR = 8;
|
||||
|
||||
WebView mView;
|
||||
DirectoryManager fileManager;
|
||||
FileReader f_in;
|
||||
FileWriter f_out;
|
||||
|
||||
FileUtils(WebView view)
|
||||
{
|
||||
mView = view;
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public FileUtils() {
|
||||
}
|
||||
|
||||
public int testSaveLocationExists(){
|
||||
if (fileManager.testSaveLocationExists())
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
public long getFreeDiskSpace(){
|
||||
long freeDiskSpace=fileManager.getFreeDiskSpace();
|
||||
return freeDiskSpace;
|
||||
}
|
||||
|
||||
public int testFileExists(String file){
|
||||
if (fileManager.testFileExists(file))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
public int testDirectoryExists(String file){
|
||||
if (fileManager.testFileExists(file))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a specific directory.
|
||||
* Everyting in side the directory would be gone.
|
||||
* TODO: JavaScript Call backs for success and error handling
|
||||
/**
|
||||
* 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 int deleteDirectory (String dir){
|
||||
if (fileManager.deleteDirectory(dir))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a specific file.
|
||||
* TODO: JavaScript Call backs for success and error handling
|
||||
*/
|
||||
public int deleteFile (String file){
|
||||
if (fileManager.deleteFile(file))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a new directory.
|
||||
* TODO: JavaScript Call backs for success and error handling
|
||||
*/
|
||||
public int createDirectory(String dir){
|
||||
if (fileManager.createDirectory(dir))
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
public String read(String filename)
|
||||
{
|
||||
String data = "";
|
||||
String output = "";
|
||||
try {
|
||||
FileInputStream fstream = new FileInputStream(filename);
|
||||
DataInputStream in = new DataInputStream(fstream);
|
||||
while (in.available() !=0)
|
||||
{
|
||||
data += in.readLine();
|
||||
}
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
data = "FAIL: File not found";
|
||||
} catch (IOException e) {
|
||||
data = "FAIL: IO ERROR";
|
||||
}
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
//System.out.println("FileUtils.execute("+action+")");
|
||||
|
||||
//mView.loadUrl("javascript:navigator.FileReader.hasRead('" + data + "')");
|
||||
try {
|
||||
if (action.equals("testSaveLocationExists")) {
|
||||
boolean b = DirectoryManager.testSaveLocationExists();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("getFreeDiskSpace")) {
|
||||
long l = DirectoryManager.getFreeDiskSpace();
|
||||
return new PluginResult(status, l);
|
||||
}
|
||||
else if (action.equals("testFileExists")) {
|
||||
boolean b = DirectoryManager.testFileExists(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("testDirectoryExists")) {
|
||||
boolean b = DirectoryManager.testFileExists(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("deleteDirectory")) {
|
||||
boolean b = DirectoryManager.deleteDirectory(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("deleteFile")) {
|
||||
boolean b = DirectoryManager.deleteFile(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("createDirectory")) {
|
||||
boolean b = DirectoryManager.createDirectory(args.getString(0));
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("readAsText")) {
|
||||
try {
|
||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, s);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
try {
|
||||
String s = this.readAsDataURL(args.getString(0));
|
||||
return new PluginResult(status, s);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("writeAsText")) {
|
||||
try {
|
||||
this.writeAsText(args.getString(0), args.getString(1), args.getBoolean(2));
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("truncate")) {
|
||||
try {
|
||||
this.truncateFile(args.getString(0), args.getLong(1));
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_FOUND_ERR);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
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("readAsText")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("writeAsText")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Read content of text file.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @param encoding The encoding to return contents as. Typical value is UTF-8.
|
||||
* (see http://www.iana.org/assignments/character-sets)
|
||||
* @return Contents of file.
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
public String readAsText(String filename, String encoding) throws FileNotFoundException, IOException {
|
||||
StringBuilder data = new StringBuilder();
|
||||
FileInputStream fis = new FileInputStream(filename);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(fis, encoding), 1024);
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
data.append(line);
|
||||
}
|
||||
return data.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read content of text file and return as base64 encoded data url.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @return Contents of file = data:<media type>;base64,<data>
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
public String readAsDataURL(String filename) throws FileNotFoundException, IOException {
|
||||
byte[] bytes = new byte[1000];
|
||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filename), 1024);
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
int numRead = 0;
|
||||
while ((numRead = bis.read(bytes, 0, 1000)) >= 0) {
|
||||
bos.write(bytes, 0, numRead);
|
||||
}
|
||||
|
||||
// Determine content type from file name
|
||||
// TODO
|
||||
String contentType = "";
|
||||
|
||||
byte[] base64 = Base64.encodeBase64(bos.toByteArray());
|
||||
String data = "data:" + contentType + ";base64," + new String(base64);
|
||||
return data;
|
||||
}
|
||||
|
||||
public int write(String filename, String data, boolean append)
|
||||
{
|
||||
String FilePath= filename;
|
||||
try {
|
||||
byte [] rawData = data.getBytes();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
|
||||
FileOutputStream out= new FileOutputStream(FilePath, append);
|
||||
byte buff[] = new byte[rawData.length];
|
||||
in.read(buff, 0, buff.length);
|
||||
out.write(buff, 0, rawData.length);
|
||||
out.flush();
|
||||
out.close();
|
||||
//mView.loadUrl("javascript:navigator.FileReader.onsuccess('File written')");
|
||||
} catch (Exception e) {
|
||||
//mView.loadUrl("javascript:navigator.FileReader.onerror('Fail')");
|
||||
// So, do we just return -1 at this point!
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
/**
|
||||
* Write contents of file.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @param data The contents of the file.
|
||||
* @param append T=append, F=overwrite
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
public void writeAsText(String filename, String data, boolean append) throws FileNotFoundException, IOException {
|
||||
String FilePath= filename;
|
||||
byte [] rawData = data.getBytes();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
|
||||
FileOutputStream out= new FileOutputStream(FilePath, append);
|
||||
byte buff[] = new byte[rawData.length];
|
||||
in.read(buff, 0, buff.length);
|
||||
out.write(buff, 0, rawData.length);
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Truncate the file to size
|
||||
*
|
||||
* @param filename
|
||||
* @param size
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
private void truncateFile(String filename, long size) throws FileNotFoundException, IOException {
|
||||
RandomAccessFile raf = new RandomAccessFile(filename, "rw");
|
||||
|
||||
if (raf.length() >= size) {
|
||||
FileChannel channel = raf.getChannel();
|
||||
channel.truncate(size);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
178
framework/src/com/phonegap/GpsListener.java
Normal file → Executable file
178
framework/src/com/phonegap/GpsListener.java
Normal file → Executable file
@@ -1,101 +1,149 @@
|
||||
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 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 DroidGap mCtx; // DroidGap 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(DroidGap 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();
|
||||
|
||||
174
framework/src/com/phonegap/NetworkListener.java
Normal file → Executable file
174
framework/src/com/phonegap/NetworkListener.java
Normal file → Executable file
@@ -1,102 +1,140 @@
|
||||
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 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 DroidGap mCtx; // DroidGap 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(DroidGap 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
155
framework/src/com/phonegap/NetworkManager.java
Normal file → Executable file
155
framework/src/com/phonegap/NetworkManager.java
Normal file → Executable file
@@ -1,57 +1,152 @@
|
||||
/*
|
||||
* 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.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.*;
|
||||
import android.webkit.WebView;
|
||||
|
||||
public class NetworkManager {
|
||||
|
||||
Context mCtx;
|
||||
WebView mView;
|
||||
ConnectivityManager sockMan;
|
||||
public class NetworkManager extends Plugin {
|
||||
|
||||
NetworkManager(Context ctx, WebView view)
|
||||
{
|
||||
mCtx = ctx;
|
||||
mView = view;
|
||||
sockMan = (ConnectivityManager) mCtx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
|
||||
ConnectivityManager sockMan;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NetworkManager() {
|
||||
}
|
||||
|
||||
public boolean isAvailable()
|
||||
{
|
||||
|
||||
/**
|
||||
* 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(DroidGap ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_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("isAvailable")) {
|
||||
boolean b = this.isAvailable();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("isWifiActive")) {
|
||||
boolean b = this.isWifiActive();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("isReachable")) {
|
||||
int i = this.isReachable(args.getString(0), args.getBoolean(1));
|
||||
return new PluginResult(status, i);
|
||||
}
|
||||
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) {
|
||||
// All methods take a while, so always use async
|
||||
return false;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if a network connection exists.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
boolean conn = false;
|
||||
if(info != null)
|
||||
if (info != null) {
|
||||
conn = info.isConnected();
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
public boolean isWifiActive()
|
||||
{
|
||||
/**
|
||||
* Determine if a WIFI connection exists.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isWifiActive() {
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
if(info != null)
|
||||
{
|
||||
if (info != null) {
|
||||
String type = info.getTypeName();
|
||||
return type.equals("WIFI");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isReachable(String uri)
|
||||
{
|
||||
if (uri.indexOf("http://") == -1)
|
||||
/**
|
||||
* Determine if a URI is reachable over the network.
|
||||
*
|
||||
* @param uri
|
||||
* @param isIpAddress
|
||||
* @return
|
||||
*/
|
||||
public int isReachable(String uri, boolean isIpAddress) {
|
||||
int reachable = NOT_REACHABLE;
|
||||
|
||||
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;
|
||||
}
|
||||
return reached;
|
||||
|
||||
if (this.isAvailable()) {
|
||||
try {
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
HttpGet httpget = new HttpGet(uri);
|
||||
httpclient.execute(httpget);
|
||||
|
||||
if (this.isWifiActive()) {
|
||||
reachable = REACHABLE_VIA_WIFI_NETWORK;
|
||||
}
|
||||
else {
|
||||
reachable = REACHABLE_VIA_CARRIER_DATA_NETWORK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reachable = NOT_REACHABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return reachable;
|
||||
}
|
||||
}
|
||||
|
||||
354
framework/src/com/phonegap/Notification.java
Executable file
354
framework/src/com/phonegap/Notification.java
Executable file
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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.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 DroidGap 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 DroidGap 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 DroidGap 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 DroidGap 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
21
framework/src/com/phonegap/SplashScreen.java
Normal file
21
framework/src/com/phonegap/SplashScreen.java
Normal file
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class SplashScreen {
|
||||
private final DroidGap gap;
|
||||
public SplashScreen(DroidGap gap) {
|
||||
this.gap = gap;
|
||||
}
|
||||
public void hide() {
|
||||
gap.runOnUiThread(new Runnable() {
|
||||
public void run() {
|
||||
gap.hideSplashScreen();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
236
framework/src/com/phonegap/Storage.java
Normal file → Executable file
236
framework/src/com/phonegap/Storage.java
Normal file → Executable file
@@ -1,78 +1,194 @@
|
||||
/*
|
||||
* 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 {
|
||||
|
||||
private static final String LOG_TAG = "SQLite Storage:";
|
||||
SQLiteDatabase myDb;
|
||||
String path;
|
||||
String txid = "";
|
||||
WebView appView;
|
||||
Context mCtx;
|
||||
SQLiteDatabase myDb = null; // Database object
|
||||
String path = null; // Database path
|
||||
String dbName = null; // Database name
|
||||
|
||||
Storage(WebView view)
|
||||
{
|
||||
appView = view;
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public Storage() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")) {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
String[] 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public void setStorage(String appPackage)
|
||||
{
|
||||
path = "/data/data/" + appPackage + "/databases/";
|
||||
/**
|
||||
* 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/";
|
||||
}
|
||||
|
||||
public void openDatabase(String db, String version, String display_name, long size)
|
||||
{
|
||||
if (path != null)
|
||||
{
|
||||
path += db + ".db";
|
||||
myDb = SQLiteDatabase.openOrCreateDatabase(path, null);
|
||||
/**
|
||||
* 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 {
|
||||
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 + "');");
|
||||
}
|
||||
}
|
||||
|
||||
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 + ")");
|
||||
}
|
||||
}
|
||||
|
||||
public void processResults(Cursor cur)
|
||||
{
|
||||
String key = "";
|
||||
String value = "";
|
||||
String resultString = "";
|
||||
/**
|
||||
* Process query results.
|
||||
*
|
||||
* @param cur Cursor into query results
|
||||
* @param tx_id Transaction id
|
||||
*/
|
||||
public void processResults(Cursor cur, String tx_id) {
|
||||
|
||||
// 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();
|
||||
}
|
||||
String key = "";
|
||||
String value = "";
|
||||
int colCount = cur.getColumnCount();
|
||||
|
||||
// Build up JSON result object for each row
|
||||
do {
|
||||
JSONObject result = new JSONObject();
|
||||
try {
|
||||
for (int i = 0; i < colCount; ++i) {
|
||||
key = cur.getColumnName(i);
|
||||
value = cur.getString(i).replace("\"", "\\\""); // must escape " with \" for JavaScript
|
||||
result.put(key, value);
|
||||
}
|
||||
|
||||
// Send row back to JavaScript
|
||||
this.sendJavascript("droiddb.addResult('" + result.toString() + "','" + tx_id + "');");
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
} while (cur.moveToNext());
|
||||
|
||||
}
|
||||
// Let JavaScript know that there are no more rows
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "');");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,53 +1,100 @@
|
||||
/*
|
||||
* 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.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(DroidGap 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;
|
||||
|
||||
82
framework/src/com/phonegap/api/IPlugin.java
Executable file
82
framework/src/com/phonegap/api/IPlugin.java
Executable file
@@ -0,0 +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, IBM Corporation
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import com.phonegap.DroidGap;
|
||||
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(DroidGap 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.
|
||||
*/
|
||||
void onPause();
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*/
|
||||
void onResume();
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
}
|
||||
128
framework/src/com/phonegap/api/Plugin.java
Executable file
128
framework/src/com/phonegap/api/Plugin.java
Executable file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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 com.phonegap.DroidGap;
|
||||
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 WebView webView; // WebView object
|
||||
public DroidGap ctx; // DroidGap 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(DroidGap 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.
|
||||
*/
|
||||
public void onPause() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*/
|
||||
public void onResume() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
}
|
||||
269
framework/src/com/phonegap/api/PluginManager.java
Executable file
269
framework/src/com/phonegap/api/PluginManager.java
Executable file
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* 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.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.webkit.WebView;
|
||||
import com.phonegap.DroidGap;
|
||||
|
||||
/**
|
||||
* 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, Plugin> plugins = new HashMap<String,Plugin>();
|
||||
private HashMap<String, String> services = new HashMap<String,String>();
|
||||
|
||||
private final DroidGap ctx;
|
||||
private final WebView app;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param app
|
||||
* @param ctx
|
||||
*/
|
||||
public PluginManager(WebView app, DroidGap ctx) {
|
||||
this.ctx = ctx;
|
||||
this.app = app;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Plugin plugin = this.addPlugin(clazz, c);
|
||||
final DroidGap 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);
|
||||
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
|
||||
* @return The plugin
|
||||
*/
|
||||
public Plugin addPlugin(String className) {
|
||||
try {
|
||||
return this.addPlugin(className, this.getClassByName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Error adding plugin "+className+".");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Plugin addPlugin(String className, Class clazz) {
|
||||
if (this.plugins.containsKey(className)) {
|
||||
return this.getPlugin(className);
|
||||
}
|
||||
try {
|
||||
Plugin plugin = (Plugin)clazz.newInstance();
|
||||
this.plugins.put(className, plugin);
|
||||
plugin.setContext((DroidGap)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 Plugin getPlugin(String className) {
|
||||
Plugin 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.
|
||||
*/
|
||||
public void onPause() {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onPause();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*/
|
||||
public void onResume() {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
106
framework/src/com/phonegap/api/PluginResult.java
Executable file
106
framework/src/com/phonegap/api/PluginResult.java
Executable file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
public class PluginResult {
|
||||
private final int status;
|
||||
private final String message;
|
||||
private boolean keepCallback = false;
|
||||
|
||||
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 = "'" + message + "'";
|
||||
}
|
||||
|
||||
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) {
|
||||
return "PhoneGap.callbackSuccess('"+callbackId+"', " + this.getJSONString() + " );";
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
128
droidgap → lib/classic.rb
Executable file → Normal file
128
droidgap → lib/classic.rb
Executable file → Normal file
@@ -1,18 +1,14 @@
|
||||
#!/usr/bin/env ruby
|
||||
require 'fileutils'
|
||||
|
||||
# ./droidgap /Users/brianleroux/Code/android-sdk-mac MyApp com.westcoastlogic example /Users/brianleroux/Desktop/MyApp
|
||||
class Build
|
||||
class Classic
|
||||
attr_reader :android_sdk_path, :name, :pkg, :www, :path
|
||||
|
||||
def initialize(*a)
|
||||
|
||||
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")
|
||||
build
|
||||
end
|
||||
|
||||
# runs the build script
|
||||
def run
|
||||
def build
|
||||
setup
|
||||
clobber
|
||||
build_jar
|
||||
create_android
|
||||
include_www
|
||||
@@ -20,13 +16,25 @@ class Build
|
||||
copy_libs
|
||||
add_name_to_strings
|
||||
write_java
|
||||
puts "Complete!"
|
||||
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')
|
||||
@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
|
||||
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
|
||||
@@ -39,78 +47,74 @@ class Build
|
||||
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.
|
||||
# TODO need to allow more flexible SDK targetting via config.xml
|
||||
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 }`
|
||||
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
|
||||
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: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 framework into the project
|
||||
# TODO need to allow for www import inc icon
|
||||
# copies stuff from src directory into the android project directory (@path)
|
||||
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")
|
||||
|
||||
# copies in the jar
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.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")
|
||||
|
||||
# drops in the layout files: main.xml and preview.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "layout")
|
||||
%w(main.xml preview.xml).each do |f|
|
||||
%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
|
||||
# if it is not in the www directory use the default one in the src dir
|
||||
@icon = File.join(framework_res_dir, "drawable", "icon.png") unless File.exists?(@icon)
|
||||
%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")
|
||||
FileUtils.mkdir_p(File.join(app_res_dir, e))
|
||||
FileUtils.cp(@icon, File.join(app_res_dir, e, "icon.png"))
|
||||
end
|
||||
|
||||
# concat JS and put into www folder.
|
||||
# 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.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "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>
|
||||
@@ -125,7 +129,6 @@ class Build
|
||||
# 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 };
|
||||
|
||||
@@ -133,47 +136,24 @@ class Build
|
||||
import android.os.Bundle;
|
||||
import com.phonegap.*;
|
||||
|
||||
public class #{ @name } extends DroidGap
|
||||
public class #{ @name.gsub(' ','') } extends DroidGap
|
||||
{
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
super.loadUrl(\"file:///android_asset/www/index.html\");
|
||||
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.gsub(' ','') }
|
||||
open(File.join(code_dir, "#{ @name }.java"),'w') { |f| f.puts j }
|
||||
end
|
||||
|
||||
# friendly output for now
|
||||
def msg
|
||||
puts "Created #{ @path }"
|
||||
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
|
||||
72
lib/create.rb
Normal file
72
lib/create.rb
Normal file
@@ -0,0 +1,72 @@
|
||||
# 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"]
|
||||
|
||||
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[:icon] = n.attributes["src"] if n.name == 'icon'
|
||||
@config[:content] = n.attributes["src"] if n.name == 'content'
|
||||
|
||||
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])
|
||||
# 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
|
||||
54
lib/update.rb
Normal file
54
lib/update.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/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
|
||||
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
|
||||
|
||||
# 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..."
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs")
|
||||
|
||||
# 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
|
||||
#
|
||||
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