mirror of
https://github.com/apache/cordova-android.git
synced 2026-01-30 00:05:28 +08:00
Compare commits
62 Commits
0.9.3
...
WebSockets
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9270d13981 | ||
|
|
83e89909ed | ||
|
|
98d51b18e2 | ||
|
|
28f27e89e4 | ||
|
|
8ede3b4cc9 | ||
|
|
939b70243d | ||
|
|
d44bb7a9d8 | ||
|
|
dccc29daf2 | ||
|
|
b402efd1f7 | ||
|
|
0c3a8fb9f7 | ||
|
|
64d4337d5f | ||
|
|
a9e1751812 | ||
|
|
2bc7bd6768 | ||
|
|
1711fb07d7 | ||
|
|
6f4673f590 | ||
|
|
5e858f8bc3 | ||
|
|
691b093ccd | ||
|
|
99002f9dce | ||
|
|
b07072c12b | ||
|
|
36dd964ba4 | ||
|
|
f848527c28 | ||
|
|
6aa055f46e | ||
|
|
7952668cf7 | ||
|
|
a0c761664d | ||
|
|
9fd9cf55cf | ||
|
|
3c9089b9c7 | ||
|
|
f220489543 | ||
|
|
b65f9517db | ||
|
|
1a0de5f626 | ||
|
|
3c0bef6cc1 | ||
|
|
040194157f | ||
|
|
7ebf8130e4 | ||
|
|
64310dc85c | ||
|
|
cd2e86af2f | ||
|
|
b353f3608d | ||
|
|
cda154209d | ||
|
|
e3c72fa915 | ||
|
|
9354b429f3 | ||
|
|
b1f0c037bd | ||
|
|
726f1094d9 | ||
|
|
1b8ab156df | ||
|
|
ee01b5058f | ||
|
|
03ea8a0b5a | ||
|
|
f090f9a70c | ||
|
|
b7abc2c344 | ||
|
|
53bdf2dd6b | ||
|
|
b9e1b1d280 | ||
|
|
9051b157f8 | ||
|
|
f16d9b01b7 | ||
|
|
2a9bc2ddf8 | ||
|
|
6e39c46b07 | ||
|
|
567ca94245 | ||
|
|
812a4b32b4 | ||
|
|
023df10f31 | ||
|
|
8d513e2765 | ||
|
|
1eae6786c4 | ||
|
|
73f278963b | ||
|
|
54eff557d9 | ||
|
|
a7415bcfc9 | ||
|
|
b6bd9ad5b8 | ||
|
|
f71d9deb5e | ||
|
|
115b428a9d |
@@ -31,7 +31,7 @@ Commands:
|
||||
|
||||
<pre>
|
||||
help ...... See this message. Type help [command name] to see specific help topics.
|
||||
gen ....... Generate an example PhoneGap application to current directory.
|
||||
gen ....... Generate the example PhoneGap application to current directory (or optionally provide an output directory as parameter).
|
||||
create .... Creates an Android compatible project from a WWW folder.
|
||||
classic ... Backwards support for droidgap script. Run "droidgap help classic" for more info.
|
||||
update .... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project.
|
||||
@@ -41,8 +41,8 @@ Commands:
|
||||
Quickstart:
|
||||
|
||||
<pre>
|
||||
$ droidgap gen example
|
||||
$ cd example
|
||||
$ droidgap gen exampleapp
|
||||
$ cd exampleapp
|
||||
$ ant debug install && adb logcat
|
||||
</pre>
|
||||
|
||||
|
||||
12
bin/droidgap
12
bin/droidgap
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env ruby
|
||||
ROOT = File.expand_path(File.dirname(__FILE__).gsub('bin',''))
|
||||
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")
|
||||
@@ -60,7 +60,7 @@ if ARGV.first.nil? || ARGV.first == 'help'
|
||||
Commands:
|
||||
|
||||
help ...... See this message. Type help [command name] to see specific help topics.
|
||||
gen ....... Generate an example PhoneGap application to current directory.
|
||||
gen ....... Generate the example PhoneGap application to current directory (or optionally provide an output directory as parameter).
|
||||
create .... Creates an Android compatible project from a WWW folder.
|
||||
classic ... Backwards support for droidgap script. Run "droidgap help classic" for more info.
|
||||
update .... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project.
|
||||
@@ -68,8 +68,8 @@ if ARGV.first.nil? || ARGV.first == 'help'
|
||||
|
||||
Quickstart:
|
||||
|
||||
$ droidgap gen example
|
||||
$ cd example
|
||||
$ droidgap gen exampleapp
|
||||
$ cd exampleapp
|
||||
$ ant debug install && adb logcat
|
||||
|
||||
EOF
|
||||
@@ -79,11 +79,13 @@ if ARGV.first.nil? || ARGV.first == 'help'
|
||||
DroidGap Generate
|
||||
-----------------
|
||||
|
||||
Generate an example PhoneGap application to path supplied or current working directory if none is supplied.
|
||||
Generate the example PhoneGap application to path supplied or current working directory if none is supplied.
|
||||
|
||||
Usage:
|
||||
|
||||
droidgap gen [path]
|
||||
|
||||
NOTE: Do *not* run "droidgap gen example" - you will end up with a recursive directory problem.
|
||||
|
||||
EOF
|
||||
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
<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" src="phonegap.0.9.4.min.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
<h1>Welcome to PhoneGap!</h1>
|
||||
<h2>this file is located at assets/index.html</h2>
|
||||
<h2>this file is located at assets/www/index.html</h2>
|
||||
<div id="info">
|
||||
<h4>Platform: <span id="platform"> </span>, Version: <span id="version"> </span></h4>
|
||||
<h4>UUID: <span id="uuid"> </span>, Name: <span id="name"> </span></h4>
|
||||
@@ -29,7 +29,8 @@
|
||||
<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>
|
||||
<a href="#" class="btn large" onclick="get_contacts();">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();">Check Network</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
|
||||
241
example/main.js
241
example/main.js
@@ -1,127 +1,140 @@
|
||||
var deviceInfo = function(){
|
||||
document.getElementById("platform").innerHTML = device.platform;
|
||||
document.getElementById("version").innerHTML = device.version;
|
||||
document.getElementById("uuid").innerHTML = device.uuid;
|
||||
document.getElementById("name").innerHTML = device.name;
|
||||
document.getElementById("width").innerHTML = screen.width;
|
||||
document.getElementById("height").innerHTML = screen.height;
|
||||
document.getElementById("colorDepth").innerHTML = screen.colorDepth;
|
||||
var deviceInfo = function() {
|
||||
document.getElementById("platform").innerHTML = device.platform;
|
||||
document.getElementById("version").innerHTML = device.version;
|
||||
document.getElementById("uuid").innerHTML = device.uuid;
|
||||
document.getElementById("name").innerHTML = device.name;
|
||||
document.getElementById("width").innerHTML = screen.width;
|
||||
document.getElementById("height").innerHTML = screen.height;
|
||||
document.getElementById("colorDepth").innerHTML = screen.colorDepth;
|
||||
};
|
||||
|
||||
var getLocation = function() {
|
||||
var suc = function(p) {
|
||||
alert(p.coords.latitude + " " + p.coords.longitude);
|
||||
};
|
||||
|
||||
var getLocation = function() {
|
||||
var suc = function(p){
|
||||
alert(p.coords.latitude + " " + p.coords.longitude);
|
||||
};
|
||||
var fail = function(){};
|
||||
navigator.geolocation.getCurrentPosition(suc,fail);
|
||||
var locFail = function() {
|
||||
};
|
||||
|
||||
var beep = function(){
|
||||
navigator.notification.beep(2);
|
||||
};
|
||||
|
||||
var vibrate = function(){
|
||||
navigator.notification.vibrate(0);
|
||||
};
|
||||
navigator.geolocation.getCurrentPosition(suc, locFail);
|
||||
};
|
||||
|
||||
function roundNumber(num) {
|
||||
var dec = 3;
|
||||
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
|
||||
return result;
|
||||
var beep = function() {
|
||||
navigator.notification.beep(2);
|
||||
};
|
||||
|
||||
var vibrate = function() {
|
||||
navigator.notification.vibrate(0);
|
||||
};
|
||||
|
||||
function roundNumber(num) {
|
||||
var dec = 3;
|
||||
var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
|
||||
return result;
|
||||
}
|
||||
|
||||
var accelerationWatch = null;
|
||||
|
||||
function updateAcceleration(a) {
|
||||
document.getElementById('x').innerHTML = roundNumber(a.x);
|
||||
document.getElementById('y').innerHTML = roundNumber(a.y);
|
||||
document.getElementById('z').innerHTML = roundNumber(a.z);
|
||||
}
|
||||
|
||||
var toggleAccel = function() {
|
||||
if (accelerationWatch !== null) {
|
||||
navigator.accelerometer.clearWatch(accelerationWatch);
|
||||
updateAcceleration({
|
||||
x : "",
|
||||
y : "",
|
||||
z : ""
|
||||
});
|
||||
accelerationWatch = null;
|
||||
} else {
|
||||
var options = {};
|
||||
options.frequency = 1000;
|
||||
accelerationWatch = navigator.accelerometer.watchAcceleration(
|
||||
updateAcceleration, function(ex) {
|
||||
alert("accel fail (" + ex.name + ": " + ex.message + ")");
|
||||
}, options);
|
||||
}
|
||||
|
||||
var accelerationWatch = false;
|
||||
|
||||
var toggleAccel = function() {
|
||||
if (accelerationWatch) {
|
||||
navigator.accelerometer.clearWatch(accelerationWatch);
|
||||
updateAcceleration( {
|
||||
x : "",
|
||||
y : "",
|
||||
z : ""
|
||||
});
|
||||
accelerationWatch = false;
|
||||
} else {
|
||||
accelerationWatch = true;
|
||||
var options = new Object();
|
||||
options.frequency = 1000;
|
||||
accelerationWatch = navigator.accelerometer.watchAcceleration(
|
||||
updateAcceleration, function(ex) {
|
||||
navigator.accelerometer.clearWatch(accel_watch_id);
|
||||
alert("accel fail (" + ex.name + ": " + ex.message + ")");
|
||||
}, options);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
function updateAcceleration(a) {
|
||||
document.getElementById('x').innerHTML = roundNumber(a.x);
|
||||
document.getElementById('y').innerHTML = roundNumber(a.y);
|
||||
document.getElementById('z').innerHTML = roundNumber(a.z);
|
||||
}
|
||||
|
||||
var preventBehavior = function(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
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 dump_pic(data)
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
console.log(data);
|
||||
viewport.style.display = "";
|
||||
viewport.style.position = "absolute";
|
||||
viewport.style.top = "10px";
|
||||
viewport.style.left = "10px";
|
||||
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
|
||||
}
|
||||
function fail(msg) {
|
||||
alert(msg);
|
||||
}
|
||||
|
||||
function close()
|
||||
{
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.position = "relative";
|
||||
viewport.style.display = "none";
|
||||
}
|
||||
function show_pic() {
|
||||
navigator.camera.getPicture(dump_pic, fail, {
|
||||
quality : 50
|
||||
});
|
||||
}
|
||||
|
||||
function fail(fail)
|
||||
{
|
||||
alert(fail);
|
||||
}
|
||||
function close() {
|
||||
var viewport = document.getElementById('viewport');
|
||||
viewport.style.position = "relative";
|
||||
viewport.style.display = "none";
|
||||
}
|
||||
|
||||
// This is just to do this.
|
||||
function readFile()
|
||||
{
|
||||
navigator.file.read('/sdcard/phonegap.txt', fail , 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 writeFile() {
|
||||
navigator.file.write('foo.txt', "This is a test of writing to a file",
|
||||
fail, fail);
|
||||
}
|
||||
|
||||
function get_contacts()
|
||||
{
|
||||
var obj = new ContactFindOptions();
|
||||
obj.filter="";
|
||||
obj.multiple=true;
|
||||
obj.limit=5;
|
||||
navigator.service.contacts.find(["displayName", "phoneNumbers", "emails"], contacts_success, fail, obj);
|
||||
}
|
||||
function contacts_success(contacts) {
|
||||
alert(contacts.length
|
||||
+ ' contacts returned.'
|
||||
+ (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted)
|
||||
: ''));
|
||||
}
|
||||
|
||||
function contacts_success(contacts)
|
||||
{
|
||||
alert(contacts.length + ' contacts returned.' +
|
||||
(contacts[2] ? (' Third contact is ' + contacts[2].displayName) : ''));
|
||||
}
|
||||
|
||||
|
||||
function init(){
|
||||
//the next line makes it impossible to see Contacts on the HTC Evo since it doesn't have a scroll button
|
||||
// document.addEventListener("touchmove", preventBehavior, false);
|
||||
document.addEventListener("deviceready", deviceInfo, true);
|
||||
}
|
||||
function get_contacts() {
|
||||
var obj = new ContactFindOptions();
|
||||
obj.filter = "";
|
||||
obj.multiple = true;
|
||||
obj.limit = 5;
|
||||
navigator.service.contacts.find(
|
||||
[ "displayName", "name" ], contacts_success,
|
||||
fail, obj);
|
||||
}
|
||||
|
||||
var networkReachableCallback = function(reachability) {
|
||||
// There is no consistency on the format of reachability
|
||||
var networkState = reachability.code || reachability;
|
||||
|
||||
var currentState = {};
|
||||
currentState[NetworkStatus.NOT_REACHABLE] = 'No network connection';
|
||||
currentState[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection';
|
||||
currentState[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK] = 'WiFi connection';
|
||||
|
||||
confirm("Connection type:\n" + currentState[networkState]);
|
||||
};
|
||||
|
||||
function check_network() {
|
||||
navigator.network.isReachable("www.mobiledevelopersolutions.com",
|
||||
networkReachableCallback, {});
|
||||
}
|
||||
|
||||
function init() {
|
||||
// the next line makes it impossible to see Contacts on the HTC Evo since it
|
||||
// doesn't have a scroll button
|
||||
// document.addEventListener("touchmove", preventBehavior, false);
|
||||
document.addEventListener("deviceready", deviceInfo, true);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ function Acceleration(x, y, z) {
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.timestamp = new Date().getTime();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides access to device accelerometer data.
|
||||
@@ -28,7 +28,7 @@ function Accelerometer() {
|
||||
* List of accelerometer watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
};
|
||||
}
|
||||
|
||||
Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
@@ -42,13 +42,13 @@ Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback != "function") {
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Accelerometer Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Accelerometer Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -68,16 +68,16 @@ Accelerometer.prototype.getCurrentAcceleration = function(successCallback, error
|
||||
Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
|
||||
|
||||
// Default interval (10 sec)
|
||||
var frequency = (options != undefined)? options.frequency : 10000;
|
||||
var frequency = (options !== undefined)? options.frequency : 10000;
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback != "function") {
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Accelerometer Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Accelerometer Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -108,12 +108,14 @@ Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallb
|
||||
Accelerometer.prototype.clearWatch = function(id) {
|
||||
|
||||
// Stop javascript timer & remove from timer list
|
||||
if (id && navigator.accelerometer.timers[id] != undefined) {
|
||||
if (id && navigator.accelerometer.timers[id] !== undefined) {
|
||||
clearInterval(navigator.accelerometer.timers[id]);
|
||||
delete navigator.accelerometer.timers[id];
|
||||
}
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
|
||||
if (typeof navigator.accelerometer === "undefined") {
|
||||
navigator.accelerometer = new Accelerometer();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3,14 +3,13 @@
|
||||
* 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) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
function App() {
|
||||
}
|
||||
function App() {}
|
||||
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
@@ -22,19 +21,19 @@ App.prototype.clearCache = function() {
|
||||
/**
|
||||
* Load the url into the webview.
|
||||
*
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs.
|
||||
* loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html");
|
||||
* keepRunning: boolean => enable app to keep running in background
|
||||
* @param url The URL to load
|
||||
* @param props Properties that can be passed in to the activity:
|
||||
* wait: int => wait msec before loading URL
|
||||
* loadingDialog: "Title,Message" => display a native loading dialog
|
||||
* hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs.
|
||||
* loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
|
||||
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
|
||||
* errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html");
|
||||
* keepRunning: boolean => enable app to keep running in background
|
||||
*
|
||||
* Example:
|
||||
* App app = new App();
|
||||
* app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
* App app = new App();
|
||||
* app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
|
||||
*/
|
||||
App.prototype.loadUrl = function(url, props) {
|
||||
PhoneGap.exec(null, null, "App", "loadUrl", [url, props]);
|
||||
@@ -64,3 +63,27 @@ App.prototype.clearHistory = function() {
|
||||
App.prototype.addService = function(serviceType, className) {
|
||||
PhoneGap.exec(null, null, "App", "addService", [serviceType, className]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
* Note: The user should not have to call this method. Instead, when the user
|
||||
* registers for the "backbutton" event, this is automatically done.
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
App.prototype.overrideBackbutton = function(override) {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [override]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Exit and terminate the application.
|
||||
*/
|
||||
App.prototype.exitApp = function() {
|
||||
return PhoneGap.exec(null, null, "App", "exitApp", []);
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.app = window.app = new App();
|
||||
});
|
||||
|
||||
@@ -59,13 +59,13 @@ Camera.prototype.PictureSourceType = Camera.PictureSourceType;
|
||||
Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback != "function") {
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Camera Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Camera Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -80,12 +80,14 @@ Camera.prototype.getPicture = function(successCallback, errorCallback, options)
|
||||
destinationType = this.options.destinationType;
|
||||
}
|
||||
var sourceType = Camera.PictureSourceType.CAMERA;
|
||||
if (typeof this.options.sourceType == "number") {
|
||||
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();
|
||||
if (typeof navigator.camera === "undefined") {
|
||||
navigator.camera = new Camera();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ function Compass() {
|
||||
* List of compass watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
};
|
||||
}
|
||||
|
||||
Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
@@ -34,13 +34,13 @@ Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback != "function") {
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Compass Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Compass Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -60,16 +60,16 @@ Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, o
|
||||
Compass.prototype.watchHeading= function(successCallback, errorCallback, options) {
|
||||
|
||||
// Default interval (100 msec)
|
||||
var frequency = (options != undefined) ? options.frequency : 100;
|
||||
var frequency = (options !== undefined) ? options.frequency : 100;
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback != "function") {
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Compass Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Compass Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -109,5 +109,7 @@ Compass.prototype.clearWatch = function(id) {
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.compass == "undefined") navigator.compass = new Compass();
|
||||
if (typeof navigator.compass === "undefined") {
|
||||
navigator.compass = new Compass();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,24 +17,17 @@
|
||||
* @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} revision 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[]} categories
|
||||
* @param {ContactField[]} urls contact's web sites
|
||||
* @param {ContactAccounts[]} accounts contact's online accounts
|
||||
* @param {DOMString} utcOffset UTC time zone offset
|
||||
* @param {DOMString} connected
|
||||
* @param {DOMString} timezone the contacts time zone
|
||||
*/
|
||||
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) {
|
||||
var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
|
||||
ims, organizations, revision, birthday, gender, note, photos, categories, urls, timezone) {
|
||||
this.id = id || null;
|
||||
this.rawId = null;
|
||||
this.displayName = displayName || null;
|
||||
@@ -45,29 +38,43 @@ var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, ad
|
||||
this.addresses = addresses || null; // ContactAddress[]
|
||||
this.ims = ims || null; // ContactField[]
|
||||
this.organizations = organizations || null; // ContactOrganization[]
|
||||
this.published = published || null;
|
||||
this.updated = updated || null;
|
||||
this.revision = revision || 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.categories = categories || null; // ContactField[]
|
||||
this.urls = urls || null; // ContactField[]
|
||||
this.accounts = accounts || null; // ContactAccount[]
|
||||
this.utcOffset = utcOffset || null;
|
||||
this.connected = connected || null;
|
||||
this.timezone = timezone || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Removes contact from device storage.
|
||||
* @param successCB success callback
|
||||
* @param errorCB error callback
|
||||
*/
|
||||
Contact.prototype.remove = function(successCB, errorCB) {
|
||||
if (this.id == null) {
|
||||
if (this.id === null) {
|
||||
var errorObj = new ContactError();
|
||||
errorObj.code = ContactError.NOT_FOUND_ERROR;
|
||||
errorCB(errorObj);
|
||||
@@ -84,48 +91,49 @@ Contact.prototype.remove = function(successCB, errorCB) {
|
||||
*/
|
||||
Contact.prototype.clone = function() {
|
||||
var clonedContact = PhoneGap.clone(this);
|
||||
var i;
|
||||
clonedContact.id = null;
|
||||
clonedContact.rawId = null;
|
||||
// Loop through and clear out any id's in phones, emails, etc.
|
||||
if (clonedContact.phoneNumbers) {
|
||||
for (i=0; i<clonedContact.phoneNumbers.length; i++) {
|
||||
clonedContact.phoneNumbers[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
|
||||
clonedContact.phoneNumbers[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.emails) {
|
||||
for (i=0; i<clonedContact.emails.length; i++) {
|
||||
clonedContact.emails[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.emails.length; i++) {
|
||||
clonedContact.emails[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.addresses) {
|
||||
for (i=0; i<clonedContact.addresses.length; i++) {
|
||||
clonedContact.addresses[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.addresses.length; i++) {
|
||||
clonedContact.addresses[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.ims) {
|
||||
for (i=0; i<clonedContact.ims.length; i++) {
|
||||
clonedContact.ims[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.ims.length; i++) {
|
||||
clonedContact.ims[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.organizations) {
|
||||
for (i=0; i<clonedContact.organizations.length; i++) {
|
||||
clonedContact.organizations[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.organizations.length; i++) {
|
||||
clonedContact.organizations[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.tags) {
|
||||
for (i=0; i<clonedContact.tags.length; i++) {
|
||||
clonedContact.tags[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.tags.length; i++) {
|
||||
clonedContact.tags[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.relationships) {
|
||||
for (i=0; i<clonedContact.relationships.length; i++) {
|
||||
clonedContact.relationships[i].id = null;
|
||||
}
|
||||
if (clonedContact.photos) {
|
||||
for (i = 0; i < clonedContact.photos.length; i++) {
|
||||
clonedContact.photos[i].id = null;
|
||||
}
|
||||
}
|
||||
if (clonedContact.urls) {
|
||||
for (i=0; i<clonedContact.urls.length; i++) {
|
||||
clonedContact.urls[i].id = null;
|
||||
}
|
||||
for (i = 0; i < clonedContact.urls.length; i++) {
|
||||
clonedContact.urls[i].id = null;
|
||||
}
|
||||
}
|
||||
return clonedContact;
|
||||
};
|
||||
@@ -162,13 +170,13 @@ var ContactName = function(formatted, familyName, givenName, middle, prefix, suf
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param type
|
||||
* @param value
|
||||
* @param primary
|
||||
* @param pref
|
||||
*/
|
||||
var ContactField = function(type, value, primary) {
|
||||
var ContactField = function(type, value, pref) {
|
||||
this.id = null;
|
||||
this.type = type || null;
|
||||
this.value = value || null;
|
||||
this.primary = primary || null;
|
||||
this.pref = pref || null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -202,38 +210,20 @@ var ContactAddress = function(formatted, streetAddress, locality, region, postal
|
||||
* @param location
|
||||
* @param desc
|
||||
*/
|
||||
var ContactOrganization = function(name, dept, title, startDate, endDate, location, desc) {
|
||||
var ContactOrganization = function(name, dept, title) {
|
||||
this.id = null;
|
||||
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 {DOMString} id unique identifier, should only be set by native code
|
||||
* @param domain
|
||||
* @param username
|
||||
* @param userid
|
||||
*/
|
||||
var ContactAccount = function(domain, username, userid) {
|
||||
this.id = null;
|
||||
this.domain = domain || null;
|
||||
this.username = username || null;
|
||||
this.userid = userid || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a group of Contacts.
|
||||
*/
|
||||
var Contacts = function() {
|
||||
this.inProgress = false;
|
||||
this.records = new Array();
|
||||
}
|
||||
this.records = [];
|
||||
};
|
||||
/**
|
||||
* Returns an array of Contacts matching the search criteria.
|
||||
* @param fields that should be searched
|
||||
@@ -254,10 +244,11 @@ Contacts.prototype.find = function(fields, successCB, errorCB, options) {
|
||||
* @returns new Contact object
|
||||
*/
|
||||
Contacts.prototype.create = function(properties) {
|
||||
var i;
|
||||
var contact = new Contact();
|
||||
for (i in properties) {
|
||||
if (contact[i]!='undefined') {
|
||||
contact[i]=properties[i];
|
||||
if (contact[i] !== 'undefined') {
|
||||
contact[i] = properties[i];
|
||||
}
|
||||
}
|
||||
return contact;
|
||||
@@ -272,52 +263,35 @@ Contacts.prototype.create = function(properties) {
|
||||
* @returns an array of Contact objects
|
||||
*/
|
||||
Contacts.prototype.cast = function(pluginResult) {
|
||||
var contacts = new Array();
|
||||
for (var i=0; i<pluginResult.message.length; i++) {
|
||||
var contacts = [];
|
||||
var i;
|
||||
for (i=0; i<pluginResult.message.length; i++) {
|
||||
contacts.push(navigator.service.contacts.create(pluginResult.message[i]));
|
||||
}
|
||||
pluginResult.message = contacts;
|
||||
return pluginResult;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var ContactFindOptions = function(filter, multiple, updatedSince) {
|
||||
this.filter = filter || '';
|
||||
this.multiple = multiple || false;
|
||||
this.limit = limit || 1;
|
||||
this.multiple = multiple || true;
|
||||
this.updatedSince = updatedSince || '';
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Add the contact interface into the browser.
|
||||
*/
|
||||
PhoneGap.addConstructor(function() {
|
||||
if(typeof navigator.service == "undefined") navigator.service = new Object();
|
||||
if(typeof navigator.service.contacts == "undefined") navigator.service.contacts = new Contacts();
|
||||
if(typeof navigator.service === "undefined") {
|
||||
navigator.service = {};
|
||||
}
|
||||
if(typeof navigator.service.contacts === "undefined") {
|
||||
navigator.service.contacts = new Contacts();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -30,6 +30,8 @@ Crypto.prototype.getPlainString = function(string) {
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.Crypto == "undefined") navigator.Crypto = new Crypto();
|
||||
if (typeof navigator.Crypto === "undefined") {
|
||||
navigator.Crypto = new Crypto();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 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) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -46,13 +46,13 @@ function Device() {
|
||||
Device.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
|
||||
// successCallback required
|
||||
if (typeof successCallback != "function") {
|
||||
if (typeof successCallback !== "function") {
|
||||
console.log("Device Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Device Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -62,31 +62,37 @@ Device.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
};
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
* This is only for Android.
|
||||
*
|
||||
* You must explicitly override the back button.
|
||||
*/
|
||||
Device.prototype.overrideBackButton = function() {
|
||||
BackButton.override();
|
||||
}
|
||||
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
|
||||
app.overrideBackbutton(true);
|
||||
};
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
* This is only for Android.
|
||||
*
|
||||
* This resets the back button to the default behaviour
|
||||
*/
|
||||
Device.prototype.resetBackButton = function() {
|
||||
BackButton.reset();
|
||||
}
|
||||
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
|
||||
app.overrideBackbutton(false);
|
||||
};
|
||||
|
||||
/*
|
||||
* DEPRECATED
|
||||
* This is only for Android.
|
||||
*
|
||||
* This terminates the activity!
|
||||
*/
|
||||
Device.prototype.exitApp = function() {
|
||||
BackButton.exitApp();
|
||||
}
|
||||
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
|
||||
app.exitApp();
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.device = window.device = new Device();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
77
framework/assets/js/filetransfer.js
Normal file
77
framework/assets/js/filetransfer.js
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
/**
|
||||
* FileTransfer uploads a file to a remote server.
|
||||
*/
|
||||
function FileTransfer() {}
|
||||
|
||||
/**
|
||||
* FileUploadResult
|
||||
*/
|
||||
function FileUploadResult() {
|
||||
this.bytesSent = 0;
|
||||
this.responseCode = null;
|
||||
this.response = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* FileTransferError
|
||||
*/
|
||||
function FileTransferError() {
|
||||
this.code = null;
|
||||
}
|
||||
|
||||
FileTransferError.FILE_NOT_FOUND_ERR = 1;
|
||||
FileTransferError.INVALID_URL_ERR = 2;
|
||||
FileTransferError.CONNECTION_ERR = 3;
|
||||
|
||||
/**
|
||||
* Given an absolute file path, uploads a file on the device to a remote server
|
||||
* using a multipart HTTP request.
|
||||
* @param filePath {String} Full path of the file on the device
|
||||
* @param server {String} URL of the server to receive the file
|
||||
* @param successCallback (Function} Callback to be invoked when upload has completed
|
||||
* @param errorCallback {Function} Callback to be invoked upon error
|
||||
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
|
||||
*/
|
||||
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) {
|
||||
|
||||
// check for options
|
||||
var fileKey = null;
|
||||
var fileName = null;
|
||||
var mimeType = null;
|
||||
var params = null;
|
||||
if (options) {
|
||||
fileKey = options.fileKey;
|
||||
fileName = options.fileName;
|
||||
mimeType = options.mimeType;
|
||||
if (options.params) {
|
||||
params = options.params;
|
||||
}
|
||||
else {
|
||||
params = {};
|
||||
}
|
||||
}
|
||||
|
||||
PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Options to customize the HTTP request used to upload files.
|
||||
* @param fileKey {String} Name of file request parameter.
|
||||
* @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
|
||||
* @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
|
||||
* @param params {Object} Object with key: value params to send to the server.
|
||||
*/
|
||||
function FileUploadOptions(fileKey, fileName, mimeType, params) {
|
||||
this.fileKey = fileKey || null;
|
||||
this.fileName = fileName || null;
|
||||
this.mimeType = mimeType || null;
|
||||
this.params = params || null;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ function Geolocation() {
|
||||
|
||||
// Geolocation listeners
|
||||
this.listeners = {};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Position error object
|
||||
@@ -28,7 +28,7 @@ function Geolocation() {
|
||||
function PositionError(code, message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
};
|
||||
}
|
||||
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
@@ -42,7 +42,7 @@ PositionError.TIMEOUT = 3;
|
||||
* @param {PositionOptions} options The options for getting the position data. (OPTIONAL)
|
||||
*/
|
||||
Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) {
|
||||
if (navigator._geo.listeners["global"]) {
|
||||
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."));
|
||||
@@ -53,20 +53,20 @@ Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallba
|
||||
var maximumAge = 10000;
|
||||
var enableHighAccuracy = false;
|
||||
var timeout = 10000;
|
||||
if (typeof options != "undefined") {
|
||||
if (typeof options.maximumAge != "undefined") {
|
||||
if (typeof options !== "undefined") {
|
||||
if (typeof options.maximumAge !== "undefined") {
|
||||
maximumAge = options.maximumAge;
|
||||
}
|
||||
if (typeof options.enableHighAccuracy != "undefined") {
|
||||
if (typeof options.enableHighAccuracy !== "undefined") {
|
||||
enableHighAccuracy = options.enableHighAccuracy;
|
||||
}
|
||||
if (typeof options.timeout != "undefined") {
|
||||
if (typeof options.timeout !== "undefined") {
|
||||
timeout = options.timeout;
|
||||
}
|
||||
}
|
||||
navigator._geo.listeners["global"] = {"success" : successCallback, "fail" : errorCallback };
|
||||
navigator._geo.listeners.global = {"success" : successCallback, "fail" : errorCallback };
|
||||
PhoneGap.exec(null, null, "Geolocation", "getCurrentLocation", [enableHighAccuracy, timeout, maximumAge]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously watches the geolocation for changes to geolocation. When a change occurs,
|
||||
@@ -81,17 +81,17 @@ Geolocation.prototype.watchPosition = function(successCallback, errorCallback, o
|
||||
var maximumAge = 10000;
|
||||
var enableHighAccuracy = false;
|
||||
var timeout = 10000;
|
||||
if (typeof options != "undefined") {
|
||||
if (typeof options.frequency != "undefined") {
|
||||
if (typeof options !== "undefined") {
|
||||
if (typeof options.frequency !== "undefined") {
|
||||
maximumAge = options.frequency;
|
||||
}
|
||||
if (typeof options.maximumAge != "undefined") {
|
||||
if (typeof options.maximumAge !== "undefined") {
|
||||
maximumAge = options.maximumAge;
|
||||
}
|
||||
if (typeof options.enableHighAccuracy != "undefined") {
|
||||
if (typeof options.enableHighAccuracy !== "undefined") {
|
||||
enableHighAccuracy = options.enableHighAccuracy;
|
||||
}
|
||||
if (typeof options.timeout != "undefined") {
|
||||
if (typeof options.timeout !== "undefined") {
|
||||
timeout = options.timeout;
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, s
|
||||
var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
|
||||
var loc = new Position(coords, stamp);
|
||||
try {
|
||||
if (lat == "undefined" || lng == "undefined") {
|
||||
if (lat === "undefined" || lng === "undefined") {
|
||||
navigator._geo.listeners[id].fail(new PositionError(PositionError.POSITION_UNAVAILABLE, "Lat/Lng are undefined."));
|
||||
}
|
||||
else {
|
||||
@@ -130,8 +130,8 @@ Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, s
|
||||
console.log("Geolocation Error: Error calling success callback function.");
|
||||
}
|
||||
|
||||
if (id == "global") {
|
||||
delete navigator._geo.listeners["global"];
|
||||
if (id === "global") {
|
||||
delete navigator._geo.listeners.global;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -186,7 +186,7 @@ PhoneGap.addConstructor(function() {
|
||||
navigator._geo = new Geolocation();
|
||||
|
||||
// No native geolocation object for Android 1.x, so use PhoneGap geolocation
|
||||
if (typeof navigator.geolocation == 'undefined') {
|
||||
if (typeof navigator.geolocation === 'undefined') {
|
||||
navigator.geolocation = navigator._geo;
|
||||
Geolocation.usingPhoneGap = true;
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.backTrigger = function() {
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('backKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
};
|
||||
|
||||
KeyEvent.prototype.menuTrigger = function() {
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('menuKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
};
|
||||
|
||||
KeyEvent.prototype.searchTrigger = function() {
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent('searchKeyDown');
|
||||
document.dispatchEvent(e);
|
||||
};
|
||||
|
||||
if (document.keyEvent == null || typeof document.keyEvent == 'undefined') {
|
||||
window.keyEvent = document.keyEvent = new KeyEvent();
|
||||
}
|
||||
@@ -40,8 +40,8 @@ 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 (msg === Media.MEDIA_STATE) {
|
||||
if (value === Media.MEDIA_STOPPED) {
|
||||
if (media.successCallback) {
|
||||
media.successCallback();
|
||||
}
|
||||
@@ -50,10 +50,10 @@ PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
media.statusCallback(value);
|
||||
}
|
||||
}
|
||||
else if (msg == Media.MEDIA_DURATION) {
|
||||
else if (msg === Media.MEDIA_DURATION) {
|
||||
media._duration = value;
|
||||
}
|
||||
else if (msg == Media.MEDIA_ERROR) {
|
||||
else if (msg === Media.MEDIA_ERROR) {
|
||||
if (media.errorCallback) {
|
||||
media.errorCallback(value);
|
||||
}
|
||||
@@ -76,25 +76,25 @@ PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
|
||||
|
||||
// successCallback optional
|
||||
if (successCallback && (typeof successCallback != "function")) {
|
||||
if (successCallback && (typeof successCallback !== "function")) {
|
||||
console.log("Media Error: successCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// errorCallback optional
|
||||
if (errorCallback && (typeof errorCallback != "function")) {
|
||||
if (errorCallback && (typeof errorCallback !== "function")) {
|
||||
console.log("Media Error: errorCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// statusCallback optional
|
||||
if (statusCallback && (typeof statusCallback != "function")) {
|
||||
if (statusCallback && (typeof statusCallback !== "function")) {
|
||||
console.log("Media Error: statusCallback is not a function");
|
||||
return;
|
||||
}
|
||||
|
||||
// statusCallback optional
|
||||
if (positionCallback && (typeof positionCallback != "function")) {
|
||||
if (positionCallback && (typeof positionCallback !== "function")) {
|
||||
console.log("Media Error: positionCallback is not a function");
|
||||
return;
|
||||
}
|
||||
@@ -129,9 +129,9 @@ Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
|
||||
* @constructor
|
||||
*/
|
||||
function MediaError() {
|
||||
this.code = null,
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
};
|
||||
}
|
||||
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
function NetworkStatus() {
|
||||
//this.code = null;
|
||||
//this.message = "";
|
||||
};
|
||||
}
|
||||
|
||||
NetworkStatus.NOT_REACHABLE = 0;
|
||||
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
@@ -30,7 +30,7 @@ function Network() {
|
||||
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
|
||||
*/
|
||||
this.lastReachability = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when the reachability status has changed.
|
||||
@@ -57,6 +57,8 @@ Network.prototype.isReachable = function(uri, callback, options) {
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.network == "undefined") navigator.network = new Network();
|
||||
if (typeof navigator.network === "undefined") {
|
||||
navigator.network = new Network();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ Notification.prototype.beep = function(count) {
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
|
||||
if (typeof navigator.notification === "undefined") {
|
||||
navigator.notification = new Notification();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 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) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@
|
||||
* document.addEventListener("resume", myResumeListener, false);
|
||||
*/
|
||||
|
||||
if (typeof(DeviceInfo) != 'object')
|
||||
if (typeof(DeviceInfo) !== 'object') {
|
||||
DeviceInfo = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* This represents the PhoneGap API itself, and provides a global namespace for accessing
|
||||
@@ -48,7 +49,7 @@ var PhoneGap = {
|
||||
/**
|
||||
* Custom pub-sub channel that can have functions subscribed to it
|
||||
*/
|
||||
PhoneGap.Channel = function(type)
|
||||
PhoneGap.Channel = function (type)
|
||||
{
|
||||
this.type = type;
|
||||
this.handlers = {};
|
||||
@@ -66,10 +67,10 @@ PhoneGap.Channel = function(type)
|
||||
*/
|
||||
PhoneGap.Channel.prototype.subscribe = function(f, c, g) {
|
||||
// need a function to call
|
||||
if (f == null) { return; }
|
||||
if (f === null) { return; }
|
||||
|
||||
var func = f;
|
||||
if (typeof c == "object" && f instanceof Function) { func = PhoneGap.close(c, 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;
|
||||
@@ -88,9 +89,9 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
|
||||
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); }
|
||||
if (typeof c === "object" && f instanceof Function) { f = PhoneGap.close(c, f); }
|
||||
f.apply(this, this.fireArgs);
|
||||
} else {
|
||||
g = this.subscribe(m);
|
||||
@@ -113,11 +114,14 @@ PhoneGap.Channel.prototype.unsubscribe = function(g) {
|
||||
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;
|
||||
var item, handler, rv;
|
||||
for (item in this.handlers) {
|
||||
if (this.handlers.hasOwnProperty(item)) {
|
||||
handler = this.handlers[item];
|
||||
if (handler instanceof Function) {
|
||||
rv = (handler.apply(this, arguments) === false);
|
||||
fail = fail || rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.fired = true;
|
||||
@@ -134,18 +138,29 @@ PhoneGap.Channel.prototype.fire = function(e) {
|
||||
PhoneGap.Channel.join = function(h, c) {
|
||||
var i = c.length;
|
||||
var f = function() {
|
||||
if (!(--i)) h();
|
||||
if (!(--i)) {
|
||||
h();
|
||||
}
|
||||
};
|
||||
var len = i;
|
||||
var j;
|
||||
for (j=0; j<len; j++) {
|
||||
if (!c[j].fired) {
|
||||
c[j].subscribeOnce(f);
|
||||
}
|
||||
else {
|
||||
i--;
|
||||
}
|
||||
}
|
||||
for (var j=0; j<i; j++) {
|
||||
(!c[j].fired?c[j].subscribeOnce(f):i--);
|
||||
if (!i) {
|
||||
h();
|
||||
}
|
||||
if (!i) h();
|
||||
};
|
||||
|
||||
/**
|
||||
* Boolean flag indicating if the PhoneGap API is available and initialized.
|
||||
*/ // TODO: Remove this, it is unused here ... -jm
|
||||
PhoneGap.available = DeviceInfo.uuid != undefined;
|
||||
PhoneGap.available = DeviceInfo.uuid !== undefined;
|
||||
|
||||
/**
|
||||
* Add an initialization function to a queue that ensures it will run and initialize
|
||||
@@ -183,7 +198,7 @@ PhoneGap.addPlugin = function(name, obj) {
|
||||
else {
|
||||
console.log("Error: Plugin "+name+" already exists.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* onDOMContentLoaded channel is fired when the DOM content
|
||||
@@ -238,6 +253,39 @@ if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); }
|
||||
PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady');
|
||||
|
||||
|
||||
// Array of channels that must fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady];
|
||||
|
||||
// Hashtable of user defined channels that must also fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsMap = {};
|
||||
|
||||
/**
|
||||
* Indicate that a feature needs to be initialized before it is ready to be used.
|
||||
* This holds up PhoneGap's "deviceready" event until the feature has been initialized
|
||||
* and PhoneGap.initComplete(feature) is called.
|
||||
*
|
||||
* @param feature {String} The unique feature name
|
||||
*/
|
||||
PhoneGap.waitForInitialization = function(feature) {
|
||||
if (feature) {
|
||||
var channel = new PhoneGap.Channel(feature);
|
||||
PhoneGap.deviceReadyChannelsMap[feature] = channel;
|
||||
PhoneGap.deviceReadyChannelsArray.push(channel);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Indicate that initialization code has completed and the feature is ready to be used.
|
||||
*
|
||||
* @param feature {String} The unique feature name
|
||||
*/
|
||||
PhoneGap.initializationComplete = function(feature) {
|
||||
var channel = PhoneGap.deviceReadyChannelsMap[feature];
|
||||
if (channel) {
|
||||
channel.fire();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create all PhoneGap objects once page has fully loaded and native side is ready.
|
||||
*/
|
||||
@@ -245,11 +293,17 @@ PhoneGap.Channel.join(function() {
|
||||
|
||||
// Start listening for XHR callbacks
|
||||
setTimeout(function() {
|
||||
if (CallbackServer.usePolling()) {
|
||||
if (PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
else {
|
||||
PhoneGap.JSCallback();
|
||||
var polling = prompt("usePolling", "gap_callbackServer:");
|
||||
if (polling == "true") {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
else {
|
||||
PhoneGap.JSCallback();
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
|
||||
@@ -259,22 +313,21 @@ PhoneGap.Channel.join(function() {
|
||||
// Fire event to notify that all objects are created
|
||||
PhoneGap.onPhoneGapReady.fire();
|
||||
|
||||
// Fire onDeviceReady event once all constructors have run and PhoneGap info has been
|
||||
// received from native side, and any user defined initialization channels.
|
||||
PhoneGap.Channel.join(function() {
|
||||
|
||||
// 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.deviceReadyChannelsArray);
|
||||
|
||||
}, [ 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();
|
||||
@@ -285,17 +338,50 @@ PhoneGap.m_document_addEventListener = document.addEventListener;
|
||||
|
||||
document.addEventListener = function(evt, handler, capture) {
|
||||
var e = evt.toLowerCase();
|
||||
if (e == 'deviceready') {
|
||||
if (e === 'deviceready') {
|
||||
PhoneGap.onDeviceReady.subscribeOnce(handler);
|
||||
} else if (e == 'resume') {
|
||||
} else if (e === 'resume') {
|
||||
PhoneGap.onResume.subscribe(handler);
|
||||
} else if (e == 'pause') {
|
||||
if (PhoneGap.onDeviceReady.fired) {
|
||||
PhoneGap.onResume.fire();
|
||||
}
|
||||
} else if (e === 'pause') {
|
||||
PhoneGap.onPause.subscribe(handler);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// If subscribing to Android backbutton
|
||||
if (e === 'backbutton') {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]);
|
||||
}
|
||||
|
||||
PhoneGap.m_document_addEventListener.call(document, evt, handler, capture);
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept calls to document.removeEventListener and watch for events that
|
||||
// are generated by PhoneGap native code
|
||||
PhoneGap.m_document_removeEventListener = document.removeEventListener;
|
||||
|
||||
document.removeEventListener = function(evt, handler, capture) {
|
||||
var e = evt.toLowerCase();
|
||||
|
||||
// If unsubscribing to Android backbutton
|
||||
if (e === 'backbutton') {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]);
|
||||
}
|
||||
|
||||
PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
|
||||
};
|
||||
|
||||
/**
|
||||
* Method to fire event from native code
|
||||
*/
|
||||
PhoneGap.fireEvent = function(type) {
|
||||
var e = document.createEvent('Events');
|
||||
e.initEvent(type);
|
||||
document.dispatchEvent(e);
|
||||
};
|
||||
|
||||
/**
|
||||
* If JSON not included, use our own stringify. (Android 1.6)
|
||||
* The restriction on ours is that it must be an array of simple types.
|
||||
@@ -304,57 +390,53 @@ document.addEventListener = function(evt, handler, capture) {
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.stringify = function(args) {
|
||||
if (typeof JSON == "undefined") {
|
||||
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 (args[i][name] != null) {
|
||||
if (!start) {
|
||||
s = s + ',';
|
||||
}
|
||||
s = s + '"' + name + '":';
|
||||
var nameType = typeof args[i][name];
|
||||
if ((nameType == "number") || (nameType == "boolean")) {
|
||||
s = s + args[i][name];
|
||||
}
|
||||
else if ((typeof args[i][name]) == 'function') {
|
||||
// don't copy the functions
|
||||
s = s + '""';
|
||||
}
|
||||
else if (args[i][name] instanceof Object) {
|
||||
s = s + this.stringify(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 + '"';
|
||||
var i, type, start, name, nameType, a;
|
||||
for (i = 0; i < args.length; i++) {
|
||||
if (args[i] != null) {
|
||||
if (i > 0) {
|
||||
s = s + ",";
|
||||
}
|
||||
type = typeof args[i];
|
||||
if ((type === "number") || (type === "boolean")) {
|
||||
s = s + args[i];
|
||||
} else if (args[i] instanceof Array) {
|
||||
s = s + "[" + args[i] + "]";
|
||||
} else if (args[i] instanceof Object) {
|
||||
start = true;
|
||||
s = s + '{';
|
||||
for (name in args[i]) {
|
||||
if (args[i][name] !== null) {
|
||||
if (!start) {
|
||||
s = s + ',';
|
||||
}
|
||||
s = s + '"' + name + '":';
|
||||
nameType = typeof args[i][name];
|
||||
if ((nameType === "number") || (nameType === "boolean")) {
|
||||
s = s + args[i][name];
|
||||
} else if ((typeof args[i][name]) === 'function') {
|
||||
// don't copy the functions
|
||||
s = s + '""';
|
||||
} else if (args[i][name] instanceof Object) {
|
||||
s = s + this.stringify(args[i][name]);
|
||||
} else {
|
||||
s = s + '"' + args[i][name] + '"';
|
||||
}
|
||||
start = false;
|
||||
}
|
||||
}
|
||||
s = s + '}';
|
||||
} else {
|
||||
a = args[i].replace(/\\/g, '\\\\');
|
||||
a = a.replace(/"/g, '\\"');
|
||||
s = s + '"' + a + '"';
|
||||
}
|
||||
}
|
||||
}
|
||||
s = s + "]";
|
||||
return s;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
return JSON.stringify(args);
|
||||
}
|
||||
};
|
||||
@@ -366,13 +448,14 @@ PhoneGap.stringify = function(args) {
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.clone = function(obj) {
|
||||
var i, retVal;
|
||||
if(!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(obj instanceof Array){
|
||||
var retVal = new Array();
|
||||
for(var i = 0; i < obj.length; ++i){
|
||||
retVal = [];
|
||||
for(i = 0; i < obj.length; ++i){
|
||||
retVal.push(PhoneGap.clone(obj[i]));
|
||||
}
|
||||
return retVal;
|
||||
@@ -385,10 +468,14 @@ PhoneGap.clone = function(obj) {
|
||||
if(!(obj instanceof Object)){
|
||||
return obj;
|
||||
}
|
||||
|
||||
retVal = new Object();
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
retVal = {};
|
||||
for(i in obj){
|
||||
if(!(i in retVal) || retVal[i] != obj[i]) {
|
||||
if(!(i in retVal) || retVal[i] !== obj[i]) {
|
||||
retVal[i] = PhoneGap.clone(obj[i]);
|
||||
}
|
||||
}
|
||||
@@ -432,23 +519,22 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
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);
|
||||
var r = prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
||||
|
||||
// If a result was returned
|
||||
if (r.length > 0) {
|
||||
eval("var v="+r+";");
|
||||
|
||||
// If status is OK, then return value back to caller
|
||||
if (v.status == PhoneGap.callbackStatus.OK) {
|
||||
if (v.status === PhoneGap.callbackStatus.OK) {
|
||||
|
||||
// If there is a success callback, then call it now with returned value
|
||||
// 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);
|
||||
success(v.message);
|
||||
} catch (e) {
|
||||
console.log("Error in success callback: " + callbackId + " = " + e);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
@@ -460,7 +546,7 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
}
|
||||
|
||||
// If no result
|
||||
else if (v.status == PhoneGap.callbackStatus.NO_RESULT) {
|
||||
else if (v.status === PhoneGap.callbackStatus.NO_RESULT) {
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
@@ -470,15 +556,15 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
|
||||
// If error, then display error
|
||||
else {
|
||||
console.log("Error: Status="+r.status+" Message="+v.message);
|
||||
console.log("Error: Status="+v.status+" Message="+v.message);
|
||||
|
||||
// If there is a fail callback, then call it now with returned value
|
||||
if (fail) {
|
||||
try {
|
||||
fail(v.message);
|
||||
}
|
||||
catch (e) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e);
|
||||
catch (e1) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e1);
|
||||
}
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
@@ -489,8 +575,8 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Error: "+e);
|
||||
} catch (e2) {
|
||||
console.log("Error: "+e2);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -504,10 +590,10 @@ PhoneGap.callbackSuccess = function(callbackId, args) {
|
||||
if (PhoneGap.callbacks[callbackId]) {
|
||||
|
||||
// If result is to be sent to callback
|
||||
if (args.status == PhoneGap.callbackStatus.OK) {
|
||||
if (args.status === PhoneGap.callbackStatus.OK) {
|
||||
try {
|
||||
if (PhoneGap.callbacks[callbackId].success) {
|
||||
PhoneGap.callbacks[callbackId].success(args.message);
|
||||
PhoneGap.callbacks[callbackId].success(args.message);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@@ -556,38 +642,43 @@ PhoneGap.callbackError = function(callbackId, args) {
|
||||
*/
|
||||
// TODO: Is this used?
|
||||
PhoneGap.run_command = function() {
|
||||
if (!PhoneGap.available || !PhoneGap.queue.ready)
|
||||
if (!PhoneGap.available || !PhoneGap.queue.ready) {
|
||||
return;
|
||||
|
||||
}
|
||||
PhoneGap.queue.ready = false;
|
||||
|
||||
var args = PhoneGap.queue.commands.shift();
|
||||
if (PhoneGap.queue.commands.length == 0) {
|
||||
if (PhoneGap.queue.commands.length === 0) {
|
||||
clearInterval(PhoneGap.queue.timer);
|
||||
PhoneGap.queue.timer = null;
|
||||
}
|
||||
|
||||
var uri = [];
|
||||
var dict = null;
|
||||
for (var i = 1; i < args.length; i++) {
|
||||
var i;
|
||||
for (i = 1; i < args.length; i++) {
|
||||
var arg = args[i];
|
||||
if (arg == undefined || arg == null)
|
||||
if (arg === undefined || arg === null) {
|
||||
arg = '';
|
||||
if (typeof(arg) == 'object')
|
||||
}
|
||||
if (typeof(arg) === 'object') {
|
||||
dict = arg;
|
||||
else
|
||||
} else {
|
||||
uri.push(encodeURIComponent(arg));
|
||||
}
|
||||
}
|
||||
var url = "gap://" + args[0] + "/" + uri.join("/");
|
||||
if (dict != null) {
|
||||
if (dict !== null) {
|
||||
var name;
|
||||
var query_args = [];
|
||||
for (var name in dict) {
|
||||
if (typeof(name) != 'string')
|
||||
continue;
|
||||
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
|
||||
for (name in dict) {
|
||||
if (dict.hasOwnProperty(name) && (typeof (name) === 'string')) {
|
||||
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
|
||||
}
|
||||
}
|
||||
if (query_args.length > 0)
|
||||
if (query_args.length > 0) {
|
||||
url += "?" + query_args.join("&");
|
||||
}
|
||||
}
|
||||
document.location = url;
|
||||
|
||||
@@ -604,14 +695,21 @@ PhoneGap.JSCallbackToken = null;
|
||||
* Java to JavaScript.
|
||||
*/
|
||||
PhoneGap.JSCallback = function() {
|
||||
|
||||
// If polling flag was changed, start using polling from now on
|
||||
if (PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
return;
|
||||
}
|
||||
|
||||
var xmlhttp = new XMLHttpRequest();
|
||||
|
||||
// Callback function when XMLHttpRequest is ready
|
||||
xmlhttp.onreadystatechange=function(){
|
||||
if(xmlhttp.readyState == 4){
|
||||
if(xmlhttp.readyState === 4){
|
||||
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status == 200) {
|
||||
if (xmlhttp.status === 200) {
|
||||
|
||||
var msg = xmlhttp.responseText;
|
||||
setTimeout(function() {
|
||||
@@ -628,41 +726,41 @@ PhoneGap.JSCallback = function() {
|
||||
}
|
||||
|
||||
// If callback ping (used to keep XHR request from timing out)
|
||||
else if (xmlhttp.status == 404) {
|
||||
else if (xmlhttp.status === 404) {
|
||||
setTimeout(PhoneGap.JSCallback, 10);
|
||||
}
|
||||
|
||||
// If security error
|
||||
else if (xmlhttp.status == 403) {
|
||||
else if (xmlhttp.status === 403) {
|
||||
console.log("JSCallback Error: Invalid token. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If server is stopping
|
||||
else if (xmlhttp.status == 503) {
|
||||
else if (xmlhttp.status === 503) {
|
||||
console.log("JSCallback Error: Service unavailable. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If request wasn't GET
|
||||
else if (xmlhttp.status == 400) {
|
||||
else if (xmlhttp.status === 400) {
|
||||
console.log("JSCallback Error: Bad request. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If error, restart callback server
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed.");
|
||||
CallbackServer.restartServer();
|
||||
prompt("restartServer", "gap_callbackServer:");
|
||||
PhoneGap.JSCallbackPort = null;
|
||||
PhoneGap.JSCallbackToken = null;
|
||||
setTimeout(PhoneGap.JSCallback, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (PhoneGap.JSCallbackPort == null) {
|
||||
PhoneGap.JSCallbackPort = CallbackServer.getPort();
|
||||
if (PhoneGap.JSCallbackPort === null) {
|
||||
PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:");
|
||||
}
|
||||
if (PhoneGap.JSCallbackToken == null) {
|
||||
PhoneGap.JSCallbackToken = CallbackServer.getToken();
|
||||
if (PhoneGap.JSCallbackToken === null) {
|
||||
PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:");
|
||||
}
|
||||
xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true);
|
||||
xmlhttp.send();
|
||||
@@ -674,6 +772,11 @@ PhoneGap.JSCallback = function() {
|
||||
*/
|
||||
PhoneGap.JSCallbackPollingPeriod = 50;
|
||||
|
||||
/**
|
||||
* Flag that can be set by the user to force polling to be used or force XHR to be used.
|
||||
*/
|
||||
PhoneGap.UsePolling = false; // T=use polling, F=use XHR
|
||||
|
||||
/**
|
||||
* This is only for Android.
|
||||
*
|
||||
@@ -682,7 +785,14 @@ PhoneGap.JSCallbackPollingPeriod = 50;
|
||||
* Java to JavaScript.
|
||||
*/
|
||||
PhoneGap.JSCallbackPolling = function() {
|
||||
var msg = CallbackServer.getJavascript();
|
||||
|
||||
// If polling flag was changed, stop using polling from now on
|
||||
if (!PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallback();
|
||||
return;
|
||||
}
|
||||
|
||||
var msg = prompt("", "gap_poll:");
|
||||
if (msg) {
|
||||
setTimeout(function() {
|
||||
try {
|
||||
@@ -715,9 +825,10 @@ PhoneGap.createUUID = function() {
|
||||
|
||||
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) {
|
||||
var i, uuidchar;
|
||||
for (i=0; i<length; i++) {
|
||||
uuidchar = parseInt((Math.random() * 256),0).toString(16);
|
||||
if (uuidchar.length === 1) {
|
||||
uuidchar = "0" + uuidchar;
|
||||
}
|
||||
uuidpart += uuidchar;
|
||||
@@ -729,11 +840,11 @@ 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -747,7 +858,7 @@ PhoneGap.includeJavascript = function(jsfile, successCallback) {
|
||||
var id = document.getElementsByTagName("head")[0];
|
||||
var el = document.createElement('script');
|
||||
el.type = 'text/javascript';
|
||||
if (typeof successCallback == 'function') {
|
||||
if (typeof successCallback === 'function') {
|
||||
el.onload = successCallback;
|
||||
}
|
||||
el.src = jsfile;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
function Position(coords, timestamp) {
|
||||
this.coords = coords;
|
||||
this.timestamp = (timestamp != 'undefined') ? timestamp : new Date().getTime();
|
||||
this.timestamp = (timestamp !== 'undefined') ? timestamp : new Date().getTime();
|
||||
}
|
||||
|
||||
function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
|
||||
@@ -50,7 +50,7 @@ function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/**
|
||||
* The altitude accuracy of the position.
|
||||
*/
|
||||
this.altitudeAccuracy = (altacc != 'undefined') ? altacc : null;
|
||||
this.altitudeAccuracy = (altacc !== 'undefined') ? altacc : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ DroidDB.prototype.completeQuery = function(id, data) {
|
||||
r.rows.resultSet = data;
|
||||
r.rows.length = data.length;
|
||||
try {
|
||||
if (typeof query.successCallback == 'function') {
|
||||
if (typeof query.successCallback === 'function') {
|
||||
query.successCallback(query.tx, r);
|
||||
}
|
||||
} catch (ex) {
|
||||
@@ -83,7 +83,7 @@ DroidDB.prototype.fail = function(reason, id) {
|
||||
tx.queryList = {};
|
||||
|
||||
try {
|
||||
if (typeof query.errorCallback == 'function') {
|
||||
if (typeof query.errorCallback === 'function') {
|
||||
query.errorCallback(query.tx, reason);
|
||||
}
|
||||
} catch (ex) {
|
||||
@@ -99,6 +99,24 @@ DroidDB.prototype.fail = function(reason, id) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 = {};
|
||||
};
|
||||
|
||||
|
||||
var DatabaseShell = function() {
|
||||
};
|
||||
|
||||
@@ -128,22 +146,6 @@ DatabaseShell.prototype.transaction = function(process, errorCallback, successCa
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
@@ -157,10 +159,13 @@ DroidDB_Tx.prototype.queryComplete = function(id) {
|
||||
// If no more outstanding queries, then fire transaction success
|
||||
if (this.successCallback) {
|
||||
var count = 0;
|
||||
for (var i in this.queryList) {
|
||||
count++;
|
||||
var i;
|
||||
for (i in this.queryList) {
|
||||
if (this.queryList.hasOwnProperty(i)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
if (count === 0) {
|
||||
try {
|
||||
this.successCallback();
|
||||
} catch(e) {
|
||||
@@ -220,7 +225,7 @@ var DroidDB_Query = function(tx) {
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute SQL statement
|
||||
@@ -233,7 +238,7 @@ var DroidDB_Query = function(tx) {
|
||||
DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
|
||||
|
||||
// Init params array
|
||||
if (typeof params == 'undefined') {
|
||||
if (typeof params === 'undefined') {
|
||||
params = [];
|
||||
}
|
||||
|
||||
@@ -304,11 +309,13 @@ var CupcakeLocalStorage = function() {
|
||||
var storage = {};
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
var i;
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
|
||||
for(var i = 0; i < result.rows.length; i++) {
|
||||
storage[result.rows.item(i)['id']] = result.rows.item(i)['body'];
|
||||
for(i = 0; i < result.rows.length; i++) {
|
||||
storage[result.rows.item(i).id] = result.rows.item(i).body;
|
||||
}
|
||||
PhoneGap.initializationComplete("cupcakeStorage");
|
||||
});
|
||||
|
||||
},
|
||||
@@ -317,7 +324,7 @@ var CupcakeLocalStorage = function() {
|
||||
}
|
||||
);
|
||||
this.setItem = function(key, val) {
|
||||
console.log('set');
|
||||
//console.log('set');
|
||||
storage[key] = val;
|
||||
|
||||
this.db.transaction(
|
||||
@@ -327,10 +334,10 @@ var CupcakeLocalStorage = function() {
|
||||
transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
this.getItem = function(key) {
|
||||
return storage[key];
|
||||
}
|
||||
};
|
||||
this.removeItem = function(key) {
|
||||
delete storage[key];
|
||||
this.db.transaction(
|
||||
@@ -340,22 +347,31 @@ var CupcakeLocalStorage = function() {
|
||||
transaction.executeSql('DELETE FROM storage where id=?', [key]);
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
this.clear = function() {
|
||||
storage = {};
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
|
||||
transaction.executeSql('DELETE FROM storage', []);
|
||||
}
|
||||
);
|
||||
};
|
||||
} catch(e) {
|
||||
alert("Database error "+e+".");
|
||||
return;
|
||||
}
|
||||
};
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof window.openDatabase == "undefined") {
|
||||
if (typeof window.openDatabase === "undefined") {
|
||||
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
|
||||
if (typeof window.localStorage == "undefined") {
|
||||
if (typeof window.localStorage === "undefined") {
|
||||
navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
|
||||
PhoneGap.waitForInitialization("cupcakeStorage");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
94
framework/assets/js/websocket.js
Executable file
94
framework/assets/js/websocket.js
Executable file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Animesh Kumar (https://github.com/anismiles)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
(function() {
|
||||
|
||||
// window object
|
||||
var global = window;
|
||||
|
||||
// WebSocket Object. All listener methods are cleaned up!
|
||||
var WebSocket = global.WebSocket = function(url) {
|
||||
// get a new websocket object from factory (check com.strumsoft.websocket.WebSocketFactory.java)
|
||||
this.socket = WebSocketFactory.getInstance(url);
|
||||
// store in registry
|
||||
if(this.socket) {
|
||||
WebSocket.store[this.socket.getId()] = this;
|
||||
} else {
|
||||
throw new Error('websocket instantiation failed! Address could be wrong.');
|
||||
}
|
||||
};
|
||||
|
||||
// storage to hold websocket object for later invokation of event methods
|
||||
WebSocket.store = {};
|
||||
|
||||
// static event methods to call event methods on target websocket objects
|
||||
WebSocket.onmessage = function (evt) {
|
||||
WebSocket.store[evt._target]['onmessage'].call(global, evt._data);
|
||||
}
|
||||
|
||||
WebSocket.onopen = function (evt) {
|
||||
WebSocket.store[evt._target]['onopen'].call(global, evt._data);
|
||||
}
|
||||
|
||||
WebSocket.onclose = function (evt) {
|
||||
WebSocket.store[evt._target]['onclose'].call(global, evt._data);
|
||||
}
|
||||
|
||||
WebSocket.onerror = function (evt) {
|
||||
WebSocket.store[evt._target]['onerror'].call(global, evt._data);
|
||||
}
|
||||
|
||||
// instance event methods
|
||||
WebSocket.prototype.send = function(data) {
|
||||
this.socket.send(data);
|
||||
}
|
||||
|
||||
WebSocket.prototype.close = function() {
|
||||
this.socket.close();
|
||||
}
|
||||
|
||||
WebSocket.prototype.getReadyState = function() {
|
||||
this.socket.getReadyState();
|
||||
}
|
||||
///////////// Must be overloaded
|
||||
WebSocket.prototype.onopen = function(){
|
||||
throw new Error('onopen not implemented.');
|
||||
};
|
||||
|
||||
// alerts message pushed from server
|
||||
WebSocket.prototype.onmessage = function(msg){
|
||||
throw new Error('onmessage not implemented.');
|
||||
};
|
||||
|
||||
// alerts message pushed from server
|
||||
WebSocket.prototype.onerror = function(msg){
|
||||
throw new Error('onerror not implemented.');
|
||||
};
|
||||
|
||||
// alert close event
|
||||
WebSocket.prototype.onclose = function(){
|
||||
throw new Error('onclose not implemented.');
|
||||
};
|
||||
})();
|
||||
9
framework/assets/www/index.html
Normal file
9
framework/assets/www/index.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="phonegap.0.9.4.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="PhoneGap" default="help">
|
||||
|
||||
<!-- LOAD VERSION -->
|
||||
<loadfile property="version" srcFile="../VERSION">
|
||||
<filterchain>
|
||||
<striplinebreaks/>
|
||||
</filterchain>
|
||||
</loadfile>
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked in in Version
|
||||
Control Systems. -->
|
||||
@@ -73,7 +80,7 @@
|
||||
<include name="lint.js" />
|
||||
</fileset>
|
||||
<fileset dir="assets/www">
|
||||
<include name="phonegap.js" />
|
||||
<include name="phonegap.${version}.js" />
|
||||
</fileset>
|
||||
</concat>
|
||||
|
||||
@@ -100,15 +107,21 @@
|
||||
<target name="build-uncompressed-javascript">
|
||||
|
||||
<!-- Clean up existing files -->
|
||||
<delete file="assets/www/phonegap.js"/>
|
||||
<delete file="assets/www/phonegap.${version}.min.js"/>
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
<delete file="assets/www/phonegap-uncompressed.js"/>
|
||||
<delete file="assets/www/phonegap.${version}.js"/>
|
||||
|
||||
<!-- Create uncompressed JS file -->
|
||||
<concat destfile="assets/www/phonegap.js">
|
||||
<concat destfile="assets/www/phonegap.${version}.js">
|
||||
<fileset dir="assets/js" includes="phonegap.js.base" />
|
||||
<fileset dir="assets/js" includes="*.js" />
|
||||
</concat>
|
||||
|
||||
<!-- update project files to reference phonegap-x.x.x.js -->
|
||||
<replaceregexp match="phonegap(.*)\.js" replace="phonegap.${version}.js" byline="true">
|
||||
<fileset file="assets/www/index.html" />
|
||||
<fileset file="../example/index.html" />
|
||||
</replaceregexp>
|
||||
</target>
|
||||
|
||||
<!-- Combine JavaScript files into one phonegap-uncompressed.js file.
|
||||
@@ -116,24 +129,30 @@
|
||||
<target name="build-javascript">
|
||||
|
||||
<!-- Clean up existing files -->
|
||||
<delete file="assets/www/phonegap.js"/>
|
||||
<delete file="assets/www/phonegap_${version}.min.js"/>
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
<delete file="assets/www/phonegap-uncompressed.js"/>
|
||||
<delete file="assets/www/phonegap_${version}.js"/>
|
||||
|
||||
<!-- Create uncompressed JS file -->
|
||||
<concat destfile="assets/www/phonegap-uncompressed.js">
|
||||
<concat destfile="assets/www/phonegap.${version}.js">
|
||||
<fileset dir="assets/js" includes="phonegap.js.base" />
|
||||
<fileset dir="assets/js" includes="*.js" />
|
||||
</concat>
|
||||
|
||||
<!-- Compress JS file -->
|
||||
<java jar="${basedir}/../util/yuicompressor/yuicompressor-2.4.2.jar" fork="true" failonerror="true">
|
||||
<arg line="--nomunge -o assets/www/phonegap-tmp.js assets/www/phonegap-uncompressed.js"/>
|
||||
<arg line="--nomunge -o assets/www/phonegap-tmp.js assets/www/phonegap.${version}.js"/>
|
||||
</java>
|
||||
<concat destfile="assets/www/phonegap.js">
|
||||
<concat destfile="assets/www/phonegap.${version}.min.js">
|
||||
<fileset dir="assets/js" includes="header.txt" />
|
||||
<fileset dir="assets/www" includes="phonegap-tmp.js" />
|
||||
</concat>
|
||||
|
||||
<!-- update project files to reference phonegap-x.x.x.min.js -->
|
||||
<replaceregexp match="phonegap(.*)\.js" replace="phonegap.${version}.min.js" byline="true">
|
||||
<fileset file="assets/www/index.html" />
|
||||
<fileset file="../example/index.html" />
|
||||
</replaceregexp>
|
||||
|
||||
<!-- Delete temp file -->
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
@@ -148,7 +167,7 @@
|
||||
"build-javascript" => "build-uncompressed-javascript".
|
||||
-->
|
||||
<target name="jar" depends="build-javascript, compile">
|
||||
<jar jarfile="phonegap.jar" basedir="bin/classes" excludes="com/phonegap/R.class,com/phonegap/R$*.class"/>
|
||||
<jar jarfile="phonegap.${version}.jar" basedir="bin/classes" excludes="com/phonegap/R.class,com/phonegap/R$*.class"/>
|
||||
</target>
|
||||
|
||||
<target name="phonegap_debug" depends="build-javascript, debug">
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 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) 2010-2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
@@ -17,7 +17,7 @@ import com.phonegap.api.PluginResult;
|
||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||
*/
|
||||
public class App extends Plugin {
|
||||
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
@@ -45,6 +45,16 @@ public class App extends Plugin {
|
||||
}
|
||||
else if (action.equals("addService")) {
|
||||
this.addService(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("overrideBackbutton")) {
|
||||
this.overrideBackbutton(args.getBoolean(0));
|
||||
}
|
||||
else if (action.equals("isBackbuttonOverridden")) {
|
||||
boolean b = this.isBackbuttonOverridden();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("exitApp")) {
|
||||
this.exitApp();
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
@@ -132,4 +142,32 @@ public class App extends Plugin {
|
||||
public void addService(String serviceType, String className) {
|
||||
this.ctx.addService(serviceType, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
public void overrideBackbutton(boolean override) {
|
||||
System.out.println("WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!");
|
||||
((DroidGap)this.ctx).bound = override;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether the Android back button is overridden by the user.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isBackbuttonOverridden() {
|
||||
return ((DroidGap)this.ctx).bound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit the Android application.
|
||||
*/
|
||||
public void exitApp() {
|
||||
((DroidGap)this.ctx).finish();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class CallbackServer implements Runnable {
|
||||
/**
|
||||
* Indicates that polling should be used instead of XHR.
|
||||
*/
|
||||
private boolean usePolling;
|
||||
private boolean usePolling = true;
|
||||
|
||||
/**
|
||||
* Security token to prevent other apps from accessing this callback server via XHR
|
||||
@@ -188,54 +188,60 @@ public class CallbackServer implements Runnable {
|
||||
request = xhrReader.readLine();
|
||||
String response = "";
|
||||
//System.out.println("CallbackServerRequest="+request);
|
||||
if (request.contains("GET")) {
|
||||
if (this.active && (request != null)) {
|
||||
if (request.contains("GET")) {
|
||||
|
||||
// Get requested file
|
||||
String[] requestParts = request.split(" ");
|
||||
|
||||
// Must have security token
|
||||
if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
|
||||
//System.out.println("CallbackServer -- Processing GET request");
|
||||
|
||||
// Must have security token
|
||||
if (request.substring(5,41).equals(this.token)) {
|
||||
//System.out.println("CallbackServer -- Processing GET request");
|
||||
|
||||
// Wait until there is some data to send, or send empty data every 10 sec
|
||||
// to prevent XHR timeout on the client
|
||||
synchronized (this) {
|
||||
while (this.empty) {
|
||||
try {
|
||||
this.wait(10000); // prevent timeout from happening
|
||||
//System.out.println("CallbackServer>>> break <<<");
|
||||
break;
|
||||
// Wait until there is some data to send, or send empty data every 10 sec
|
||||
// to prevent XHR timeout on the client
|
||||
synchronized (this) {
|
||||
while (this.empty) {
|
||||
try {
|
||||
this.wait(10000); // prevent timeout from happening
|
||||
//System.out.println("CallbackServer>>> break <<<");
|
||||
break;
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
catch (Exception e) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If server is still running
|
||||
if (this.active) {
|
||||
// If server is still running
|
||||
if (this.active) {
|
||||
|
||||
// If no data, then send 404 back to client before it times out
|
||||
if (this.empty) {
|
||||
//System.out.println("CallbackServer -- sending data 0");
|
||||
response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
|
||||
// If no data, then send 404 back to client before it times out
|
||||
if (this.empty) {
|
||||
//System.out.println("CallbackServer -- sending data 0");
|
||||
response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
|
||||
}
|
||||
else {
|
||||
//System.out.println("CallbackServer -- sending item");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript();
|
||||
}
|
||||
}
|
||||
else {
|
||||
//System.out.println("CallbackServer -- sending item");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript();
|
||||
response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";
|
||||
response = "HTTP/1.1 403 Forbidden\r\n\r\n ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
response = "HTTP/1.1 403 Forbidden\r\n\r\n ";
|
||||
response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
|
||||
}
|
||||
//System.out.println("CallbackServer: response="+response);
|
||||
//System.out.println("CallbackServer: closing output");
|
||||
output.writeBytes(response);
|
||||
output.flush();
|
||||
}
|
||||
else {
|
||||
response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
|
||||
}
|
||||
//System.out.println("CallbackServer: response="+response);
|
||||
//System.out.println("CallbackServer: closing output");
|
||||
output.writeBytes(response);
|
||||
output.flush();
|
||||
output.close();
|
||||
output.close();
|
||||
xhrReader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -201,6 +201,9 @@ public class CameraLauncher extends Plugin {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
|
||||
}
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error capturing image.");
|
||||
@@ -228,6 +231,9 @@ public class CameraLauncher extends Plugin {
|
||||
try {
|
||||
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
this.processPicture(bitmap);
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
this.failPicture("Error retrieving image.");
|
||||
@@ -261,11 +267,15 @@ public class CameraLauncher extends Plugin {
|
||||
byte[] output = Base64.encodeBase64(code);
|
||||
String js_out = new String(output);
|
||||
this.success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId);
|
||||
js_out = null;
|
||||
output = null;
|
||||
code = null;
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
this.failPicture("Error compressing image.");
|
||||
}
|
||||
jpeg_data = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -155,6 +155,9 @@ public abstract class ContactAccessor {
|
||||
else if (key.startsWith("urls")) {
|
||||
map.put("urls", true);
|
||||
}
|
||||
else if (key.startsWith("photos")) {
|
||||
map.put("photos", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
|
||||
@@ -104,22 +104,28 @@ public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
*/
|
||||
public JSONArray search(JSONArray fields, JSONObject options) {
|
||||
String searchTerm = "";
|
||||
int limit = 1;
|
||||
boolean multiple = false;
|
||||
try {
|
||||
searchTerm = options.getString("filter");
|
||||
int limit = Integer.MAX_VALUE;
|
||||
boolean multiple = true;
|
||||
|
||||
if (options != null) {
|
||||
searchTerm = options.optString("filter");
|
||||
if (searchTerm.length()==0) {
|
||||
searchTerm = "%";
|
||||
}
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (multiple) {
|
||||
limit = options.getInt("limit");
|
||||
try {
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (!multiple) {
|
||||
limit = 1;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// Multiple was not specified so we assume the default is true.
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
else {
|
||||
searchTerm = "%";
|
||||
}
|
||||
|
||||
ContentResolver cr = mApp.getContentResolver();
|
||||
@@ -309,7 +315,7 @@ public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
try{
|
||||
im.put("id", cursor.getString(
|
||||
cursor.getColumnIndex(ContactMethods._ID)));
|
||||
im.put("primary", false);
|
||||
im.put("perf", false);
|
||||
im.put("value", cursor.getString(
|
||||
cursor.getColumnIndex(ContactMethodsColumns.DATA)));
|
||||
im.put("type", getContactType(cursor.getInt(
|
||||
@@ -343,10 +349,6 @@ public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
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);
|
||||
@@ -401,7 +403,7 @@ public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
phone = new JSONObject();
|
||||
try{
|
||||
phone.put("id", cursor.getString(cursor.getColumnIndex(Phones._ID)));
|
||||
phone.put("primary", false);
|
||||
phone.put("perf", false);
|
||||
phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER)));
|
||||
phone.put("type", getPhoneType(cursor.getInt(cursor.getColumnIndex(Phones.TYPE))));
|
||||
phones.put(phone);
|
||||
@@ -430,7 +432,7 @@ public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
email = new JSONObject();
|
||||
try{
|
||||
email.put("id", cursor.getString(cursor.getColumnIndex(ContactMethods._ID)));
|
||||
email.put("primary", false);
|
||||
email.put("perf", 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)));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -42,7 +42,7 @@ public class ContactManager extends Plugin {
|
||||
|
||||
try {
|
||||
if (action.equals("search")) {
|
||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.getJSONObject(1));
|
||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
|
||||
return new PluginResult(status, res, "navigator.service.contacts.cast");
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "0.9.3"; // PhoneGap version
|
||||
public static String phonegapVersion = "0.9.4"; // PhoneGap version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
|
||||
@@ -8,15 +8,9 @@
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class provides file directory utilities.
|
||||
@@ -78,29 +72,6 @@ public class DirectoryManager {
|
||||
return (freeSpace);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
// Make sure SD card exists
|
||||
if ((testSaveLocationExists()) && (!directoryName.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), directoryName);
|
||||
status = newPath.mkdir();
|
||||
status = true;
|
||||
}
|
||||
|
||||
// If no SD card or invalid dir name
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if SD card exists.
|
||||
@@ -123,95 +94,6 @@ public class DirectoryManager {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
// Make sure SD card exists
|
||||
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
|
||||
File path = Environment.getExternalStorageDirectory();
|
||||
File newPath = constructFilePaths(path.toString(), fileName);
|
||||
checker.checkDelete(newPath.toString());
|
||||
|
||||
// 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
|
||||
try{
|
||||
for (int i=0; i < listfile.length; i++){
|
||||
File deletedFile = new File (newPath.toString()+"/"+listfile[i].toString());
|
||||
deletedFile.delete();
|
||||
}
|
||||
newPath.delete();
|
||||
Log.i("DirectoryManager deleteDirectory", fileName);
|
||||
status = true;
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If dir not a directory, then error
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
// 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);
|
||||
newPath.delete();
|
||||
status = true;
|
||||
}catch (SecurityException se){
|
||||
se.printStackTrace();
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
// If not a file, then error
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If no SD card
|
||||
else {
|
||||
status = false;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new file object from two file paths.
|
||||
*
|
||||
@@ -229,45 +111,4 @@ public class DirectoryManager {
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will determine the file properties of the file specified
|
||||
* by the filePath. Creates a JSONObject with name, lastModifiedDate and
|
||||
* size properties.
|
||||
*
|
||||
* @param filePath the file to get the properties of
|
||||
* @return a JSONObject with the files properties
|
||||
*/
|
||||
protected static JSONObject getFile(String filePath) {
|
||||
File fp = new File(filePath);
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("name", fp.getAbsolutePath());
|
||||
obj.put("lastModifiedDate", new Date(fp.lastModified()).toString());
|
||||
obj.put("size", fp.length());
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns a JSONArray of file paths. Android's default
|
||||
* location where files can be written is Environment.getExternalStorageDirectory().
|
||||
* We are returning a array with one element so the interface can remain
|
||||
* consistent with BlackBerry as they have two areas where files can be
|
||||
* written.
|
||||
*
|
||||
* @return an array of file paths
|
||||
*/
|
||||
protected static JSONArray getRootPaths() {
|
||||
JSONArray retVal = new JSONArray();
|
||||
String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
|
||||
retVal.put(path);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,11 +3,12 @@
|
||||
* 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) 2010-2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
@@ -15,6 +16,8 @@ import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Picture;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
@@ -24,9 +27,11 @@ import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebView.PictureListener;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
@@ -53,8 +58,8 @@ import com.phonegap.api.PhonegapActivity;
|
||||
* super.onCreate(savedInstanceState);
|
||||
*
|
||||
* // Set properties for activity
|
||||
* super.setProperty("loadingDialog", "Title,Message"); // show loading dialog
|
||||
* super.setProperty("errorUrl", "file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().
|
||||
* super.setStringProperty("loadingDialog", "Title,Message"); // show loading dialog
|
||||
* super.setStringProperty("errorUrl", "file:///android_asset/www/error.html"); // if error loading file in super.loadUrl().
|
||||
*
|
||||
* // Initialize activity
|
||||
* super.init();
|
||||
@@ -66,7 +71,7 @@ import com.phonegap.api.PhonegapActivity;
|
||||
* super.appView.clearCache(true);
|
||||
*
|
||||
* // Load your application
|
||||
* super.setProperty("splashscreen", R.drawable.splash); // load splash.jpg image from the resource drawable directory
|
||||
* super.setIntegerProperty("splashscreen", R.drawable.splash); // load splash.jpg image from the resource drawable directory
|
||||
* super.loadUrl("file:///android_asset/www/index.html", 3000); // show splash screen 3 sec before loading app
|
||||
* }
|
||||
* }
|
||||
@@ -75,30 +80,30 @@ import com.phonegap.api.PhonegapActivity;
|
||||
*
|
||||
* // Display a native loading dialog. Format for value = "Title,Message".
|
||||
* // (String - default=null)
|
||||
* super.setProperty("loadingDialog", "Wait,Loading Demo...");
|
||||
* super.setStringProperty("loadingDialog", "Wait,Loading Demo...");
|
||||
*
|
||||
* // Hide loadingDialog when page loaded instead of when deviceready event
|
||||
* // occurs. (Boolean - default=false)
|
||||
* super.setProperty("hideLoadingDialogOnPage", true);
|
||||
* super.setBooleanProperty("hideLoadingDialogOnPage", true);
|
||||
*
|
||||
* // Cause all links on web page to be loaded into existing web view,
|
||||
* // instead of being loaded into new browser. (Boolean - default=false)
|
||||
* super.setProperty("loadInWebView", true);
|
||||
* super.setBooleanProperty("loadInWebView", true);
|
||||
*
|
||||
* // Load a splash screen image from the resource drawable directory.
|
||||
* // (Integer - default=0)
|
||||
* super.setProperty("splashscreen", R.drawable.splash);
|
||||
* super.setIntegerProperty("splashscreen", R.drawable.splash);
|
||||
*
|
||||
* // Time in msec to wait before triggering a timeout error when loading
|
||||
* // with super.loadUrl(). (Integer - default=20000)
|
||||
* super.setProperty("loadUrlTimeoutValue", 60000);
|
||||
* super.setIntegerProperty("loadUrlTimeoutValue", 60000);
|
||||
*
|
||||
* // URL to load if there's an error loading specified URL with loadUrl().
|
||||
* // Should be a local URL starting with file://. (String - default=null)
|
||||
* super.setProperty("errorUrl", "file:///android_asset/www/error.html");
|
||||
* super.setStringProperty("errorUrl", "file:///android_asset/www/error.html");
|
||||
*
|
||||
* // Enable app to keep running in background. (Boolean - default=true)
|
||||
* super.setProperty("keepRunning", false);
|
||||
* super.setBooleanProperty("keepRunning", false);
|
||||
*/
|
||||
public class DroidGap extends PhonegapActivity {
|
||||
|
||||
@@ -107,7 +112,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
protected WebViewClient webViewClient;
|
||||
|
||||
private LinearLayout root;
|
||||
private BrowserKey mKey;
|
||||
boolean bound = false;
|
||||
public CallbackServer callbackServer;
|
||||
protected PluginManager pluginManager;
|
||||
protected boolean cancelLoadUrl = false;
|
||||
@@ -177,6 +182,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
// Setup the hardware volume controls to handle volume control
|
||||
setVolumeControlStream(AudioManager.STREAM_MUSIC);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,12 +269,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
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");
|
||||
|
||||
this.addService("App", "com.phonegap.App");
|
||||
this.addService("Geolocation", "com.phonegap.GeoBroker");
|
||||
@@ -284,6 +285,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.addService("Notification", "com.phonegap.Notification");
|
||||
this.addService("Storage", "com.phonegap.Storage");
|
||||
this.addService("Temperature", "com.phonegap.TempListener");
|
||||
this.addService("FileTransfer", "com.phonegap.FileTransfer");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -653,10 +655,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
// Clean up objects
|
||||
if (this.mKey != null) {
|
||||
}
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onDestroy();
|
||||
|
||||
@@ -758,6 +756,69 @@ public class DroidGap extends PhonegapActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to display a prompt dialog to the user.
|
||||
* If the client returns true, WebView will assume that the client will
|
||||
* handle the prompt dialog and call the appropriate JsPromptResult method.
|
||||
*
|
||||
* @param view
|
||||
* @param url
|
||||
* @param message
|
||||
* @param defaultValue
|
||||
* @param result
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
|
||||
// Calling PluginManager.exec() to call a native service using
|
||||
// prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
||||
if (defaultValue.substring(0, 4).equals("gap:")) {
|
||||
JSONArray array;
|
||||
try {
|
||||
array = new JSONArray(defaultValue.substring(4));
|
||||
String service = array.getString(0);
|
||||
String action = array.getString(1);
|
||||
String callbackId = array.getString(2);
|
||||
boolean async = array.getBoolean(3);
|
||||
String r = pluginManager.exec(service, action, callbackId, message, async);
|
||||
result.confirm(r);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (defaultValue.equals("gap_poll:")) {
|
||||
String r = callbackServer.getJavascript();
|
||||
result.confirm(r);
|
||||
}
|
||||
|
||||
// Calling into CallbackServer
|
||||
else if (defaultValue.equals("gap_callbackServer:")) {
|
||||
String r = "";
|
||||
if (message.equals("usePolling")) {
|
||||
r = ""+callbackServer.usePolling();
|
||||
}
|
||||
else if (message.equals("restartServer")) {
|
||||
callbackServer.restartServer();
|
||||
}
|
||||
else if (message.equals("getPort")) {
|
||||
r = Integer.toString(callbackServer.getPort());
|
||||
}
|
||||
else if (message.equals("getToken")) {
|
||||
r = callbackServer.getToken();
|
||||
}
|
||||
result.confirm(r);
|
||||
}
|
||||
|
||||
// Show dialog
|
||||
else {
|
||||
//@TODO:
|
||||
result.confirm("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -901,8 +962,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If http, https or file
|
||||
else if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("file://")) {
|
||||
// All else
|
||||
else {
|
||||
|
||||
int i = url.lastIndexOf('/');
|
||||
String newBaseUrl = url;
|
||||
@@ -911,6 +972,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
// If our app or file:, then load into our webview
|
||||
// NOTE: This replaces our app with new URL. When BACK is pressed,
|
||||
// our app is reloaded and restarted. All state is lost.
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || this.ctx.baseUrl.equals(newBaseUrl)) {
|
||||
this.ctx.appView.loadUrl(url);
|
||||
}
|
||||
@@ -927,8 +990,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -952,7 +1013,12 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If splash screen is showing, clear it
|
||||
if (this.ctx.splashscreen != 0) {
|
||||
this.ctx.splashscreen = 0;
|
||||
appView.setBackgroundResource(0);
|
||||
appView.setPictureListener(new PictureListener(){
|
||||
public void onNewPicture(WebView viewtwo, Picture picture) {
|
||||
appView.setBackgroundResource(0);
|
||||
appView.setPictureListener(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Stop "app loading" spinner if showing
|
||||
@@ -1005,8 +1071,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
|
||||
// If back key is bound, then send event to JavaScript
|
||||
if (mKey.isBound()) {
|
||||
this.appView.loadUrl("javascript:document.keyEvent.backTrigger()");
|
||||
if (this.bound) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');");
|
||||
}
|
||||
|
||||
// If not bound
|
||||
@@ -1026,12 +1092,12 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
// If menu key
|
||||
else if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
appView.loadUrl("javascript:keyEvent.menuTrigger()");
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');");
|
||||
}
|
||||
|
||||
// If search key
|
||||
else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
appView.loadUrl("javascript:keyEvent.searchTrigger()");
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');");
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
359
framework/src/com/phonegap/FileTransfer.java
Normal file
359
framework/src/com/phonegap/FileTransfer.java
Normal file
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.CookieManager;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
public class FileTransfer extends Plugin {
|
||||
|
||||
private static final String LOG_TAG = "FileUploader";
|
||||
private static final String LINE_START = "--";
|
||||
private static final String LINE_END = "\r\n";
|
||||
private static final String BOUNDRY = "*****";
|
||||
|
||||
public static int FILE_NOT_FOUND_ERR = 1;
|
||||
public static int INVALID_URL_ERR = 2;
|
||||
public static int CONNECTION_ERR = 3;
|
||||
|
||||
private SSLSocketFactory defaultSSLSocketFactory = null;
|
||||
private HostnameVerifier defaultHostnameVerifier = null;
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
String file = null;
|
||||
String server = null;
|
||||
try {
|
||||
file = args.getString(0);
|
||||
server = args.getString(1);
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Missing filename or server name");
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing filename or server name");
|
||||
}
|
||||
|
||||
// Setup the options
|
||||
String fileKey = null;
|
||||
String fileName = null;
|
||||
String mimeType = null;
|
||||
|
||||
fileKey = getArgument(args, 2, "file");
|
||||
fileName = getArgument(args, 3, "image.jpg");
|
||||
mimeType = getArgument(args, 4, "image/jpeg");
|
||||
|
||||
try {
|
||||
JSONObject params = args.optJSONObject(5);
|
||||
boolean trustEveryone = args.optBoolean(6);
|
||||
|
||||
if (action.equals("upload")) {
|
||||
FileUploadResult r = upload(file, server, fileKey, fileName, mimeType, params, trustEveryone);
|
||||
Log.d(LOG_TAG, "****** About to return a result from upload");
|
||||
return new PluginResult(PluginResult.Status.OK, r.toJSONObject());
|
||||
} else {
|
||||
return new PluginResult(PluginResult.Status.INVALID_ACTION);
|
||||
}
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
JSONObject error = createFileUploadError(FILE_NOT_FOUND_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
JSONObject error = createFileUploadError(INVALID_URL_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (SSLException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
Log.d(LOG_TAG, "Got my ssl exception!!!");
|
||||
JSONObject error = createFileUploadError(CONNECTION_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (IOException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
JSONObject error = createFileUploadError(CONNECTION_ERR);
|
||||
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
// always verify the host - don't check for certificate
|
||||
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This function will install a trust manager that will blindly trust all SSL
|
||||
* certificates. The reason this code is being added is to enable developers
|
||||
* to do development using self signed SSL certificates on their web server.
|
||||
*
|
||||
* The standard HttpsURLConnection class will throw an exception on self
|
||||
* signed certificates if this code is not run.
|
||||
*/
|
||||
private void trustAllHosts() {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
|
||||
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
|
||||
return new java.security.cert.X509Certificate[] {};
|
||||
}
|
||||
|
||||
public void checkClientTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] chain,
|
||||
String authType) throws CertificateException {
|
||||
}
|
||||
} };
|
||||
|
||||
// Install the all-trusting trust manager
|
||||
try {
|
||||
// Backup the current SSL socket factory
|
||||
defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
|
||||
// Install our all trusting manager
|
||||
SSLContext sc = SSLContext.getInstance("TLS");
|
||||
sc.init(null, trustAllCerts, new java.security.SecureRandom());
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an error object based on the passed in errorCode
|
||||
* @param errorCode the error
|
||||
* @return JSONObject containing the error
|
||||
*/
|
||||
private JSONObject createFileUploadError(int errorCode) {
|
||||
JSONObject error = null;
|
||||
try {
|
||||
error = new JSONObject();
|
||||
error.put("code", errorCode);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to read a parameter from the list of JSON args.
|
||||
* @param args the args passed to the Plugin
|
||||
* @param position the position to retrieve the arg from
|
||||
* @param defaultString the default to be used if the arg does not exist
|
||||
* @return String with the retrieved value
|
||||
*/
|
||||
private String getArgument(JSONArray args, int position, String defaultString) {
|
||||
String arg = defaultString;
|
||||
if(args.length() >= position) {
|
||||
arg = args.optString(position);
|
||||
if (arg == null || "null".equals(arg)) {
|
||||
arg = defaultString;
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the specified file to the server URL provided using an HTTP
|
||||
* multipart request.
|
||||
* @param file Full path of the file on the file system
|
||||
* @param server URL of the server to receive the file
|
||||
* @param fileKey Name of file request parameter
|
||||
* @param fileName File name to be used on server
|
||||
* @param mimeType Describes file content type
|
||||
* @param params key:value pairs of user-defined parameters
|
||||
* @return FileUploadResult containing result of upload request
|
||||
*/
|
||||
public FileUploadResult upload(String file, String server, final String fileKey, final String fileName,
|
||||
final String mimeType, JSONObject params, boolean trustEveryone) throws IOException, SSLException {
|
||||
// Create return object
|
||||
FileUploadResult result = new FileUploadResult();
|
||||
|
||||
// Get a input stream of the file on the phone
|
||||
InputStream fileInputStream = getPathFromUri(file);
|
||||
|
||||
HttpURLConnection conn = null;
|
||||
DataOutputStream dos = null;
|
||||
|
||||
int bytesRead, bytesAvailable, bufferSize;
|
||||
long totalBytes;
|
||||
byte[] buffer;
|
||||
int maxBufferSize = 8096;
|
||||
|
||||
//------------------ CLIENT REQUEST
|
||||
// open a URL connection to the server
|
||||
URL url = new URL(server);
|
||||
|
||||
// Open a HTTP connection to the URL based on protocol
|
||||
if (url.getProtocol().toLowerCase().equals("https")) {
|
||||
// Using standard HTTPS connection. Will not allow self signed certificate
|
||||
if (!trustEveryone) {
|
||||
conn = (HttpsURLConnection) url.openConnection();
|
||||
}
|
||||
// Use our HTTPS connection that blindly trusts everyone.
|
||||
// This should only be used in debug environments
|
||||
else {
|
||||
// Setup the HTTPS connection class to trust everyone
|
||||
trustAllHosts();
|
||||
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
|
||||
// Save the current hostnameVerifier
|
||||
defaultHostnameVerifier = https.getHostnameVerifier();
|
||||
// Setup the connection not to verify hostnames
|
||||
https.setHostnameVerifier(DO_NOT_VERIFY);
|
||||
conn = https;
|
||||
}
|
||||
}
|
||||
// Return a standard HTTP conneciton
|
||||
else {
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
}
|
||||
|
||||
// Allow Inputs
|
||||
conn.setDoInput(true);
|
||||
|
||||
// Allow Outputs
|
||||
conn.setDoOutput(true);
|
||||
|
||||
// Don't use a cached copy.
|
||||
conn.setUseCaches(false);
|
||||
|
||||
// Use a post method.
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Connection", "Keep-Alive");
|
||||
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY);
|
||||
|
||||
// Set the cookies on the response
|
||||
String cookie = CookieManager.getInstance().getCookie(server);
|
||||
if (cookie != null) {
|
||||
conn.setRequestProperty("Cookie", cookie);
|
||||
}
|
||||
|
||||
dos = new DataOutputStream( conn.getOutputStream() );
|
||||
|
||||
// Send any extra parameters
|
||||
try {
|
||||
for (Iterator iter = params.keys(); iter.hasNext();) {
|
||||
Object key = iter.next();
|
||||
dos.writeBytes(LINE_START + BOUNDRY + LINE_END);
|
||||
dos.writeBytes("Content-Disposition: form-data; name=\"" + key.toString() + "\"; ");
|
||||
dos.writeBytes(LINE_END + LINE_END);
|
||||
dos.writeBytes(params.getString(key.toString()));
|
||||
dos.writeBytes(LINE_END);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
dos.writeBytes(LINE_START + BOUNDRY + LINE_END);
|
||||
dos.writeBytes("Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"" + fileName +"\"" + LINE_END);
|
||||
dos.writeBytes("Content-Type: " + mimeType + LINE_END);
|
||||
dos.writeBytes(LINE_END);
|
||||
|
||||
// create a buffer of maximum size
|
||||
bytesAvailable = fileInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
buffer = new byte[bufferSize];
|
||||
|
||||
// read file and write it into form...
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
totalBytes = 0;
|
||||
|
||||
while (bytesRead > 0) {
|
||||
totalBytes += bytesRead;
|
||||
result.setBytesSent(totalBytes);
|
||||
dos.write(buffer, 0, bufferSize);
|
||||
bytesAvailable = fileInputStream.available();
|
||||
bufferSize = Math.min(bytesAvailable, maxBufferSize);
|
||||
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
|
||||
}
|
||||
|
||||
// send multipart form data necesssary after file data...
|
||||
dos.writeBytes(LINE_END);
|
||||
dos.writeBytes(LINE_START + BOUNDRY + LINE_START + LINE_END);
|
||||
|
||||
// close streams
|
||||
fileInputStream.close();
|
||||
dos.flush();
|
||||
dos.close();
|
||||
|
||||
//------------------ read the SERVER RESPONSE
|
||||
StringBuffer responseString = new StringBuffer("");
|
||||
DataInputStream inStream = new DataInputStream ( conn.getInputStream() );
|
||||
String line;
|
||||
while (( line = inStream.readLine()) != null) {
|
||||
responseString.append(line);
|
||||
}
|
||||
Log.d(LOG_TAG, "got response from server");
|
||||
Log.d(LOG_TAG, responseString.toString());
|
||||
|
||||
// send request and retrieve response
|
||||
result.setResponseCode(conn.getResponseCode());
|
||||
result.setResponse(responseString.toString());
|
||||
|
||||
inStream.close();
|
||||
conn.disconnect();
|
||||
|
||||
// Revert back to the proper verifier and socket factories
|
||||
if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
|
||||
((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier);
|
||||
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input stream based on file path or content:// uri
|
||||
*
|
||||
* @param path
|
||||
* @return an input stream
|
||||
* @throws FileNotFoundException
|
||||
*/
|
||||
private InputStream getPathFromUri(String path) throws FileNotFoundException {
|
||||
if (path.startsWith("content:")) {
|
||||
Uri uri = Uri.parse(path);
|
||||
return ctx.getContentResolver().openInputStream(uri);
|
||||
}
|
||||
else {
|
||||
return new FileInputStream(path);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
52
framework/src/com/phonegap/FileUploadResult.java
Normal file
52
framework/src/com/phonegap/FileUploadResult.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* Encapsulates the result and/or status of uploading a file to a remote server.
|
||||
*/
|
||||
public class FileUploadResult {
|
||||
|
||||
private long bytesSent = 0; // bytes sent
|
||||
private int responseCode = -1; // HTTP response code
|
||||
private String response = null; // HTTP response
|
||||
|
||||
public long getBytesSent() {
|
||||
return bytesSent;
|
||||
}
|
||||
|
||||
public void setBytesSent(long bytes) {
|
||||
this.bytesSent = bytes;
|
||||
}
|
||||
|
||||
public int getResponseCode() {
|
||||
return responseCode;
|
||||
}
|
||||
|
||||
public void setResponseCode(int responseCode) {
|
||||
this.responseCode = responseCode;
|
||||
}
|
||||
|
||||
public String getResponse() {
|
||||
return response;
|
||||
}
|
||||
|
||||
public void setResponse(String response) {
|
||||
this.response = response;
|
||||
}
|
||||
|
||||
public JSONObject toJSONObject() throws JSONException {
|
||||
return new JSONObject(
|
||||
"{bytesSent:" + bytesSent +
|
||||
",responseCode:" + responseCode +
|
||||
",response:" + JSONObject.quote(response) + "}");
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@@ -16,16 +18,25 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import com.phonegap.file.EncodingException;
|
||||
import com.phonegap.file.FileExistsException;
|
||||
import com.phonegap.file.InvalidModificationException;
|
||||
import com.phonegap.file.NoModificationAllowedException;
|
||||
import com.phonegap.file.TypeMismatchException;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
private static final String LOG_TAG = "FileUtils";
|
||||
|
||||
public static int NOT_FOUND_ERR = 1;
|
||||
public static int SECURITY_ERR = 2;
|
||||
public static int ABORT_ERR = 3;
|
||||
@@ -35,7 +46,16 @@ public class FileUtils extends Plugin {
|
||||
public static int NO_MODIFICATION_ALLOWED_ERR = 6;
|
||||
public static int INVALID_STATE_ERR = 7;
|
||||
public static int SYNTAX_ERR = 8;
|
||||
public static int INVALID_MODIFICATION_ERR = 9;
|
||||
public static int QUOTA_EXCEEDED_ERR = 10;
|
||||
public static int TYPE_MISMATCH_ERR = 11;
|
||||
public static int PATH_EXISTS_ERR = 12;
|
||||
|
||||
public static int TEMPORARY = 0;
|
||||
public static int PERSISTENT = 1;
|
||||
public static int RESOURCE = 2;
|
||||
public static int APPLICATION = 3;
|
||||
|
||||
FileReader f_in;
|
||||
FileWriter f_out;
|
||||
|
||||
@@ -59,114 +79,763 @@ public class FileUtils extends Plugin {
|
||||
//System.out.println("FileUtils.execute("+action+")");
|
||||
|
||||
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("getRootPaths")) {
|
||||
return new PluginResult(status, DirectoryManager.getRootPaths());
|
||||
}
|
||||
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);
|
||||
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("readAsText")) {
|
||||
try {
|
||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, s);
|
||||
} 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 (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 (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("write")) {
|
||||
try {
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getLong(2));
|
||||
return new PluginResult(status, fileSize);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("truncate")) {
|
||||
try {
|
||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
||||
return new PluginResult(status, fileSize);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("requestFileSystem")) {
|
||||
long size = args.optLong(1);
|
||||
if (size != 0) {
|
||||
if (size > DirectoryManager.getFreeDiskSpace()) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.QUOTA_EXCEEDED_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
}
|
||||
}
|
||||
JSONObject obj = requestFileSystem(args.getInt(0));
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castFS");
|
||||
}
|
||||
}
|
||||
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("resolveLocalFileSystemURI")) {
|
||||
JSONObject obj = resolveLocalFileSystemURI(args.getString(0));
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
|
||||
}
|
||||
}
|
||||
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("getMetadata")) {
|
||||
JSONObject obj = getMetadata(args.getString(0));
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castDate");
|
||||
}
|
||||
}
|
||||
else if (action.equals("write")) {
|
||||
try {
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getLong(2));
|
||||
return new PluginResult(status, fileSize);
|
||||
} 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("getFileMetadata")) {
|
||||
JSONObject obj = getFileMetadata(args.getString(0));
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castDate");
|
||||
}
|
||||
}
|
||||
else if (action.equals("truncate")) {
|
||||
try {
|
||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
||||
return new PluginResult(status, fileSize);
|
||||
} 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("getParent")) {
|
||||
JSONObject obj = getParent(args.getString(0));
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
|
||||
}
|
||||
}
|
||||
else if (action.equals("getFile")) {
|
||||
JSONObject obj = DirectoryManager.getFile(args.getString(0));
|
||||
return new PluginResult(status, obj);
|
||||
else if (action.equals("getDirectory")) {
|
||||
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), true);
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
|
||||
}
|
||||
else if (action.equals("getFile")) {
|
||||
JSONObject obj = getFile(args.getString(0), args.getString(1), args.optJSONObject(2), false);
|
||||
return new PluginResult(status, obj, "window.localFileSystem._castEntry");
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
boolean success;
|
||||
|
||||
success = remove(args.getString(0));
|
||||
|
||||
if (success) {
|
||||
return new PluginResult(status);
|
||||
} else {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
}
|
||||
}
|
||||
else if (action.equals("removeRecursively")) {
|
||||
boolean success = removeRecursively(args.getString(0));
|
||||
if (success) {
|
||||
return new PluginResult(status);
|
||||
} else {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
}
|
||||
}
|
||||
else if (action.equals("moveTo")) {
|
||||
JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), true);
|
||||
return new PluginResult(status, entry, "window.localFileSystem._castEntry");
|
||||
}
|
||||
else if (action.equals("copyTo")) {
|
||||
JSONObject entry = transferTo(args.getString(0), args.getJSONObject(1), args.optString(2), false);
|
||||
return new PluginResult(status, entry, "window.localFileSystem._castEntry");
|
||||
}
|
||||
else if (action.equals("readEntries")) {
|
||||
JSONArray entries = readEntries(args.getString(0));
|
||||
return new PluginResult(status, entries, "window.localFileSystem._castEntries");
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (FileNotFoundException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.NOT_FOUND_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (FileExistsException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.PATH_EXISTS_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (NoModificationAllowedException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (JSONException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.NO_MODIFICATION_ALLOWED_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (InvalidModificationException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (MalformedURLException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (IOException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.INVALID_MODIFICATION_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (EncodingException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.ENCODING_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
} catch (TypeMismatchException e) {
|
||||
JSONObject error = new JSONObject().put("code", FileUtils.TYPE_MISMATCH_ERR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, error);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the user to look up the Entry for a file or directory referred to by a local URI.
|
||||
*
|
||||
* @param url of the file/directory to look up
|
||||
* @return a JSONObject representing a Entry from the filesystem
|
||||
* @throws MalformedURLException if the url is not valid
|
||||
* @throws FileNotFoundException if the file does not exist
|
||||
* @throws IOException if the user can't read the file
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException {
|
||||
// Test to see if this is a valid URL first
|
||||
@SuppressWarnings("unused")
|
||||
URL testUrl = new URL(url);
|
||||
|
||||
File fp = null;
|
||||
if (url.startsWith("file://")) {
|
||||
fp = new File(url.substring(7, url.length()));
|
||||
} else {
|
||||
fp = new File(url);
|
||||
}
|
||||
if (!fp.exists()) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
if (!fp.canRead()) {
|
||||
throw new IOException();
|
||||
}
|
||||
return getEntry(fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the list of files from this directory.
|
||||
*
|
||||
* @param fileName the directory to read from
|
||||
* @return a JSONArray containing JSONObjects that represent Entry objects.
|
||||
* @throws FileNotFoundException if the directory is not found.
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONArray readEntries(String fileName) throws FileNotFoundException, JSONException {
|
||||
File fp = new File(fileName);
|
||||
|
||||
if (!fp.exists()) {
|
||||
// The directory we are listing doesn't exist so we should fail.
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
|
||||
JSONArray entries = new JSONArray();
|
||||
|
||||
if (fp.isDirectory()) {
|
||||
File[] files = fp.listFiles();
|
||||
for (int i=0; i<files.length; i++) {
|
||||
entries.put(getEntry(files[i]));
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* A setup method that handles the move/copy of files/directories
|
||||
*
|
||||
* @param fileName to be copied/moved
|
||||
* @param newParent is the location where the file will be copied/moved to
|
||||
* @param newName for the file directory to be called, if null use existing file name
|
||||
* @param move if false do a copy, if true do a move
|
||||
* @return a Entry object
|
||||
* @throws FileExistsException
|
||||
* @throws NoModificationAllowedException
|
||||
* @throws IOException
|
||||
* @throws InvalidModificationException
|
||||
* @throws EncodingException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject transferTo(String fileName, JSONObject newParent, String newName, boolean move) throws JSONException, FileExistsException, NoModificationAllowedException, IOException, InvalidModificationException, EncodingException {
|
||||
// Check for invalid file name
|
||||
if (newName != null && newName.contains(":")) {
|
||||
throw new EncodingException("Bad file name");
|
||||
}
|
||||
|
||||
File source = new File(fileName);
|
||||
|
||||
if (!source.exists()) {
|
||||
// The file/directory we are copying doesn't exist so we should fail.
|
||||
throw new FileNotFoundException("The source does not exist");
|
||||
}
|
||||
|
||||
File destinationDir = new File(newParent.getString("fullPath"));
|
||||
if (!destinationDir.exists()) {
|
||||
// The destination does not exist so we should fail.
|
||||
throw new FileNotFoundException("The source does not exist");
|
||||
}
|
||||
|
||||
// Figure out where we should be copying to
|
||||
File destination = createDestination(newName, source, destinationDir);
|
||||
|
||||
//Log.d(LOG_TAG, "Source: " + source.getAbsolutePath());
|
||||
//Log.d(LOG_TAG, "Destin: " + destination.getAbsolutePath());
|
||||
|
||||
// Check to see if source and destination are the same file
|
||||
if (source.getAbsolutePath().equals(destination.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy a file onto itself");
|
||||
}
|
||||
|
||||
if (source.isDirectory()) {
|
||||
if (move) {
|
||||
return moveDirectory(source, destination);
|
||||
} else {
|
||||
return copyDirectory(source, destination);
|
||||
}
|
||||
} else {
|
||||
if (move) {
|
||||
return moveFile(source, destination);
|
||||
} else {
|
||||
return copyFile(source, destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the destination File object based on name passed in
|
||||
*
|
||||
* @param newName for the file directory to be called, if null use existing file name
|
||||
* @param fp represents the source file
|
||||
* @param destination represents the destination file
|
||||
* @return a File object that represents the destination
|
||||
*/
|
||||
private File createDestination(String newName, File fp, File destination) {
|
||||
File destFile = null;
|
||||
|
||||
// I know this looks weird but it is to work around a JSON bug.
|
||||
if ("null".equals(newName) || "".equals(newName) ) {
|
||||
newName = null;
|
||||
}
|
||||
|
||||
if (newName != null) {
|
||||
destFile = new File(destination.getAbsolutePath() + File.separator + newName);
|
||||
} else {
|
||||
destFile = new File(destination.getAbsolutePath() + File.separator + fp.getName());
|
||||
}
|
||||
return destFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a file
|
||||
*
|
||||
* @param srcFile file to be copied
|
||||
* @param destFile destination to be copied to
|
||||
* @return a FileEntry object
|
||||
* @throws IOException
|
||||
* @throws InvalidModificationException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject copyFile(File srcFile, File destFile) throws IOException, InvalidModificationException, JSONException {
|
||||
// Renaming a file to an existing directory should fail
|
||||
if (destFile.exists() && destFile.isDirectory()) {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
}
|
||||
|
||||
FileChannel input = new FileInputStream(srcFile).getChannel();
|
||||
FileChannel output = new FileOutputStream(destFile).getChannel();
|
||||
|
||||
input.transferTo(0, input.size(), output);
|
||||
|
||||
input.close();
|
||||
output.close();
|
||||
|
||||
/*
|
||||
if (srcFile.length() != destFile.length()) {
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
return getEntry(destFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a directory
|
||||
*
|
||||
* @param srcDir directory to be copied
|
||||
* @param destinationDir destination to be copied to
|
||||
* @return a DirectoryEntry object
|
||||
* @throws JSONException
|
||||
* @throws IOException
|
||||
* @throws NoModificationAllowedException
|
||||
* @throws InvalidModificationException
|
||||
*/
|
||||
private JSONObject copyDirectory(File srcDir, File destinationDir) throws JSONException, IOException, NoModificationAllowedException, InvalidModificationException {
|
||||
// Renaming a file to an existing directory should fail
|
||||
if (destinationDir.exists() && destinationDir.isFile()) {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
}
|
||||
|
||||
// Check to make sure we are not copying the directory into itself
|
||||
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy itself into itself");
|
||||
}
|
||||
|
||||
// See if the destination directory exists. If not create it.
|
||||
if (!destinationDir.exists()) {
|
||||
if (!destinationDir.mkdir()) {
|
||||
// If we can't create the directory then fail
|
||||
throw new NoModificationAllowedException("Couldn't create the destination direcotry");
|
||||
}
|
||||
}
|
||||
|
||||
for (File file : srcDir.listFiles()) {
|
||||
if (file.isDirectory()) {
|
||||
copyDirectory(file, destinationDir);
|
||||
} else {
|
||||
File destination = new File(destinationDir.getAbsoluteFile() + File.separator + file.getName());
|
||||
copyFile(file, destination);
|
||||
}
|
||||
}
|
||||
|
||||
return getEntry(destinationDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a file
|
||||
*
|
||||
* @param srcFile file to be copied
|
||||
* @param destFile destination to be copied to
|
||||
* @return a FileEntry object
|
||||
* @throws IOException
|
||||
* @throws InvalidModificationException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject moveFile(File srcFile, File destFile) throws JSONException, InvalidModificationException {
|
||||
// Renaming a file to an existing directory should fail
|
||||
if (destFile.exists() && destFile.isDirectory()) {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
}
|
||||
|
||||
// Try to rename the file
|
||||
if (!srcFile.renameTo(destFile)) {
|
||||
// Trying to rename the file failed. Possibly because we moved across file system on the device.
|
||||
// Now we have to do things the hard way
|
||||
// 1) Copy all the old file
|
||||
// 2) delete the src file
|
||||
}
|
||||
|
||||
return getEntry(destFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a directory
|
||||
*
|
||||
* @param srcDir directory to be copied
|
||||
* @param destinationDir destination to be copied to
|
||||
* @return a DirectoryEntry object
|
||||
* @throws JSONException
|
||||
* @throws IOException
|
||||
* @throws NoModificationAllowedException
|
||||
* @throws InvalidModificationException
|
||||
*/
|
||||
private JSONObject moveDirectory(File srcDir, File destinationDir) throws JSONException, FileExistsException, NoModificationAllowedException, InvalidModificationException {
|
||||
// Renaming a file to an existing directory should fail
|
||||
if (destinationDir.exists() && destinationDir.isFile()) {
|
||||
throw new InvalidModificationException("Can't rename a file to a directory");
|
||||
}
|
||||
|
||||
// Check to make sure we are not copying the directory into itself
|
||||
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy itself into itself");
|
||||
}
|
||||
|
||||
// If the destination directory already exists and is empty then delete it. This is according to spec.
|
||||
if (destinationDir.exists()) {
|
||||
if (destinationDir.list().length > 0) {
|
||||
throw new InvalidModificationException("directory is not empty");
|
||||
}
|
||||
}
|
||||
|
||||
// Try to rename the directory
|
||||
if (!srcDir.renameTo(destinationDir)) {
|
||||
// Trying to rename the directory failed. Possibly because we moved across file system on the device.
|
||||
// Now we have to do things the hard way
|
||||
// 1) Copy all the old files
|
||||
// 2) delete the src directory
|
||||
}
|
||||
|
||||
return getEntry(destinationDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a directory and all of its contents, if any. In the event of an error
|
||||
* [e.g. trying to delete a directory that contains a file that cannot be removed],
|
||||
* some of the contents of the directory may be deleted.
|
||||
* It is an error to attempt to delete the root directory of a filesystem.
|
||||
*
|
||||
* @param filePath the directory to be removed
|
||||
* @return a boolean representing success of failure
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
private boolean removeRecursively(String filePath) throws FileExistsException {
|
||||
File fp = new File(filePath);
|
||||
|
||||
// You can't delete the root directory.
|
||||
if (atRootDirectory(filePath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return removeDirRecursively(fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through a directory deleting all the files.
|
||||
*
|
||||
* @param directory to be removed
|
||||
* @return a boolean representing success of failure
|
||||
* @throws FileExistsException
|
||||
*/
|
||||
private boolean removeDirRecursively(File directory) throws FileExistsException {
|
||||
if (directory.isDirectory()) {
|
||||
for (File file : directory.listFiles()) {
|
||||
removeDirRecursively(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (!directory.delete()) {
|
||||
throw new FileExistsException("could not delete: " + directory.getName());
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file or directory. It is an error to attempt to delete a directory that is not empty.
|
||||
* It is an error to attempt to delete the root directory of a filesystem.
|
||||
*
|
||||
* @param filePath file or directory to be removed
|
||||
* @return a boolean representing success of failure
|
||||
* @throws NoModificationAllowedException
|
||||
* @throws InvalidModificationException
|
||||
*/
|
||||
private boolean remove(String filePath) throws NoModificationAllowedException, InvalidModificationException {
|
||||
File fp = new File(filePath);
|
||||
|
||||
// You can't delete the root directory.
|
||||
if (atRootDirectory(filePath)) {
|
||||
throw new NoModificationAllowedException("You can't delete the root directory");
|
||||
}
|
||||
|
||||
// You can't delete a directory that is not empty
|
||||
if (fp.isDirectory() && fp.list().length > 0) {
|
||||
throw new InvalidModificationException("You can't delete a directory that is not empty.");
|
||||
}
|
||||
|
||||
return fp.delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates or looks up a file.
|
||||
*
|
||||
* @param dirPath base directory
|
||||
* @param fileName file/directory to lookup or create
|
||||
* @param options specify whether to create or not
|
||||
* @param directory if true look up directory, if false look up file
|
||||
* @return a Entry object
|
||||
* @throws FileExistsException
|
||||
* @throws IOException
|
||||
* @throws TypeMismatchException
|
||||
* @throws EncodingException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getFile(String dirPath, String fileName, JSONObject options, boolean directory) throws FileExistsException, IOException, TypeMismatchException, EncodingException, JSONException {
|
||||
boolean create = false;
|
||||
boolean exclusive = false;
|
||||
if (options != null) {
|
||||
create = options.optBoolean("create");
|
||||
if (create) {
|
||||
exclusive = options.optBoolean("exclusive");
|
||||
}
|
||||
}
|
||||
|
||||
// Check for a ":" character in the file to line up with BB and iOS
|
||||
if (fileName.contains(":")) {
|
||||
throw new EncodingException("This file has a : in it's name");
|
||||
}
|
||||
|
||||
File fp = createFileObject(dirPath, fileName);
|
||||
|
||||
if (create) {
|
||||
if (exclusive && fp.exists()) {
|
||||
throw new FileExistsException("create/exclusive fails");
|
||||
}
|
||||
if (directory) {
|
||||
fp.mkdir();
|
||||
} else {
|
||||
fp.createNewFile();
|
||||
}
|
||||
if (!fp.exists()) {
|
||||
throw new FileExistsException("create fails");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!fp.exists()) {
|
||||
throw new FileNotFoundException("path does not exist");
|
||||
}
|
||||
if (directory) {
|
||||
if (fp.isFile()) {
|
||||
throw new TypeMismatchException("path doesn't exist or is file");
|
||||
}
|
||||
} else {
|
||||
if (fp.isDirectory()) {
|
||||
throw new TypeMismatchException("path doesn't exist or is directory");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return the directory
|
||||
return getEntry(fp);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the path starts with a '/' just return that file object. If not construct the file
|
||||
* object from the path passed in and the file name.
|
||||
*
|
||||
* @param dirPath root directory
|
||||
* @param fileName new file name
|
||||
* @return
|
||||
*/
|
||||
private File createFileObject(String dirPath, String fileName) {
|
||||
File fp = null;
|
||||
if (fileName.startsWith("/")) {
|
||||
fp = new File(fileName);
|
||||
} else {
|
||||
fp = new File(dirPath + File.separator + fileName);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the parent DirectoryEntry containing this Entry.
|
||||
* If this Entry is the root of its filesystem, its parent is itself.
|
||||
*
|
||||
* @param filePath
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getParent(String filePath) throws JSONException {
|
||||
if (atRootDirectory(filePath)) {
|
||||
return getEntry(filePath);
|
||||
}
|
||||
return getEntry(new File(filePath).getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if we are at the root directory. Useful since we are
|
||||
* not allow to delete this directory.
|
||||
*
|
||||
* @param filePath to directory
|
||||
* @return true if we are at the root, false otherwise.
|
||||
*/
|
||||
private boolean atRootDirectory(String filePath) {
|
||||
if (filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + ctx.getPackageName() + "/cache") ||
|
||||
filePath.equals(Environment.getExternalStorageDirectory().getAbsolutePath())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up metadata about this entry.
|
||||
*
|
||||
* @param filePath to entry
|
||||
* @return a Metadata object
|
||||
* @throws FileNotFoundException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getMetadata(String filePath) throws FileNotFoundException, JSONException {
|
||||
File file = new File(filePath);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException("Failed to find file in getMetadata");
|
||||
}
|
||||
|
||||
JSONObject metadata = new JSONObject();
|
||||
metadata.put("modificationTime", file.lastModified());
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File that represents the current state of the file that this FileEntry represents.
|
||||
*
|
||||
* @param filePath to entry
|
||||
* @return returns a JSONObject represent a W3C File object
|
||||
* @throws FileNotFoundException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getFileMetadata(String filePath) throws FileNotFoundException, JSONException {
|
||||
File file = new File(filePath);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new FileNotFoundException("File: " + filePath + " does not exist.");
|
||||
}
|
||||
|
||||
JSONObject metadata = new JSONObject();
|
||||
metadata.put("size", file.length());
|
||||
metadata.put("type", getMimeType(filePath));
|
||||
metadata.put("name", file.getName());
|
||||
metadata.put("fullPath", file.getAbsolutePath());
|
||||
metadata.put("lastModifiedDate", file.lastModified());
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests a filesystem in which to store application data.
|
||||
*
|
||||
* @param type of file system requested
|
||||
* @return a JSONObject representing the file system
|
||||
* @throws IOException
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject requestFileSystem(int type) throws IOException, JSONException {
|
||||
JSONObject fs = new JSONObject();
|
||||
if (type == TEMPORARY) {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
fs.put("name", "temporary");
|
||||
fs.put("root", getEntry(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getPackageName() + "/cache/"));
|
||||
|
||||
// Create the cache dir if it doesn't exist.
|
||||
File fp = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getPackageName() + "/cache/");
|
||||
fp.mkdirs();
|
||||
} else {
|
||||
throw new IOException("SD Card not mounted");
|
||||
}
|
||||
}
|
||||
else if (type == PERSISTENT) {
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
fs.put("name", "persistent");
|
||||
fs.put("root", getEntry(Environment.getExternalStorageDirectory()));
|
||||
} else {
|
||||
throw new IOException("SD Card not mounted");
|
||||
}
|
||||
}
|
||||
else if (type == RESOURCE) {
|
||||
fs.put("name", "resource");
|
||||
|
||||
}
|
||||
else if (type == APPLICATION) {
|
||||
fs.put("name", "application");
|
||||
|
||||
}
|
||||
else {
|
||||
throw new IOException("No filesystem of type requested");
|
||||
}
|
||||
|
||||
return fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object representing a directory on the device's file system
|
||||
*
|
||||
* @param path to the directory
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getEntry(File file) throws JSONException {
|
||||
JSONObject entry = new JSONObject();
|
||||
|
||||
entry.put("isFile", file.isFile());
|
||||
entry.put("isDirectory", file.isDirectory());
|
||||
entry.put("name", file.getName());
|
||||
entry.put("fullPath", file.getAbsolutePath());
|
||||
// I can't add the next thing it as it would be an infinite loop
|
||||
//entry.put("filesystem", null);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON Object representing a directory on the device's file system
|
||||
*
|
||||
* @param path to the directory
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getEntry(String path) throws JSONException {
|
||||
return getEntry(new File(path));
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("readAsText")) {
|
||||
return false;
|
||||
}
|
||||
@@ -176,6 +845,39 @@ public class FileUtils extends Plugin {
|
||||
else if (action.equals("writeAsText")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("requestFileSystem")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("getMetadata")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("toURI")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("getParent")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("getFile")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("getDirectory")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("removeRecursively")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("readEntries")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("getFileMetadata")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("resolveLocalFileSystemURI")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -220,16 +922,30 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
|
||||
// Determine content type from file name
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
String contentType = map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
|
||||
if (contentType == null && filename.startsWith("content")) {
|
||||
contentType = "image/jpeg";
|
||||
String contentType = null;
|
||||
if (filename.startsWith("content:")) {
|
||||
Uri fileUri = Uri.parse(filename);
|
||||
contentType = this.ctx.getContentResolver().getType(fileUri);
|
||||
}
|
||||
else {
|
||||
contentType = getMimeType(filename);
|
||||
}
|
||||
|
||||
byte[] base64 = Base64.encodeBase64(bos.toByteArray());
|
||||
String data = "data:" + contentType + ";base64," + new String(base64);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the mime type of a given file name.
|
||||
*
|
||||
* @param filename
|
||||
* @return a mime type
|
||||
*/
|
||||
private String getMimeType(String filename) {
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents of file.
|
||||
@@ -305,3 +1021,4 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ public class GeoListener {
|
||||
* @param msg The error message
|
||||
*/
|
||||
void fail(int code, String msg) {
|
||||
this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', " + ", " + code + ", '" + msg + "');");
|
||||
this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');");
|
||||
this.stop();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ package com.phonegap.api;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
public class PluginResult {
|
||||
private final int status;
|
||||
private final String message;
|
||||
@@ -32,6 +34,12 @@ public class PluginResult {
|
||||
this.cast = cast;
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONObject message, String cast) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message.toString();
|
||||
this.cast = cast;
|
||||
}
|
||||
|
||||
public PluginResult(Status status, JSONArray message) {
|
||||
this.status = status.ordinal();
|
||||
this.message = message.toString();
|
||||
|
||||
9
framework/src/com/phonegap/file/EncodingException.java
Normal file
9
framework/src/com/phonegap/file/EncodingException.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class EncodingException extends Exception {
|
||||
|
||||
public EncodingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
9
framework/src/com/phonegap/file/FileExistsException.java
Normal file
9
framework/src/com/phonegap/file/FileExistsException.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class FileExistsException extends Exception {
|
||||
|
||||
public FileExistsException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class InvalidModificationException extends Exception {
|
||||
|
||||
public InvalidModificationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class NoModificationAllowedException extends Exception {
|
||||
|
||||
public NoModificationAllowedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.phonegap.file;
|
||||
|
||||
public class TypeMismatchException extends Exception {
|
||||
|
||||
public TypeMismatchException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
658
framework/src/com/phonegap/websocket/WebSocket.java
Executable file
658
framework/src/com/phonegap/websocket/WebSocket.java
Executable file
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Nathan Rajlich (https://github.com/TooTallNate)
|
||||
* Copyright (c) 2010 Animesh Kumar (https://github.com/anismiles)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package com.phonegap.websocket;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.NotYetConnectedException;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* The <tt>WebSocket</tt> is an implementation of WebSocket Client API, and
|
||||
* expects a valid "ws://" URI to connect to. When connected, an instance
|
||||
* recieves important events related to the life of the connection, like
|
||||
* <var>onOpen</var>, <var>onClose</var>, <var>onError</var> and
|
||||
* <var>onMessage</var>. An instance can send messages to the server via the
|
||||
* <var>send</var> method.
|
||||
*
|
||||
* @author Animesh Kumar
|
||||
*/
|
||||
public class WebSocket implements Runnable {
|
||||
|
||||
/**
|
||||
* Enum for WebSocket Draft
|
||||
*/
|
||||
public enum Draft {
|
||||
DRAFT75, DRAFT76
|
||||
}
|
||||
|
||||
// //////////////// CONSTANT
|
||||
/**
|
||||
* The connection has not yet been established.
|
||||
*/
|
||||
public final static int WEBSOCKET_STATE_CONNECTING = 0;
|
||||
/**
|
||||
* The WebSocket connection is established and communication is possible.
|
||||
*/
|
||||
public final static int WEBSOCKET_STATE_OPEN = 1;
|
||||
/**
|
||||
* The connection is going through the closing handshake.
|
||||
*/
|
||||
public final static int WEBSOCKET_STATE_CLOSING = 2;
|
||||
/**
|
||||
* The connection has been closed or could not be opened.
|
||||
*/
|
||||
public final static int WEBSOCKET_STATE_CLOSED = 3;
|
||||
|
||||
/**
|
||||
* An empty string
|
||||
*/
|
||||
private static String BLANK_MESSAGE = "";
|
||||
/**
|
||||
* The javascript method name for onOpen event.
|
||||
*/
|
||||
private static String EVENT_ON_OPEN = "onopen";
|
||||
/**
|
||||
* The javascript method name for onMessage event.
|
||||
*/
|
||||
private static String EVENT_ON_MESSAGE = "onmessage";
|
||||
/**
|
||||
* The javascript method name for onClose event.
|
||||
*/
|
||||
private static String EVENT_ON_CLOSE = "onclose";
|
||||
/**
|
||||
* The javascript method name for onError event.
|
||||
*/
|
||||
private static String EVENT_ON_ERROR = "onerror";
|
||||
/**
|
||||
* The default port of WebSockets, as defined in the spec.
|
||||
*/
|
||||
public static final int DEFAULT_PORT = 80;
|
||||
/**
|
||||
* The WebSocket protocol expects UTF-8 encoded bytes.
|
||||
*/
|
||||
public static final String UTF8_CHARSET = "UTF-8";
|
||||
/**
|
||||
* The byte representing Carriage Return, or \r
|
||||
*/
|
||||
public static final byte DATA_CR = (byte) 0x0D;
|
||||
/**
|
||||
* The byte representing Line Feed, or \n
|
||||
*/
|
||||
public static final byte DATA_LF = (byte) 0x0A;
|
||||
/**
|
||||
* The byte representing the beginning of a WebSocket text frame.
|
||||
*/
|
||||
public static final byte DATA_START_OF_FRAME = (byte) 0x00;
|
||||
/**
|
||||
* The byte representing the end of a WebSocket text frame.
|
||||
*/
|
||||
public static final byte DATA_END_OF_FRAME = (byte) 0xFF;
|
||||
|
||||
// //////////////// INSTANCE Variables
|
||||
/**
|
||||
* The WebView instance from Phonegap DroidGap
|
||||
*/
|
||||
private WebView appView;
|
||||
/**
|
||||
* The unique id for this instance (helps to bind this to javascript events)
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* The URI this client is supposed to connect to.
|
||||
*/
|
||||
private URI uri;
|
||||
/**
|
||||
* The port of the websocket server
|
||||
*/
|
||||
private int port;
|
||||
/**
|
||||
* The Draft of the WebSocket protocol the Client is adhering to.
|
||||
*/
|
||||
private Draft draft;
|
||||
/**
|
||||
* The <tt>SocketChannel</tt> instance to use for this server connection.
|
||||
* This is used to read and write data to.
|
||||
*/
|
||||
private SocketChannel socketChannel;
|
||||
/**
|
||||
* The 'Selector' used to get event keys from the underlying socket.
|
||||
*/
|
||||
private Selector selector;
|
||||
/**
|
||||
* Keeps track of whether or not the client thread should continue running.
|
||||
*/
|
||||
private boolean running;
|
||||
/**
|
||||
* Internally used to determine whether to recieve data as part of the
|
||||
* remote handshake, or as part of a text frame.
|
||||
*/
|
||||
private boolean handshakeComplete;
|
||||
/**
|
||||
* The 1-byte buffer reused throughout the WebSocket connection to read
|
||||
* data.
|
||||
*/
|
||||
private ByteBuffer buffer;
|
||||
/**
|
||||
* The bytes that make up the remote handshake.
|
||||
*/
|
||||
private ByteBuffer remoteHandshake;
|
||||
/**
|
||||
* The bytes that make up the current text frame being read.
|
||||
*/
|
||||
private ByteBuffer currentFrame;
|
||||
/**
|
||||
* Queue of buffers that need to be sent to the client.
|
||||
*/
|
||||
private BlockingQueue<ByteBuffer> bufferQueue;
|
||||
/**
|
||||
* Lock object to ensure that data is sent from the bufferQueue in the
|
||||
* proper order
|
||||
*/
|
||||
private Object bufferQueueMutex = new Object();
|
||||
/**
|
||||
* Number 1 used in handshake
|
||||
*/
|
||||
private int number1 = 0;
|
||||
/**
|
||||
* Number 2 used in handshake
|
||||
*/
|
||||
private int number2 = 0;
|
||||
/**
|
||||
* Key3 used in handshake
|
||||
*/
|
||||
private byte[] key3 = null;
|
||||
/**
|
||||
* The readyState attribute represents the state of the connection.
|
||||
*/
|
||||
private int readyState = WEBSOCKET_STATE_CONNECTING;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Note: this is protected because it's supposed to be instantiated from
|
||||
* {@link WebSocketFactory} only.
|
||||
*
|
||||
* @param appView
|
||||
* {@link android.webkit.WebView}
|
||||
* @param uri
|
||||
* websocket server {@link URI}
|
||||
* @param draft
|
||||
* websocket server {@link Draft} implementation (75/76)
|
||||
* @param id
|
||||
* unique id for this instance
|
||||
*/
|
||||
protected WebSocket(WebView appView, URI uri, Draft draft, String id) {
|
||||
this.appView = appView;
|
||||
this.uri = uri;
|
||||
this.draft = draft;
|
||||
|
||||
// port
|
||||
port = uri.getPort();
|
||||
if (port == -1) {
|
||||
port = DEFAULT_PORT;
|
||||
}
|
||||
|
||||
// Id
|
||||
this.id = id;
|
||||
|
||||
this.bufferQueue = new LinkedBlockingQueue<ByteBuffer>();
|
||||
this.handshakeComplete = false;
|
||||
this.remoteHandshake = this.currentFrame = null;
|
||||
this.buffer = ByteBuffer.allocate(1);
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////////////
|
||||
// /////////////////////////// WEB SOCKET API Methods
|
||||
// ///////////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* Starts a new Thread and connects to server
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public Thread connect() throws IOException {
|
||||
this.running = true;
|
||||
this.readyState = WEBSOCKET_STATE_CONNECTING;
|
||||
// open socket
|
||||
socketChannel = SocketChannel.open();
|
||||
socketChannel.configureBlocking(false);
|
||||
// set address
|
||||
socketChannel.connect(new InetSocketAddress(uri.getHost(), port));
|
||||
// start a thread to make connection
|
||||
|
||||
// More info:
|
||||
// http://groups.google.com/group/android-developers/browse_thread/thread/45a8b53e9bf60d82
|
||||
// http://stackoverflow.com/questions/2879455/android-2-2-and-bad-address-family-on-socket-connect
|
||||
System.setProperty("java.net.preferIPv4Stack", "true");
|
||||
System.setProperty("java.net.preferIPv6Addresses", "false");
|
||||
|
||||
selector = Selector.open();
|
||||
socketChannel.register(selector, SelectionKey.OP_CONNECT);
|
||||
Log.v("websocket", "Starting a new thread to manage data reading/writing");
|
||||
|
||||
Thread th = new Thread(this);
|
||||
th.start();
|
||||
// return thread object for explicit closing, if needed
|
||||
return th;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while (this.running) {
|
||||
try {
|
||||
_connect();
|
||||
} catch (IOException e) {
|
||||
this.onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes connection with server
|
||||
*/
|
||||
public void close() {
|
||||
this.readyState = WebSocket.WEBSOCKET_STATE_CLOSING;
|
||||
|
||||
// close socket channel
|
||||
try {
|
||||
this.socketChannel.close();
|
||||
} catch (IOException e) {
|
||||
this.onError(e);
|
||||
}
|
||||
this.running = false;
|
||||
selector.wakeup();
|
||||
|
||||
// fire onClose method
|
||||
this.onClose();
|
||||
|
||||
this.readyState = WebSocket.WEBSOCKET_STATE_CLOSED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends <var>text</var> to server
|
||||
*
|
||||
* @param text
|
||||
* String to send to server
|
||||
*/
|
||||
public void send(String text) {
|
||||
try {
|
||||
if (this.readyState == WEBSOCKET_STATE_OPEN) {
|
||||
_send(text);
|
||||
} else {
|
||||
this.onError(new NotYetConnectedException());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
this.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an entire text frame has been recieved.
|
||||
*
|
||||
* @param msg
|
||||
* Message from websocket server
|
||||
*/
|
||||
public void onMessage(String msg) {
|
||||
appView.loadUrl(buildJavaScriptData(EVENT_ON_MESSAGE, msg));
|
||||
}
|
||||
|
||||
public void onOpen() {
|
||||
appView.loadUrl(buildJavaScriptData(EVENT_ON_OPEN, BLANK_MESSAGE));
|
||||
}
|
||||
|
||||
public void onClose() {
|
||||
appView.loadUrl(buildJavaScriptData(EVENT_ON_CLOSE, BLANK_MESSAGE));
|
||||
}
|
||||
|
||||
public void onError(Throwable t) {
|
||||
String msg = t.getMessage();
|
||||
appView.loadUrl(buildJavaScriptData(EVENT_ON_ERROR, msg));
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the readyState
|
||||
*/
|
||||
public int getReadyState() {
|
||||
return readyState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds text for javascript engine to invoke proper event method with
|
||||
* proper data.
|
||||
*
|
||||
* @param event
|
||||
* websocket event (onOpen, onMessage etc.)
|
||||
* @param msg
|
||||
* Text message received from websocket server
|
||||
* @return
|
||||
*/
|
||||
private String buildJavaScriptData(String event, String msg) {
|
||||
String _d = "javascript:WebSocket." + event + "(" + "{" + "\"_target\":\"" + id + "\","
|
||||
+ "\"_data\":'" + msg + "'" + "}" + ")";
|
||||
return _d;
|
||||
}
|
||||
|
||||
// //////////////////////////////////////////////////////////////////////////////////////
|
||||
// /////////////////////////// WEB SOCKET Internal Methods
|
||||
// //////////////////////////////
|
||||
// //////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean _send(String text) throws IOException {
|
||||
if (!this.handshakeComplete) {
|
||||
throw new NotYetConnectedException();
|
||||
}
|
||||
if (text == null) {
|
||||
throw new NullPointerException("Cannot send 'null' data to a WebSocket.");
|
||||
}
|
||||
|
||||
// Get 'text' into a WebSocket "frame" of bytes
|
||||
byte[] textBytes = text.getBytes(UTF8_CHARSET.toString());
|
||||
ByteBuffer b = ByteBuffer.allocate(textBytes.length + 2);
|
||||
b.put(DATA_START_OF_FRAME);
|
||||
b.put(textBytes);
|
||||
b.put(DATA_END_OF_FRAME);
|
||||
b.rewind();
|
||||
|
||||
// See if we have any backlog that needs to be sent first
|
||||
if (_write()) {
|
||||
// Write the ByteBuffer to the socket
|
||||
this.socketChannel.write(b);
|
||||
}
|
||||
|
||||
// If we didn't get it all sent, add it to the buffer of buffers
|
||||
if (b.remaining() > 0) {
|
||||
if (!this.bufferQueue.offer(b)) {
|
||||
throw new IOException("Buffers are full, message could not be sent to"
|
||||
+ this.socketChannel.socket().getRemoteSocketAddress());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// actual connection logic
|
||||
private void _connect() throws IOException {
|
||||
// Continuous loop that is only supposed to end when "close" is called.
|
||||
|
||||
selector.select();
|
||||
Set<SelectionKey> keys = selector.selectedKeys();
|
||||
Iterator<SelectionKey> i = keys.iterator();
|
||||
|
||||
while (i.hasNext()) {
|
||||
SelectionKey key = i.next();
|
||||
i.remove();
|
||||
if (key.isConnectable()) {
|
||||
if (socketChannel.isConnectionPending()) {
|
||||
socketChannel.finishConnect();
|
||||
}
|
||||
socketChannel.register(selector, SelectionKey.OP_READ);
|
||||
_writeHandshake();
|
||||
}
|
||||
if (key.isReadable()) {
|
||||
try {
|
||||
_read();
|
||||
} catch (NoSuchAlgorithmException nsa) {
|
||||
this.onError(nsa);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void _writeHandshake() throws IOException {
|
||||
String path = this.uri.getPath();
|
||||
if (path.indexOf("/") != 0) {
|
||||
path = "/" + path;
|
||||
}
|
||||
|
||||
String host = uri.getHost() + (port != DEFAULT_PORT ? ":" + port : "");
|
||||
String origin = "*"; // TODO: Make 'origin' configurable
|
||||
String request = "GET " + path + " HTTP/1.1\r\n" + "Upgrade: WebSocket\r\n"
|
||||
+ "Connection: Upgrade\r\n" + "Host: " + host + "\r\n" + "Origin: " + origin
|
||||
+ "\r\n";
|
||||
|
||||
// Add randon keys for Draft76
|
||||
if (this.draft == Draft.DRAFT76) {
|
||||
request += "Sec-WebSocket-Key1: " + this._randomKey() + "\r\n";
|
||||
request += "Sec-WebSocket-Key2: " + this._randomKey() + "\r\n";
|
||||
this.key3 = new byte[8];
|
||||
(new Random()).nextBytes(this.key3);
|
||||
}
|
||||
|
||||
request += "\r\n";
|
||||
_write(request.getBytes(UTF8_CHARSET));
|
||||
if (this.key3 != null) {
|
||||
_write(this.key3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean _write() throws IOException {
|
||||
synchronized (this.bufferQueueMutex) {
|
||||
ByteBuffer buffer = this.bufferQueue.peek();
|
||||
while (buffer != null) {
|
||||
this.socketChannel.write(buffer);
|
||||
if (buffer.remaining() > 0) {
|
||||
return false; // Didn't finish this buffer. There's more to
|
||||
// send.
|
||||
} else {
|
||||
this.bufferQueue.poll(); // Buffer finished. Remove it.
|
||||
buffer = this.bufferQueue.peek();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private void _write(byte[] bytes) throws IOException {
|
||||
this.socketChannel.write(ByteBuffer.wrap(bytes));
|
||||
}
|
||||
|
||||
private void _read() throws IOException, NoSuchAlgorithmException {
|
||||
this.buffer.rewind();
|
||||
|
||||
int bytesRead = -1;
|
||||
try {
|
||||
bytesRead = this.socketChannel.read(this.buffer);
|
||||
} catch (Exception ex) {
|
||||
}
|
||||
|
||||
if (bytesRead == -1) {
|
||||
close();
|
||||
} else if (bytesRead > 0) {
|
||||
this.buffer.rewind();
|
||||
|
||||
if (!this.handshakeComplete) {
|
||||
_readHandshake();
|
||||
} else {
|
||||
_readFrame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _readFrame() throws UnsupportedEncodingException {
|
||||
byte newestByte = this.buffer.get();
|
||||
|
||||
if (newestByte == DATA_START_OF_FRAME) { // Beginning of Frame
|
||||
this.currentFrame = null;
|
||||
|
||||
} else if (newestByte == DATA_END_OF_FRAME) { // End of Frame
|
||||
String textFrame = null;
|
||||
// currentFrame will be null if END_OF_FRAME was send directly after
|
||||
// START_OF_FRAME, thus we will send 'null' as the sent message.
|
||||
if (this.currentFrame != null) {
|
||||
textFrame = new String(this.currentFrame.array(), UTF8_CHARSET.toString());
|
||||
}
|
||||
// fire onMessage method
|
||||
this.onMessage(textFrame);
|
||||
|
||||
} else { // Regular frame data, add to current frame buffer
|
||||
ByteBuffer frame = ByteBuffer.allocate((this.currentFrame != null ? this.currentFrame
|
||||
.capacity() : 0)
|
||||
+ this.buffer.capacity());
|
||||
if (this.currentFrame != null) {
|
||||
this.currentFrame.rewind();
|
||||
frame.put(this.currentFrame);
|
||||
}
|
||||
frame.put(newestByte);
|
||||
this.currentFrame = frame;
|
||||
}
|
||||
}
|
||||
|
||||
private void _readHandshake() throws IOException, NoSuchAlgorithmException {
|
||||
ByteBuffer ch = ByteBuffer.allocate((this.remoteHandshake != null ? this.remoteHandshake
|
||||
.capacity() : 0)
|
||||
+ this.buffer.capacity());
|
||||
if (this.remoteHandshake != null) {
|
||||
this.remoteHandshake.rewind();
|
||||
ch.put(this.remoteHandshake);
|
||||
}
|
||||
ch.put(this.buffer);
|
||||
this.remoteHandshake = ch;
|
||||
byte[] h = this.remoteHandshake.array();
|
||||
// If the ByteBuffer contains 16 random bytes, and ends with
|
||||
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), then the client
|
||||
// handshake is complete for Draft 76 Client.
|
||||
if ((h.length >= 20 && h[h.length - 20] == DATA_CR && h[h.length - 19] == DATA_LF
|
||||
&& h[h.length - 18] == DATA_CR && h[h.length - 17] == DATA_LF)) {
|
||||
_readHandshake(new byte[] { h[h.length - 16], h[h.length - 15], h[h.length - 14],
|
||||
h[h.length - 13], h[h.length - 12], h[h.length - 11], h[h.length - 10],
|
||||
h[h.length - 9], h[h.length - 8], h[h.length - 7], h[h.length - 6],
|
||||
h[h.length - 5], h[h.length - 4], h[h.length - 3], h[h.length - 2],
|
||||
h[h.length - 1] });
|
||||
|
||||
// If the ByteBuffer contains 8 random bytes,ends with
|
||||
// 0x0D 0x0A 0x0D 0x0A (or two CRLFs), and the response
|
||||
// contains Sec-WebSocket-Key1 then the client
|
||||
// handshake is complete for Draft 76 Server.
|
||||
} else if ((h.length >= 12 && h[h.length - 12] == DATA_CR && h[h.length - 11] == DATA_LF
|
||||
&& h[h.length - 10] == DATA_CR && h[h.length - 9] == DATA_LF)
|
||||
&& new String(this.remoteHandshake.array(), UTF8_CHARSET)
|
||||
.contains("Sec-WebSocket-Key1")) {// ************************
|
||||
_readHandshake(new byte[] { h[h.length - 8], h[h.length - 7], h[h.length - 6],
|
||||
h[h.length - 5], h[h.length - 4], h[h.length - 3], h[h.length - 2],
|
||||
h[h.length - 1] });
|
||||
|
||||
// Consider Draft 75, and the Flash Security Policy
|
||||
// Request edge-case.
|
||||
} else if ((h.length >= 4 && h[h.length - 4] == DATA_CR && h[h.length - 3] == DATA_LF
|
||||
&& h[h.length - 2] == DATA_CR && h[h.length - 1] == DATA_LF)
|
||||
&& !(new String(this.remoteHandshake.array(), UTF8_CHARSET).contains("Sec"))
|
||||
|| (h.length == 23 && h[h.length - 1] == 0)) {
|
||||
_readHandshake(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void _readHandshake(byte[] handShakeBody) throws IOException, NoSuchAlgorithmException {
|
||||
// byte[] handshakeBytes = this.remoteHandshake.array();
|
||||
// String handshake = new String(handshakeBytes, UTF8_CHARSET);
|
||||
// TODO: Do some parsing of the returned handshake, and close connection
|
||||
// in received anything unexpected!
|
||||
|
||||
this.handshakeComplete = true;
|
||||
boolean isConnectionReady = true;
|
||||
|
||||
if (this.draft == WebSocket.Draft.DRAFT76) {
|
||||
if (handShakeBody == null) {
|
||||
isConnectionReady = true;
|
||||
}
|
||||
byte[] challenge = new byte[] { (byte) (this.number1 >> 24),
|
||||
(byte) ((this.number1 << 8) >> 24), (byte) ((this.number1 << 16) >> 24),
|
||||
(byte) ((this.number1 << 24) >> 24), (byte) (this.number2 >> 24),
|
||||
(byte) ((this.number2 << 8) >> 24), (byte) ((this.number2 << 16) >> 24),
|
||||
(byte) ((this.number2 << 24) >> 24), this.key3[0], this.key3[1], this.key3[2],
|
||||
this.key3[3], this.key3[4], this.key3[5], this.key3[6], this.key3[7] };
|
||||
MessageDigest md5 = MessageDigest.getInstance("MD5");
|
||||
byte[] expected = md5.digest(challenge);
|
||||
for (int i = 0; i < handShakeBody.length; i++) {
|
||||
if (expected[i] != handShakeBody[i]) {
|
||||
isConnectionReady = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isConnectionReady) {
|
||||
this.readyState = WEBSOCKET_STATE_OPEN;
|
||||
// fire onOpen method
|
||||
this.onOpen();
|
||||
} else {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
private String _randomKey() {
|
||||
Random r = new Random();
|
||||
long maxNumber = 4294967295L;
|
||||
long spaces = r.nextInt(12) + 1;
|
||||
int max = new Long(maxNumber / spaces).intValue();
|
||||
max = Math.abs(max);
|
||||
int number = r.nextInt(max) + 1;
|
||||
if (this.number1 == 0) {
|
||||
this.number1 = number;
|
||||
} else {
|
||||
this.number2 = number;
|
||||
}
|
||||
long product = number * spaces;
|
||||
String key = Long.toString(product);
|
||||
int numChars = r.nextInt(12);
|
||||
for (int i = 0; i < numChars; i++) {
|
||||
int position = r.nextInt(key.length());
|
||||
position = Math.abs(position);
|
||||
char randChar = (char) (r.nextInt(95) + 33);
|
||||
// exclude numbers here
|
||||
if (randChar >= 48 && randChar <= 57) {
|
||||
randChar -= 15;
|
||||
}
|
||||
key = new StringBuilder(key).insert(position, randChar).toString();
|
||||
}
|
||||
for (int i = 0; i < spaces; i++) {
|
||||
int position = r.nextInt(key.length() - 1) + 1;
|
||||
position = Math.abs(position);
|
||||
key = new StringBuilder(key).insert(position, "\u0020").toString();
|
||||
}
|
||||
return key;
|
||||
}
|
||||
}
|
||||
85
framework/src/com/phonegap/websocket/WebSocketFactory.java
Executable file
85
framework/src/com/phonegap/websocket/WebSocketFactory.java
Executable file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Animesh Kumar (https://github.com/anismiles)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
package com.phonegap.websocket;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Random;
|
||||
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* The <tt>WebSocketFactory</tt> is like a helper class to instantiate new
|
||||
* WebSocket instaces especially from Javascript side. It expects a valid
|
||||
* "ws://" URI.
|
||||
*
|
||||
* @author Animesh Kumar
|
||||
*/
|
||||
public class WebSocketFactory {
|
||||
|
||||
/** The app view. */
|
||||
WebView appView;
|
||||
|
||||
/**
|
||||
* Instantiates a new web socket factory.
|
||||
*
|
||||
* @param appView
|
||||
* the app view
|
||||
*/
|
||||
public WebSocketFactory(WebView appView) {
|
||||
this.appView = appView;
|
||||
}
|
||||
|
||||
public WebSocket getInstance(String url) {
|
||||
// use Draft75 by default
|
||||
return getInstance(url, WebSocket.Draft.DRAFT75);
|
||||
}
|
||||
|
||||
public WebSocket getInstance(String url, WebSocket.Draft draft) {
|
||||
WebSocket socket = null;
|
||||
Thread th = null;
|
||||
try {
|
||||
socket = new WebSocket(appView, new URI(url), draft, getRandonUniqueId());
|
||||
th = socket.connect();
|
||||
return socket;
|
||||
} catch (Exception e) {
|
||||
//Log.v("websocket", e.toString());
|
||||
if(th != null) {
|
||||
th.interrupt();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates random unique ids for WebSocket instances
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
private String getRandonUniqueId() {
|
||||
return "WEBSOCKET." + new Random().nextInt(100);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,9 +19,15 @@ class Classic
|
||||
end
|
||||
|
||||
def setup
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__).gsub('lib',''))
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__).gsub(/lib$/,''))
|
||||
@framework_dir = File.join(@android_dir, "framework")
|
||||
@icon = File.join(@www, 'icon.png')
|
||||
@icon = File.join(@www, 'icon.png') unless File.exists?(@icon)
|
||||
# Hash that stores the location of icons for each resolution type. Uses the default icon for all resolutions as a baseline.
|
||||
@icons = {
|
||||
:"drawable-ldpi" => @icon,
|
||||
:"drawable-mdpi" => @icon,
|
||||
:"drawable-hdpi" => @icon
|
||||
} if @icons.nil?
|
||||
@app_js_dir = ''
|
||||
@content = 'index.html'
|
||||
end
|
||||
@@ -82,11 +88,12 @@ class Classic
|
||||
|
||||
# copies stuff from src directory into the android project directory (@path)
|
||||
def copy_libs
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION'))
|
||||
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")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.#{ version }.jar"), File.join(@path, "libs")
|
||||
# copies in the strings.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "values")
|
||||
FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml")
|
||||
@@ -96,11 +103,19 @@ class Classic
|
||||
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|
|
||||
# if specific resolution icons are specified, use those. if not, see if a general purpose icon was defined.
|
||||
# finally, fall back to using the default PhoneGap one.
|
||||
currentIcon = ""
|
||||
if !@icons[e.to_sym].nil? && File.exists?(File.join(@www, @icons[e.to_sym]))
|
||||
currentIcon = File.join(@www, @icons[e.to_sym])
|
||||
elsif File.exists?(@icon)
|
||||
currentIcon = @icon
|
||||
else
|
||||
currentIcon = File.join(framework_res_dir, "drawable", "icon.png")
|
||||
end
|
||||
FileUtils.mkdir_p(File.join(app_res_dir, e))
|
||||
FileUtils.cp(@icon, File.join(app_res_dir, e, "icon.png"))
|
||||
FileUtils.cp(currentIcon, File.join(app_res_dir, e, "icon.png"))
|
||||
end
|
||||
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
|
||||
js_dir = File.join(@framework_dir, "assets", "js")
|
||||
@@ -110,7 +125,7 @@ class Classic
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
|
||||
# puts app name in strings
|
||||
|
||||
@@ -29,7 +29,7 @@ class Create < Classic
|
||||
@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 "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
|
||||
|
||||
@@ -40,16 +40,40 @@ class Create < Classic
|
||||
if File.exists?(config_file)
|
||||
require 'rexml/document'
|
||||
f = File.new config_file
|
||||
doc = REXML::Document.new(f)
|
||||
@config = {}
|
||||
doc = REXML::Document.new(f)
|
||||
@config = {}
|
||||
@config[:id] = doc.root.attributes["id"]
|
||||
@config[:version] = doc.root.attributes["version"]
|
||||
|
||||
@config[:icons] = {}
|
||||
defaultIconSize = 0
|
||||
doc.root.elements.each do |n|
|
||||
@config[:name] = n.text.gsub('-','').gsub(' ','') if n.name == 'name'
|
||||
@config[:description] = n.text if n.name == 'description'
|
||||
@config[:icon] = n.attributes["src"] if n.name == 'icon'
|
||||
@config[:content] = n.attributes["src"] if n.name == 'content'
|
||||
@config[:content] = n.attributes["src"] if n.name == 'content'
|
||||
if n.name == 'icon'
|
||||
if n.attributes["width"] == '72' && n.attributes["height"] == '72'
|
||||
@config[:icons]["drawable-hdpi".to_sym] = n.attributes["src"]
|
||||
if 72 > defaultIconSize
|
||||
@config[:icon] = n.attributes["src"]
|
||||
defaultIconSize = 72
|
||||
end
|
||||
elsif n.attributes["width"] == '48' && n.attributes["height"] == '48'
|
||||
@config[:icons]["drawable-mdpi".to_sym] = n.attributes["src"]
|
||||
if 48 > defaultIconSize
|
||||
@config[:icon] = n.attributes["src"]
|
||||
defaultIconSize = 48
|
||||
end
|
||||
elsif n.attributes["width"] == '36' && n.attributes["height"] == '36'
|
||||
@config[:icons]["drawable-ldpi".to_sym] = n.attributes["src"]
|
||||
if 36 > defaultIconSize
|
||||
@config[:icon] = n.attributes["src"]
|
||||
defaultIconSize = 36
|
||||
end
|
||||
else
|
||||
@config[:icon] = n.attributes["src"]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if n.name == "preference" && n.attributes["name"] == 'javascript_folder'
|
||||
@config[:js_dir] = n.attributes["value"]
|
||||
@@ -62,7 +86,8 @@ class Create < Classic
|
||||
# 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])
|
||||
@icon = File.join(@www, @config[:icon]) if @config[:icon]
|
||||
@icons = @config[:icons] if @config[:icons].length > 0
|
||||
# sets the app js dir where phonegap.js gets copied
|
||||
@app_js_dir = @config[:js_dir] ? @config[:js_dir] : ''
|
||||
# sets the start page
|
||||
|
||||
Reference in New Issue
Block a user