mirror of
https://github.com/apache/cordova-android.git
synced 2026-02-10 00:03:01 +08:00
Compare commits
5 Commits
0.9.5.1
...
WebSockets
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9270d13981 | ||
|
|
83e89909ed | ||
|
|
98d51b18e2 | ||
|
|
28f27e89e4 | ||
|
|
8ede3b4cc9 |
@@ -5,7 +5,7 @@
|
||||
<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.0.9.5.min.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>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.phonegap" android:versionName="1.1" android:versionCode="5">
|
||||
<supports-screens
|
||||
android:largeScreens="true"
|
||||
@@ -8,7 +8,6 @@
|
||||
android:resizeable="true"
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
@@ -24,9 +23,6 @@
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:debuggable="true">
|
||||
<activity android:name=".StandAlone"
|
||||
@@ -37,7 +33,6 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -3,25 +3,21 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("accelerometer")) {
|
||||
PhoneGap.addResource("accelerometer");
|
||||
|
||||
/** @constructor */
|
||||
var Acceleration = function(x, y, z) {
|
||||
function Acceleration(x, y, z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.timestamp = new Date().getTime();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides access to device accelerometer data.
|
||||
* @constructor
|
||||
*/
|
||||
var Accelerometer = function() {
|
||||
function Accelerometer() {
|
||||
|
||||
/**
|
||||
* The last known acceleration. type=Acceleration()
|
||||
@@ -32,7 +28,7 @@ var Accelerometer = function() {
|
||||
* List of accelerometer watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
};
|
||||
}
|
||||
|
||||
Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
@@ -123,4 +119,3 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.accelerometer = new Accelerometer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,14 +6,10 @@
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("app")) {
|
||||
PhoneGap.addResource("app");
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @constructor
|
||||
*/
|
||||
var App = function() {};
|
||||
function App() {}
|
||||
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
@@ -24,7 +20,7 @@ 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
|
||||
@@ -34,7 +30,7 @@ App.prototype.clearCache = function() {
|
||||
* 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});
|
||||
@@ -60,7 +56,7 @@ App.prototype.clearHistory = function() {
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
@@ -71,10 +67,10 @@ App.prototype.addService = function(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) {
|
||||
@@ -91,4 +87,3 @@ App.prototype.exitApp = function() {
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.app = window.app = new App();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,18 +3,15 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("camera")) {
|
||||
PhoneGap.addResource("camera");
|
||||
|
||||
/**
|
||||
* This class provides access to the device camera.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
var Camera = function() {
|
||||
Camera = function() {
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
this.options = null;
|
||||
@@ -94,4 +91,3 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.camera = new Camera();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,191 +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-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("capture")) {
|
||||
PhoneGap.addResource("capture");
|
||||
|
||||
/**
|
||||
* Represents a single file.
|
||||
*
|
||||
* name {DOMString} name of the file, without path information
|
||||
* fullPath {DOMString} the full path of the file, including the name
|
||||
* type {DOMString} mime type
|
||||
* lastModifiedDate {Date} last modified date
|
||||
* size {Number} size of the file in bytes
|
||||
*/
|
||||
var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
|
||||
this.name = name || null;
|
||||
this.fullPath = fullPath || null;
|
||||
this.type = type || null;
|
||||
this.lastModifiedDate = lastModifiedDate || null;
|
||||
this.size = size || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch device camera application for recording video(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
*/
|
||||
MediaFile.prototype.getFormatData = function(successCallback, errorCallback){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* MediaFileData encapsulates format information of a media file.
|
||||
*
|
||||
* @param {DOMString} codecs
|
||||
* @param {long} bitrate
|
||||
* @param {long} height
|
||||
* @param {long} width
|
||||
* @param {float} duration
|
||||
*/
|
||||
var MediaFileData = function(codecs, bitrate, height, width, duration){
|
||||
this.codecs = codecs || null;
|
||||
this.bitrate = bitrate || 0;
|
||||
this.height = height || 0;
|
||||
this.width = width || 0;
|
||||
this.duration = duration || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The CaptureError interface encapsulates all errors in the Capture API.
|
||||
*/
|
||||
var CaptureError = function(){
|
||||
this.code = null;
|
||||
};
|
||||
|
||||
// Capture error codes
|
||||
CaptureError.CAPTURE_INTERNAL_ERR = 0;
|
||||
CaptureError.CAPTURE_APPLICATION_BUSY = 1;
|
||||
CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
|
||||
CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
|
||||
CaptureError.CAPTURE_NOT_SUPPORTED = 20;
|
||||
|
||||
/**
|
||||
* The Capture interface exposes an interface to the camera and microphone of the hosting device.
|
||||
*/
|
||||
var Capture = function(){
|
||||
this.supportedAudioModes = [];
|
||||
this.supportedImageModes = [];
|
||||
this.supportedVideoModes = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch audio recorder application for recording audio clip(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureAudioOptions} options
|
||||
*/
|
||||
Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureAudio", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch camera application for taking image(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureImageOptions} options
|
||||
*/
|
||||
Capture.prototype.captureImage = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureImage", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch camera application for taking image(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureImageOptions} options
|
||||
*/
|
||||
Capture.prototype._castMediaFile = function(pluginResult){
|
||||
var mediaFiles = [];
|
||||
var i;
|
||||
for (i = 0; i < pluginResult.message.length; i++) {
|
||||
var mediaFile = new MediaFile();
|
||||
mediaFile.name = pluginResult.message[i].name;
|
||||
mediaFile.fullPath = pluginResult.message[i].fullPath;
|
||||
mediaFile.type = pluginResult.message[i].type;
|
||||
mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
|
||||
mediaFile.size = pluginResult.message[i].size;
|
||||
mediaFiles.push(mediaFile);
|
||||
}
|
||||
pluginResult.message = mediaFiles;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch device camera application for recording video(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureVideoOptions} options
|
||||
*/
|
||||
Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureVideo", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates a set of parameters that the capture device supports.
|
||||
*/
|
||||
var ConfigurationData = function(){
|
||||
// The ASCII-encoded string in lower case representing the media type.
|
||||
this.type = null;
|
||||
// The height attribute represents height of the image or video in pixels.
|
||||
// In the case of a sound clip this attribute has value 0.
|
||||
this.height = 0;
|
||||
// The width attribute represents width of the image or video in pixels.
|
||||
// In the case of a sound clip this attribute has value 0
|
||||
this.width = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all image capture operation configuration options.
|
||||
*/
|
||||
var CaptureImageOptions = function(){
|
||||
// Upper limit of images user can take. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// The selected image mode. Must match with one of the elements in supportedImageModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all video capture operation configuration options.
|
||||
*/
|
||||
var CaptureVideoOptions = function(){
|
||||
// Upper limit of videos user can record. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// Maximum duration of a single video clip in seconds.
|
||||
this.duration = 0;
|
||||
// The selected video mode. Must match with one of the elements in supportedVideoModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all audio capture operation configuration options.
|
||||
*/
|
||||
var CaptureAudioOptions = function(){
|
||||
// Upper limit of sound clips user can record. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// Maximum duration of a single sound clip in seconds.
|
||||
this.duration = 0;
|
||||
// The selected audio mode. Must match with one of the elements in supportedAudioModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function(){
|
||||
if (typeof navigator.device === "undefined") {
|
||||
navigator.device = window.device = new Device();
|
||||
}
|
||||
if (typeof navigator.device.capture === "undefined") {
|
||||
navigator.device.capture = window.device.capture = new Capture();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -3,17 +3,14 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("compass")) {
|
||||
PhoneGap.addResource("compass");
|
||||
|
||||
/**
|
||||
* This class provides access to device Compass data.
|
||||
* @constructor
|
||||
*/
|
||||
var Compass = function() {
|
||||
function Compass() {
|
||||
/**
|
||||
* The last known Compass position.
|
||||
*/
|
||||
@@ -23,7 +20,7 @@ var Compass = function() {
|
||||
* List of compass watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
};
|
||||
}
|
||||
|
||||
Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
@@ -116,4 +113,3 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.compass = new Compass();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,31 +3,27 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("contact")) {
|
||||
PhoneGap.addResource("contact");
|
||||
|
||||
/**
|
||||
* Contains information about a single contact.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier
|
||||
* @param {DOMString} displayName
|
||||
* @param {ContactName} name
|
||||
* @param {DOMString} nickname
|
||||
* @param {Array.<ContactField>} phoneNumbers array of phone numbers
|
||||
* @param {Array.<ContactField>} emails array of email addresses
|
||||
* @param {Array.<ContactAddress>} addresses array of addresses
|
||||
* @param {Array.<ContactField>} ims instant messaging user ids
|
||||
* @param {Array.<ContactOrganization>} organizations
|
||||
* @param {ContactField[]} phoneNumbers array of phone numbers
|
||||
* @param {ContactField[]} emails array of email addresses
|
||||
* @param {ContactAddress[]} addresses array of addresses
|
||||
* @param {ContactField[]} ims instant messaging user ids
|
||||
* @param {ContactOrganization[]} organizations
|
||||
* @param {DOMString} revision date contact was last updated
|
||||
* @param {DOMString} birthday contact's birthday
|
||||
* @param {DOMString} gender contact's gender
|
||||
* @param {DOMString} note user notes about contact
|
||||
* @param {Array.<ContactField>} photos
|
||||
* @param {Array.<ContactField>} categories
|
||||
* @param {Array.<ContactField>} urls contact's web sites
|
||||
* @param {ContactField[]} photos
|
||||
* @param {ContactField[]} categories
|
||||
* @param {ContactField[]} urls contact's web sites
|
||||
* @param {DOMString} timezone the contacts time zone
|
||||
*/
|
||||
var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
|
||||
@@ -54,8 +50,7 @@ var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, a
|
||||
|
||||
/**
|
||||
* ContactError.
|
||||
* An error code assigned by an implementation when an error has occurreds
|
||||
* @constructor
|
||||
* An error code assigned by an implementation when an error has occurred
|
||||
*/
|
||||
var ContactError = function() {
|
||||
this.code=null;
|
||||
@@ -154,7 +149,6 @@ Contact.prototype.save = function(successCB, errorCB) {
|
||||
|
||||
/**
|
||||
* Contact name.
|
||||
* @constructor
|
||||
* @param formatted
|
||||
* @param familyName
|
||||
* @param givenName
|
||||
@@ -173,7 +167,6 @@ var ContactName = function(formatted, familyName, givenName, middle, prefix, suf
|
||||
|
||||
/**
|
||||
* Generic contact field.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param type
|
||||
* @param value
|
||||
@@ -188,7 +181,6 @@ var ContactField = function(type, value, pref) {
|
||||
|
||||
/**
|
||||
* Contact address.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param formatted
|
||||
* @param streetAddress
|
||||
@@ -209,7 +201,6 @@ var ContactAddress = function(formatted, streetAddress, locality, region, postal
|
||||
|
||||
/**
|
||||
* Contact organization.
|
||||
* @constructor
|
||||
* @param {DOMString} id unique identifier, should only be set by native code
|
||||
* @param name
|
||||
* @param dept
|
||||
@@ -228,7 +219,6 @@ var ContactOrganization = function(name, dept, title) {
|
||||
|
||||
/**
|
||||
* Represents a group of Contacts.
|
||||
* @constructor
|
||||
*/
|
||||
var Contacts = function() {
|
||||
this.inProgress = false;
|
||||
@@ -265,10 +255,10 @@ Contacts.prototype.create = function(properties) {
|
||||
};
|
||||
|
||||
/**
|
||||
* This function returns and array of contacts. It is required as we need to convert raw
|
||||
* JSON objects into concrete Contact objects. Currently this method is called after
|
||||
* This function returns and array of contacts. It is required as we need to convert raw
|
||||
* JSON objects into concrete Contact objects. Currently this method is called after
|
||||
* navigator.service.contacts.find but before the find methods success call back.
|
||||
*
|
||||
*
|
||||
* @param jsonArray an array of JSON Objects that need to be converted to Contact objects.
|
||||
* @returns an array of Contact objects
|
||||
*/
|
||||
@@ -284,7 +274,6 @@ Contacts.prototype.cast = function(pluginResult) {
|
||||
|
||||
/**
|
||||
* ContactFindOptions.
|
||||
* @constructor
|
||||
* @param filter used to match contacts against
|
||||
* @param multiple boolean used to determine if more than one contact should be returned
|
||||
* @param updatedSince return only contact records that have been updated on or after the given time
|
||||
@@ -306,4 +295,3 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.service.contacts = new Contacts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,17 +3,11 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
// TODO: Needs to be commented
|
||||
|
||||
if (!PhoneGap.hasResource("crypto")) {
|
||||
PhoneGap.addResource("crypto");
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var Crypto = function() {
|
||||
};
|
||||
|
||||
@@ -40,4 +34,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.Crypto = new Crypto();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,15 +6,12 @@
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("device")) {
|
||||
PhoneGap.addResource("device");
|
||||
|
||||
/**
|
||||
* This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
|
||||
* phone, etc.
|
||||
* @constructor
|
||||
*/
|
||||
var Device = function() {
|
||||
function Device() {
|
||||
this.available = PhoneGap.available;
|
||||
this.platform = null;
|
||||
this.version = null;
|
||||
@@ -38,7 +35,7 @@ var Device = function() {
|
||||
console.log("Error initializing PhoneGap: " + e);
|
||||
alert("Error initializing PhoneGap: "+e);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get device info
|
||||
@@ -72,7 +69,7 @@ Device.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
*/
|
||||
Device.prototype.overrideBackButton = function() {
|
||||
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
|
||||
navigator.app.overrideBackbutton(true);
|
||||
app.overrideBackbutton(true);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -83,7 +80,7 @@ Device.prototype.overrideBackButton = function() {
|
||||
*/
|
||||
Device.prototype.resetBackButton = function() {
|
||||
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
|
||||
navigator.app.overrideBackbutton(false);
|
||||
app.overrideBackbutton(false);
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -94,12 +91,9 @@ Device.prototype.resetBackButton = function() {
|
||||
*/
|
||||
Device.prototype.exitApp = function() {
|
||||
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
|
||||
navigator.app.exitApp();
|
||||
app.exitApp();
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.device === "undefined") {
|
||||
navigator.device = window.device = new Device();
|
||||
}
|
||||
navigator.device = window.device = new Device();
|
||||
});
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,50 +1,44 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("filetransfer")) {
|
||||
PhoneGap.addResource("filetransfer");
|
||||
|
||||
/**
|
||||
* FileTransfer uploads a file to a remote server.
|
||||
* @constructor
|
||||
*/
|
||||
var FileTransfer = function() {};
|
||||
function FileTransfer() {}
|
||||
|
||||
/**
|
||||
* FileUploadResult
|
||||
* @constructor
|
||||
*/
|
||||
var FileUploadResult = function() {
|
||||
function FileUploadResult() {
|
||||
this.bytesSent = 0;
|
||||
this.responseCode = null;
|
||||
this.response = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* FileTransferError
|
||||
* @constructor
|
||||
*/
|
||||
var FileTransferError = function() {
|
||||
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
|
||||
* 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
|
||||
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
|
||||
*/
|
||||
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) {
|
||||
|
||||
@@ -64,22 +58,20 @@ FileTransfer.prototype.upload = function(filePath, server, successCallback, erro
|
||||
params = {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Options to customize the HTTP request used to upload files.
|
||||
* @constructor
|
||||
* @param fileKey {String} Name of file request parameter.
|
||||
* @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
|
||||
* @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
|
||||
* @param params {Object} Object with key: value params to send to the server.
|
||||
*/
|
||||
var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
|
||||
function FileUploadOptions(fileKey, fileName, mimeType, params) {
|
||||
this.fileKey = fileKey || null;
|
||||
this.fileName = fileName || null;
|
||||
this.mimeType = mimeType || null;
|
||||
this.params = params || null;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,36 +3,32 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("geolocation")) {
|
||||
PhoneGap.addResource("geolocation");
|
||||
|
||||
/**
|
||||
* This class provides access to device GPS data.
|
||||
* @constructor
|
||||
*/
|
||||
var Geolocation = function() {
|
||||
function Geolocation() {
|
||||
|
||||
// The last known GPS position.
|
||||
this.lastPosition = null;
|
||||
|
||||
// Geolocation listeners
|
||||
this.listeners = {};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Position error object
|
||||
*
|
||||
* @constructor
|
||||
* @param code
|
||||
* @param message
|
||||
*/
|
||||
var PositionError = function(code, message) {
|
||||
function PositionError(code, message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
};
|
||||
}
|
||||
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
@@ -195,4 +191,4 @@ PhoneGap.addConstructor(function() {
|
||||
Geolocation.usingPhoneGap = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,16 +3,66 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("media")) {
|
||||
PhoneGap.addResource("media");
|
||||
/**
|
||||
* List of media objects.
|
||||
* PRIVATE
|
||||
*/
|
||||
PhoneGap.mediaObjects = {};
|
||||
|
||||
/**
|
||||
* Object that receives native callbacks.
|
||||
* PRIVATE
|
||||
*/
|
||||
PhoneGap.Media = function() {};
|
||||
|
||||
/**
|
||||
* Get the media object.
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
*/
|
||||
PhoneGap.Media.getMediaObject = function(id) {
|
||||
return PhoneGap.mediaObjects[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio has status update.
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
* @param status The status code (int)
|
||||
* @param msg The status message (string)
|
||||
*/
|
||||
PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
var media = PhoneGap.mediaObjects[id];
|
||||
|
||||
// If state update
|
||||
if (msg === Media.MEDIA_STATE) {
|
||||
if (value === Media.MEDIA_STOPPED) {
|
||||
if (media.successCallback) {
|
||||
media.successCallback();
|
||||
}
|
||||
}
|
||||
if (media.statusCallback) {
|
||||
media.statusCallback(value);
|
||||
}
|
||||
}
|
||||
else if (msg === Media.MEDIA_DURATION) {
|
||||
media._duration = value;
|
||||
}
|
||||
else if (msg === Media.MEDIA_ERROR) {
|
||||
if (media.errorCallback) {
|
||||
media.errorCallback(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class provides access to the device media, interfaces to both sound and video
|
||||
*
|
||||
* @constructor
|
||||
* @param src The file name or url to play
|
||||
* @param successCallback The callback to be called when the file is done playing or recording.
|
||||
* successCallback() - OPTIONAL
|
||||
@@ -23,7 +73,7 @@ PhoneGap.addResource("media");
|
||||
* @param positionCallback The callback to be called when media position has changed.
|
||||
* positionCallback(long position) - OPTIONAL
|
||||
*/
|
||||
var Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
|
||||
Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
|
||||
|
||||
// successCallback optional
|
||||
if (successCallback && (typeof successCallback !== "function")) {
|
||||
@@ -78,10 +128,10 @@ Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
|
||||
* This class contains information about any Media errors.
|
||||
* @constructor
|
||||
*/
|
||||
var MediaError = function() {
|
||||
function MediaError() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
};
|
||||
}
|
||||
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
@@ -102,13 +152,6 @@ Media.prototype.stop = function() {
|
||||
return PhoneGap.exec(null, null, "Media", "stopPlayingAudio", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Seek or jump to a new time in the track..
|
||||
*/
|
||||
Media.prototype.seekTo = function(milliseconds) {
|
||||
PhoneGap.exec(null, null, "Media", "seekToAudio", [this.id, milliseconds]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Pause playing audio file.
|
||||
*/
|
||||
@@ -128,6 +171,8 @@ Media.prototype.getDuration = function() {
|
||||
|
||||
/**
|
||||
* Get position of audio.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Media.prototype.getCurrentPosition = function(success, fail) {
|
||||
PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]);
|
||||
@@ -154,58 +199,3 @@ Media.prototype.release = function() {
|
||||
PhoneGap.exec(null, null, "Media", "release", [this.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* List of media objects.
|
||||
* PRIVATE
|
||||
*/
|
||||
PhoneGap.mediaObjects = {};
|
||||
|
||||
/**
|
||||
* Object that receives native callbacks.
|
||||
* PRIVATE
|
||||
* @constructor
|
||||
*/
|
||||
PhoneGap.Media = function() {};
|
||||
|
||||
/**
|
||||
* Get the media object.
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
*/
|
||||
PhoneGap.Media.getMediaObject = function(id) {
|
||||
return PhoneGap.mediaObjects[id];
|
||||
};
|
||||
|
||||
/**
|
||||
* Audio has status update.
|
||||
* PRIVATE
|
||||
*
|
||||
* @param id The media object id (string)
|
||||
* @param status The status code (int)
|
||||
* @param msg The status message (string)
|
||||
*/
|
||||
PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
var media = PhoneGap.mediaObjects[id];
|
||||
|
||||
// If state update
|
||||
if (msg === Media.MEDIA_STATE) {
|
||||
if (value === Media.MEDIA_STOPPED) {
|
||||
if (media.successCallback) {
|
||||
media.successCallback();
|
||||
}
|
||||
}
|
||||
if (media.statusCallback) {
|
||||
media.statusCallback(value);
|
||||
}
|
||||
}
|
||||
else if (msg === Media.MEDIA_DURATION) {
|
||||
media._duration = value;
|
||||
}
|
||||
else if (msg === Media.MEDIA_ERROR) {
|
||||
if (media.errorCallback) {
|
||||
media.errorCallback(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,20 +3,17 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("network")) {
|
||||
PhoneGap.addResource("network");
|
||||
|
||||
/**
|
||||
* This class contains information about any NetworkStatus.
|
||||
* @constructor
|
||||
*/
|
||||
var NetworkStatus = function() {
|
||||
function NetworkStatus() {
|
||||
//this.code = null;
|
||||
//this.message = "";
|
||||
};
|
||||
}
|
||||
|
||||
NetworkStatus.NOT_REACHABLE = 0;
|
||||
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
@@ -26,14 +23,14 @@ NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
* This class provides access to device Network data (reachability).
|
||||
* @constructor
|
||||
*/
|
||||
var Network = function() {
|
||||
function Network() {
|
||||
/**
|
||||
* The last known Network status.
|
||||
* { hostName: string, ipAddress: string,
|
||||
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
|
||||
*/
|
||||
this.lastReachability = null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when the reachability status has changed.
|
||||
@@ -59,54 +56,9 @@ Network.prototype.isReachable = function(uri, callback, options) {
|
||||
PhoneGap.exec(callback, null, "Network Status", "isReachable", [uri, isIpAddress]);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class contains information about the current network Connection.
|
||||
* @constructor
|
||||
*/
|
||||
var Connection = function() {
|
||||
this.type = null;
|
||||
this.homeNW = null;
|
||||
this.currentNW = null;
|
||||
|
||||
var me = this;
|
||||
this.getInfo(
|
||||
function(info) {
|
||||
me.type = info.type;
|
||||
me.homeNW = info.homeNW;
|
||||
me.currentNW = info.currentNW;
|
||||
PhoneGap.onPhoneGapConnectionReady.fire();
|
||||
},
|
||||
function(e) {
|
||||
console.log("Error initializing Network Connection: " + e);
|
||||
});
|
||||
};
|
||||
|
||||
Connection.UNKNOWN = 0;
|
||||
Connection.ETHERNET = 1;
|
||||
Connection.WIFI = 2;
|
||||
Connection.CELL_2G = 3;
|
||||
Connection.CELL_3G = 4;
|
||||
Connection.CELL_4G = 5;
|
||||
Connection.NONE = 20;
|
||||
|
||||
/**
|
||||
* Get connection info
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the Connection data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the Connection data. (OPTIONAL)
|
||||
*/
|
||||
Connection.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
// Get info
|
||||
PhoneGap.exec(successCallback, errorCallback, "Network Status", "getConnectionInfo", []);
|
||||
};
|
||||
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof navigator.network === "undefined") {
|
||||
navigator.network = new Network();
|
||||
}
|
||||
if (typeof navigator.network.connection === "undefined") {
|
||||
navigator.network.connection = new Connection();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,18 +3,14 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("notification")) {
|
||||
PhoneGap.addResource("notification");
|
||||
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
* @constructor
|
||||
*/
|
||||
var Notification = function() {
|
||||
};
|
||||
function Notification() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a native alert dialog, with a customizable title and button text.
|
||||
@@ -119,4 +115,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.notification = new Notification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (typeof PhoneGap === "undefined") {
|
||||
|
||||
/**
|
||||
* The order of events during page load and PhoneGap startup is as follows:
|
||||
@@ -19,8 +18,6 @@ if (typeof PhoneGap === "undefined") {
|
||||
* onPhoneGapInfoReady Internal event fired when device properties are available
|
||||
* onDeviceReady User event fired to indicate that PhoneGap is ready
|
||||
* onResume User event fired to indicate a start/resume lifecycle event
|
||||
* onPause User event fired to indicate a pause lifecycle event
|
||||
* onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
|
||||
*
|
||||
* The only PhoneGap events that user code should register for are:
|
||||
* onDeviceReady
|
||||
@@ -29,11 +26,10 @@ if (typeof PhoneGap === "undefined") {
|
||||
* Listeners can be registered as:
|
||||
* document.addEventListener("deviceready", myDeviceReadyListener, false);
|
||||
* document.addEventListener("resume", myResumeListener, false);
|
||||
* document.addEventListener("pause", myPauseListener, false);
|
||||
*/
|
||||
|
||||
if (typeof(DeviceInfo) !== 'object') {
|
||||
var DeviceInfo = {};
|
||||
DeviceInfo = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,34 +45,9 @@ var PhoneGap = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* List of resource files loaded by PhoneGap.
|
||||
* This is used to ensure JS and other files are loaded only once.
|
||||
*/
|
||||
PhoneGap.resources = {base: true};
|
||||
|
||||
/**
|
||||
* Determine if resource has been loaded by PhoneGap
|
||||
*
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.hasResource = function(name) {
|
||||
return PhoneGap.resources[name];
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a resource to list of loaded resources by PhoneGap
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
PhoneGap.addResource = function(name) {
|
||||
PhoneGap.resources[name] = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom pub-sub channel that can have functions subscribed to it
|
||||
* @constructor
|
||||
*/
|
||||
PhoneGap.Channel = function (type)
|
||||
{
|
||||
@@ -88,7 +59,7 @@ PhoneGap.Channel = function (type)
|
||||
};
|
||||
|
||||
/**
|
||||
* Subscribes the given function to the channel. Any time that
|
||||
* Subscribes the given function to the channel. Any time that
|
||||
* Channel.fire is called so too will the function.
|
||||
* Optionally specify an execution context for the function
|
||||
* and a guid that can be used to stop subscribing to the channel.
|
||||
@@ -120,7 +91,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
|
||||
_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);
|
||||
@@ -128,7 +99,7 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
|
||||
return g;
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Unsubscribes the function with the given guid from the channel.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.unsubscribe = function(g) {
|
||||
@@ -137,7 +108,7 @@ PhoneGap.Channel.prototype.unsubscribe = function(g) {
|
||||
delete this.handlers[g];
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Calls all functions subscribed to this channel.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.fire = function(e) {
|
||||
@@ -230,7 +201,7 @@ PhoneGap.addPlugin = function(name, obj) {
|
||||
};
|
||||
|
||||
/**
|
||||
* onDOMContentLoaded channel is fired when the DOM content
|
||||
* onDOMContentLoaded channel is fired when the DOM content
|
||||
* of the page has been parsed.
|
||||
*/
|
||||
PhoneGap.onDOMContentLoaded = new PhoneGap.Channel('onDOMContentLoaded');
|
||||
@@ -258,12 +229,6 @@ PhoneGap.onPhoneGapReady = new PhoneGap.Channel('onPhoneGapReady');
|
||||
*/
|
||||
PhoneGap.onPhoneGapInfoReady = new PhoneGap.Channel('onPhoneGapInfoReady');
|
||||
|
||||
/**
|
||||
* onPhoneGapConnectionReady channel is fired when the PhoneGap connection properties
|
||||
* has been set.
|
||||
*/
|
||||
PhoneGap.onPhoneGapConnectionReady = new PhoneGap.Channel('onPhoneGapConnectionReady');
|
||||
|
||||
/**
|
||||
* onResume channel is fired when the PhoneGap native code
|
||||
* resumes.
|
||||
@@ -276,19 +241,8 @@ PhoneGap.onResume = new PhoneGap.Channel('onResume');
|
||||
*/
|
||||
PhoneGap.onPause = new PhoneGap.Channel('onPause');
|
||||
|
||||
/**
|
||||
* onDestroy channel is fired when the PhoneGap native code
|
||||
* is destroyed. It is used internally.
|
||||
* Window.onunload should be used by the user.
|
||||
*/
|
||||
PhoneGap.onDestroy = new PhoneGap.Channel('onDestroy');
|
||||
PhoneGap.onDestroy.subscribeOnce(function() {
|
||||
PhoneGap.shuttingDown = true;
|
||||
});
|
||||
PhoneGap.shuttingDown = false;
|
||||
|
||||
// _nativeReady is global variable that the native side can set
|
||||
// to signify that the native code is ready. It is a global since
|
||||
// to signify that the native code is ready. It is a global since
|
||||
// it may be called before any PhoneGap JS is ready.
|
||||
if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); }
|
||||
|
||||
@@ -300,7 +254,7 @@ PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady');
|
||||
|
||||
|
||||
// Array of channels that must fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady, PhoneGap.onPhoneGapConnectionReady];
|
||||
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady];
|
||||
|
||||
// Hashtable of user defined channels that must also fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsMap = {};
|
||||
@@ -324,7 +278,7 @@ PhoneGap.waitForInitialization = function(feature) {
|
||||
* 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) {
|
||||
@@ -362,7 +316,7 @@ PhoneGap.Channel.join(function() {
|
||||
// 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();
|
||||
|
||||
@@ -393,18 +347,18 @@ document.addEventListener = function(evt, handler, capture) {
|
||||
}
|
||||
} else if (e === 'pause') {
|
||||
PhoneGap.onPause.subscribe(handler);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If subscribing to Android backbutton
|
||||
if (e === 'backbutton') {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
PhoneGap.m_document_addEventListener.call(document, evt, handler, capture);
|
||||
}
|
||||
};
|
||||
|
||||
// Intercept calls to document.removeEventListener and watch for events that
|
||||
// Intercept calls to document.removeEventListener and watch for events that
|
||||
// are generated by PhoneGap native code
|
||||
PhoneGap.m_document_removeEventListener = document.removeEventListener;
|
||||
|
||||
@@ -414,7 +368,7 @@ document.removeEventListener = function(evt, handler, capture) {
|
||||
// If unsubscribing to Android backbutton
|
||||
if (e === 'backbutton') {
|
||||
PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]);
|
||||
}
|
||||
}
|
||||
|
||||
PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
|
||||
};
|
||||
@@ -433,14 +387,14 @@ PhoneGap.fireEvent = function(type) {
|
||||
* The restriction on ours is that it must be an array of simple types.
|
||||
*
|
||||
* @param args
|
||||
* @return {String}
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.stringify = function(args) {
|
||||
if (typeof JSON === "undefined") {
|
||||
var s = "[";
|
||||
var i, type, start, name, nameType, a;
|
||||
for (i = 0; i < args.length; i++) {
|
||||
if (args[i] !== null) {
|
||||
if (args[i] != null) {
|
||||
if (i > 0) {
|
||||
s = s + ",";
|
||||
}
|
||||
@@ -465,7 +419,7 @@ PhoneGap.stringify = function(args) {
|
||||
// don't copy the functions
|
||||
s = s + '""';
|
||||
} else if (args[i][name] instanceof Object) {
|
||||
s = s + PhoneGap.stringify(args[i][name]);
|
||||
s = s + this.stringify(args[i][name]);
|
||||
} else {
|
||||
s = s + '"' + args[i][name] + '"';
|
||||
}
|
||||
@@ -491,41 +445,41 @@ PhoneGap.stringify = function(args) {
|
||||
* Does a deep clone of the object.
|
||||
*
|
||||
* @param obj
|
||||
* @return {Object}
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.clone = function(obj) {
|
||||
var i, retVal;
|
||||
if(!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(obj instanceof Array){
|
||||
retVal = [];
|
||||
for(i = 0; i < obj.length; ++i){
|
||||
retVal.push(PhoneGap.clone(obj[i]));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (obj instanceof Function) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(!(obj instanceof Object)){
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(obj instanceof Array){
|
||||
retVal = [];
|
||||
for(i = 0; i < obj.length; ++i){
|
||||
retVal.push(PhoneGap.clone(obj[i]));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (obj instanceof Function) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(!(obj instanceof Object)){
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
retVal = {};
|
||||
for(i in obj){
|
||||
if(!(i in retVal) || retVal[i] !== obj[i]) {
|
||||
retVal[i] = PhoneGap.clone(obj[i]);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
retVal = {};
|
||||
for(i in obj){
|
||||
if(!(i in retVal) || retVal[i] !== obj[i]) {
|
||||
retVal[i] = PhoneGap.clone(obj[i]);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
PhoneGap.callbackId = 0;
|
||||
@@ -545,7 +499,7 @@ PhoneGap.callbackStatus = {
|
||||
|
||||
|
||||
/**
|
||||
* Execute a PhoneGap command. It is up to the native side whether this action is synch or async.
|
||||
* Execute a PhoneGap command. It is up to the native side whether this action is synch or async.
|
||||
* The native side can return:
|
||||
* Synchronous: PluginResult object as a JSON string
|
||||
* Asynchrounous: Empty string ""
|
||||
@@ -556,7 +510,7 @@ PhoneGap.callbackStatus = {
|
||||
* @param {Function} fail The fail callback
|
||||
* @param {String} service The name of the service to use
|
||||
* @param {String} action Action to be run in PhoneGap
|
||||
* @param {Array.<String>} [args] Zero or more arguments to pass to the method
|
||||
* @param {String[]} [args] Zero or more arguments to pass to the method
|
||||
*/
|
||||
PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
try {
|
||||
@@ -564,13 +518,13 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
if (success || fail) {
|
||||
PhoneGap.callbacks[callbackId] = {success:success, fail:fail};
|
||||
}
|
||||
|
||||
var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, 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) {
|
||||
|
||||
@@ -593,7 +547,7 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
|
||||
// If no result
|
||||
else if (v.status === PhoneGap.callbackStatus.NO_RESULT) {
|
||||
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!v.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
@@ -646,7 +600,7 @@ PhoneGap.callbackSuccess = function(callbackId, args) {
|
||||
console.log("Error in success callback: "+callbackId+" = "+e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!args.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
@@ -670,7 +624,7 @@ PhoneGap.callbackError = function(callbackId, args) {
|
||||
catch (e) {
|
||||
console.log("Error in error callback: "+callbackId+" = "+e);
|
||||
}
|
||||
|
||||
|
||||
// Clear callback if not expecting any more results
|
||||
if (!args.keepCallback) {
|
||||
delete PhoneGap.callbacks[callbackId];
|
||||
@@ -736,17 +690,12 @@ PhoneGap.JSCallbackToken = null;
|
||||
/**
|
||||
* This is only for Android.
|
||||
*
|
||||
* Internal function that uses XHR to call into PhoneGap Java code and retrieve
|
||||
* Internal function that uses XHR to call into PhoneGap Java code and retrieve
|
||||
* any JavaScript code that needs to be run. This is used for callbacks from
|
||||
* Java to JavaScript.
|
||||
*/
|
||||
PhoneGap.JSCallback = function() {
|
||||
|
||||
// Exit if shutting down app
|
||||
if (PhoneGap.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If polling flag was changed, start using polling from now on
|
||||
if (PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallbackPolling();
|
||||
@@ -759,16 +708,10 @@ PhoneGap.JSCallback = function() {
|
||||
xmlhttp.onreadystatechange=function(){
|
||||
if(xmlhttp.readyState === 4){
|
||||
|
||||
// Exit if shutting down app
|
||||
if (PhoneGap.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If callback has JavaScript statement to execute
|
||||
if (xmlhttp.status === 200) {
|
||||
|
||||
// Need to url decode the response and replace %20 with a space
|
||||
var msg = decodeURIComponent(xmlhttp.responseText.replace(/\+/g, '%20'));
|
||||
var msg = xmlhttp.responseText;
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(msg);
|
||||
@@ -837,17 +780,12 @@ PhoneGap.UsePolling = false; // T=use polling, F=use XHR
|
||||
/**
|
||||
* This is only for Android.
|
||||
*
|
||||
* Internal function that uses polling to call into PhoneGap Java code and retrieve
|
||||
* Internal function that uses polling to call into PhoneGap Java code and retrieve
|
||||
* any JavaScript code that needs to be run. This is used for callbacks from
|
||||
* Java to JavaScript.
|
||||
*/
|
||||
PhoneGap.JSCallbackPolling = function() {
|
||||
|
||||
// Exit if shutting down app
|
||||
if (PhoneGap.shuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If polling flag was changed, stop using polling from now on
|
||||
if (!PhoneGap.UsePolling) {
|
||||
PhoneGap.JSCallback();
|
||||
@@ -875,7 +813,7 @@ PhoneGap.JSCallbackPolling = function() {
|
||||
/**
|
||||
* Create a UUID
|
||||
*
|
||||
* @return {String}
|
||||
* @return
|
||||
*/
|
||||
PhoneGap.createUUID = function() {
|
||||
return PhoneGap.UUIDcreatePart(4) + '-' +
|
||||
@@ -917,7 +855,7 @@ PhoneGap.close = function(context, func, params) {
|
||||
* @param {Function} successCallback The callback to call when the file has been loaded.
|
||||
*/
|
||||
PhoneGap.includeJavascript = function(jsfile, successCallback) {
|
||||
var id = document.getElementsByTagName("head")[0];
|
||||
var id = document.getElementsByTagName("head")[0];
|
||||
var el = document.createElement('script');
|
||||
el.type = 'text/javascript';
|
||||
if (typeof successCallback === 'function') {
|
||||
@@ -926,17 +864,3 @@ PhoneGap.includeJavascript = function(jsfile, successCallback) {
|
||||
el.src = jsfile;
|
||||
id.appendChild(el);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is provided to bridge the gap between the way plugins were setup in 0.9.3 and 0.9.4.
|
||||
* Users should be calling navigator.add.addService() instead of PluginManager.addService().
|
||||
* @class
|
||||
* @deprecated
|
||||
*/
|
||||
var PluginManager = {
|
||||
addService: function(serviceType, className) {
|
||||
navigator.app.addService(serviceType, className);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("position")) {
|
||||
PhoneGap.addResource("position");
|
||||
|
||||
/**
|
||||
* This class contains position information.
|
||||
* @param {Object} lat
|
||||
@@ -20,13 +17,12 @@ PhoneGap.addResource("position");
|
||||
* @param {Object} vel
|
||||
* @constructor
|
||||
*/
|
||||
var Position = function(coords, timestamp) {
|
||||
function Position(coords, timestamp) {
|
||||
this.coords = coords;
|
||||
this.timestamp = (timestamp !== 'undefined') ? timestamp : new Date().getTime();
|
||||
};
|
||||
}
|
||||
|
||||
/** @constructor */
|
||||
var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
|
||||
function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/**
|
||||
* The latitude of the position.
|
||||
*/
|
||||
@@ -54,14 +50,14 @@ var Coordinates = function(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class specifies the options for requesting position data.
|
||||
* @constructor
|
||||
*/
|
||||
var PositionOptions = function() {
|
||||
function PositionOptions() {
|
||||
/**
|
||||
* Specifies the desired position accuracy.
|
||||
*/
|
||||
@@ -71,19 +67,18 @@ var PositionOptions = function() {
|
||||
* is called.
|
||||
*/
|
||||
this.timeout = 10000;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* This class contains information about any GSP errors.
|
||||
* @constructor
|
||||
*/
|
||||
var PositionError = function() {
|
||||
function PositionError() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
};
|
||||
}
|
||||
|
||||
PositionError.UNKNOWN_ERROR = 0;
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
PositionError.TIMEOUT = 3;
|
||||
}
|
||||
|
||||
@@ -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-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -12,42 +12,9 @@
|
||||
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("storage")) {
|
||||
PhoneGap.addResource("storage");
|
||||
|
||||
/**
|
||||
* SQL result set object
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB_Rows = function() {
|
||||
this.resultSet = []; // results array
|
||||
this.length = 0; // number of rows
|
||||
};
|
||||
|
||||
/**
|
||||
* Get item from SQL result set
|
||||
*
|
||||
* @param row The row number to return
|
||||
* @return The row object
|
||||
*/
|
||||
DroidDB_Rows.prototype.item = function(row) {
|
||||
return this.resultSet[row];
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL result set that is returned to user.
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB_Result = function() {
|
||||
this.rows = new DroidDB_Rows();
|
||||
};
|
||||
|
||||
/**
|
||||
* Storage object that is called by native code when performing queries.
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB = function() {
|
||||
this.queryQueue = {};
|
||||
@@ -132,40 +99,9 @@ DroidDB.prototype.fail = function(reason, id) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL query object
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @constructor
|
||||
* @param tx The transaction object that this query belongs to
|
||||
*/
|
||||
var DroidDB_Query = function(tx) {
|
||||
|
||||
// Set the id of the query
|
||||
this.id = PhoneGap.createUUID();
|
||||
|
||||
// Add this query to the queue
|
||||
droiddb.queryQueue[this.id] = this;
|
||||
|
||||
// Init result
|
||||
this.resultSet = [];
|
||||
|
||||
// Set transaction that this query belongs to
|
||||
this.tx = tx;
|
||||
|
||||
// Add this query to transaction list
|
||||
this.tx.queryList[this.id] = this;
|
||||
|
||||
// Callbacks
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Transaction object
|
||||
* PRIVATE METHOD
|
||||
* @constructor
|
||||
*/
|
||||
var DroidDB_Tx = function() {
|
||||
|
||||
@@ -180,6 +116,37 @@ var DroidDB_Tx = function() {
|
||||
this.queryList = {};
|
||||
};
|
||||
|
||||
|
||||
var DatabaseShell = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a transaction.
|
||||
* Does not support rollback in event of failure.
|
||||
*
|
||||
* @param process {Function} The transaction function
|
||||
* @param successCallback {Function}
|
||||
* @param errorCallback {Function}
|
||||
*/
|
||||
DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
|
||||
var tx = new DroidDB_Tx();
|
||||
tx.successCallback = successCallback;
|
||||
tx.errorCallback = errorCallback;
|
||||
try {
|
||||
process(tx);
|
||||
} catch (e) {
|
||||
console.log("Transaction error: "+e);
|
||||
if (tx.errorCallback) {
|
||||
try {
|
||||
tx.errorCallback(e);
|
||||
} catch (ex) {
|
||||
console.log("Transaction error calling user error callback: "+e);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Mark query in transaction as complete.
|
||||
* If all queries are complete, call the user's transaction success callback.
|
||||
@@ -195,7 +162,7 @@ DroidDB_Tx.prototype.queryComplete = function(id) {
|
||||
var i;
|
||||
for (i in this.queryList) {
|
||||
if (this.queryList.hasOwnProperty(i)) {
|
||||
count++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count === 0) {
|
||||
@@ -231,6 +198,35 @@ DroidDB_Tx.prototype.queryFailed = function(id, reason) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL query object
|
||||
* PRIVATE METHOD
|
||||
*
|
||||
* @param tx The transaction object that this query belongs to
|
||||
*/
|
||||
var DroidDB_Query = function(tx) {
|
||||
|
||||
// Set the id of the query
|
||||
this.id = PhoneGap.createUUID();
|
||||
|
||||
// Add this query to the queue
|
||||
droiddb.queryQueue[this.id] = this;
|
||||
|
||||
// Init result
|
||||
this.resultSet = [];
|
||||
|
||||
// Set transaction that this query belongs to
|
||||
this.tx = tx;
|
||||
|
||||
// Add this query to transaction list
|
||||
this.tx.queryList[this.id] = this;
|
||||
|
||||
// Callbacks
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute SQL statement
|
||||
*
|
||||
@@ -258,33 +254,31 @@ DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCa
|
||||
PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
|
||||
};
|
||||
|
||||
var DatabaseShell = function() {
|
||||
/**
|
||||
* SQL result set that is returned to user.
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
DroidDB_Result = function() {
|
||||
this.rows = new DroidDB_Rows();
|
||||
};
|
||||
|
||||
/**
|
||||
* Start a transaction.
|
||||
* Does not support rollback in event of failure.
|
||||
*
|
||||
* @param process {Function} The transaction function
|
||||
* @param successCallback {Function}
|
||||
* @param errorCallback {Function}
|
||||
* SQL result set object
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
|
||||
var tx = new DroidDB_Tx();
|
||||
tx.successCallback = successCallback;
|
||||
tx.errorCallback = errorCallback;
|
||||
try {
|
||||
process(tx);
|
||||
} catch (e) {
|
||||
console.log("Transaction error: "+e);
|
||||
if (tx.errorCallback) {
|
||||
try {
|
||||
tx.errorCallback(e);
|
||||
} catch (ex) {
|
||||
console.log("Transaction error calling user error callback: "+e);
|
||||
}
|
||||
}
|
||||
}
|
||||
DroidDB_Rows = function() {
|
||||
this.resultSet = []; // results array
|
||||
this.length = 0; // number of rows
|
||||
};
|
||||
|
||||
/**
|
||||
* Get item from SQL result set
|
||||
*
|
||||
* @param row The row number to return
|
||||
* @return The row object
|
||||
*/
|
||||
DroidDB_Rows.prototype.item = function(row) {
|
||||
return this.resultSet[row];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -296,133 +290,88 @@ DatabaseShell.prototype.transaction = function(process, errorCallback, successCa
|
||||
* @param size Database size in bytes
|
||||
* @return Database object
|
||||
*/
|
||||
var DroidDB_openDatabase = function(name, version, display_name, size) {
|
||||
DroidDB_openDatabase = function(name, version, display_name, size) {
|
||||
PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
|
||||
var db = new DatabaseShell();
|
||||
return db;
|
||||
};
|
||||
|
||||
/**
|
||||
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
|
||||
* TODO: Do similar for sessionStorage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
|
||||
* TODO: Do similar for sessionStorage.
|
||||
*/
|
||||
|
||||
var CupcakeLocalStorage = function() {
|
||||
try {
|
||||
|
||||
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
|
||||
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
|
||||
var storage = {};
|
||||
this.length = 0;
|
||||
function setLength (length) {
|
||||
this.length = length;
|
||||
localStorage.length = length;
|
||||
}
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
var i;
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
|
||||
for(var i = 0; i < result.rows.length; i++) {
|
||||
storage[result.rows.item(i)['id']] = result.rows.item(i)['body'];
|
||||
for(i = 0; i < result.rows.length; i++) {
|
||||
storage[result.rows.item(i).id] = result.rows.item(i).body;
|
||||
}
|
||||
setLength(result.rows.length);
|
||||
PhoneGap.initializationComplete("cupcakeStorage");
|
||||
PhoneGap.initializationComplete("cupcakeStorage");
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
function (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
);
|
||||
this.setItem = function(key, val) {
|
||||
if (typeof(storage[key])=='undefined') {
|
||||
this.length++;
|
||||
}
|
||||
//console.log('set');
|
||||
storage[key] = val;
|
||||
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
|
||||
transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
|
||||
}
|
||||
);
|
||||
};
|
||||
this.getItem = function(key) {
|
||||
this.getItem = function(key) {
|
||||
return storage[key];
|
||||
};
|
||||
this.removeItem = function(key) {
|
||||
delete storage[key];
|
||||
this.length--;
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
|
||||
transaction.executeSql('DELETE FROM storage where id=?', [key]);
|
||||
}
|
||||
);
|
||||
};
|
||||
this.clear = function() {
|
||||
storage = {};
|
||||
this.length = 0;
|
||||
this.db.transaction(
|
||||
function (transaction) {
|
||||
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
|
||||
|
||||
transaction.executeSql('DELETE FROM storage', []);
|
||||
}
|
||||
);
|
||||
};
|
||||
this.key = function(index) {
|
||||
var i = 0;
|
||||
for (var j in storage) {
|
||||
if (i==index) {
|
||||
return j;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
};
|
||||
} catch(e) {
|
||||
alert("Database error "+e+".");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
var setupDroidDB = function() {
|
||||
if (typeof window.openDatabase === "undefined") {
|
||||
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
if (typeof window.openDatabase === "undefined") {
|
||||
setupDroidDB();
|
||||
} else {
|
||||
window.openDatabase_orig = window.openDatabase;
|
||||
window.openDatabase = function(name, version, desc, size){
|
||||
// Some versions of Android will throw a SECURITY_ERR so we need
|
||||
// to catch the exception and seutp our own DB handling.
|
||||
var db = null;
|
||||
try {
|
||||
db = window.openDatabase_orig(name, version, desc, size);
|
||||
}
|
||||
catch (ex) {
|
||||
db = null;
|
||||
}
|
||||
|
||||
if (db == null) {
|
||||
setupDroidDB();
|
||||
return DroidDB_openDatabase(name, version, desc, size);
|
||||
}
|
||||
else {
|
||||
return db;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window.localStorage === "undefined") {
|
||||
navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
|
||||
PhoneGap.waitForInitialization("cupcakeStorage");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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.');
|
||||
};
|
||||
})();
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="phonegap.0.9.5.min.js"></script>
|
||||
<script src="phonegap.0.9.4.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
27
framework/gen/com/phonegap/R.java
Normal file
27
framework/gen/com/phonegap/R.java
Normal file
@@ -0,0 +1,27 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
|
||||
package com.phonegap;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
}
|
||||
public static final class drawable {
|
||||
public static final int icon=0x7f020000;
|
||||
public static final int splash=0x7f020001;
|
||||
}
|
||||
public static final class id {
|
||||
public static final int appView=0x7f050000;
|
||||
}
|
||||
public static final class layout {
|
||||
public static final int main=0x7f030000;
|
||||
}
|
||||
public static final class string {
|
||||
public static final int app_name=0x7f040000;
|
||||
public static final int go=0x7f040001;
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,6 @@ public class AudioHandler extends Plugin {
|
||||
else if (action.equals("startPlayingAudio")) {
|
||||
this.startPlayingAudio(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("seekToAudio")) {
|
||||
this.seekToAudio(args.getString(0), args.getInt(1));
|
||||
}
|
||||
else if (action.equals("pausePlayingAudio")) {
|
||||
this.pausePlayingAudio(args.getString(0));
|
||||
}
|
||||
@@ -184,20 +181,6 @@ public class AudioHandler extends Plugin {
|
||||
audio.startPlaying(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a location.
|
||||
*
|
||||
*
|
||||
* @param id The id of the audio player
|
||||
* @param miliseconds int: number of milliseconds to skip 1000 = 1 second
|
||||
*/
|
||||
public void seekToAudio(String id, int milliseconds) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
audio.seekToPlaying(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
*
|
||||
|
||||
@@ -233,15 +233,6 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek or jump to a new time in the track.
|
||||
*/
|
||||
public void seekToPlaying(int milliseconds) {
|
||||
if (this.mPlayer != null) {
|
||||
this.mPlayer.seekTo(milliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause playing.
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,6 @@ import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
@@ -222,10 +221,7 @@ public class CallbackServer implements Runnable {
|
||||
}
|
||||
else {
|
||||
//System.out.println("CallbackServer -- sending item");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n";
|
||||
String js = this.getJavascript();
|
||||
if (js != null)
|
||||
response += URLEncoder.encode(js, "UTF-8");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,353 +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) 2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
public class Capture extends Plugin {
|
||||
|
||||
private static final String _DATA = "_data"; // The column name where the file path is stored
|
||||
private static final int CAPTURE_AUDIO = 0; // Constant for capture audio
|
||||
private static final int CAPTURE_IMAGE = 1; // Constant for capture image
|
||||
private static final int CAPTURE_VIDEO = 2; // Constant for capture video
|
||||
private static final String LOG_TAG = "Capture";
|
||||
private String callbackId; // The ID of the callback to be invoked with our result
|
||||
private long limit; // the number of pics/vids/clips to take
|
||||
private double duration; // optional duration parameter for video recording
|
||||
private JSONArray results; // The array of results to be returned to the user
|
||||
private Uri imageUri; // Uri of captured image
|
||||
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
this.callbackId = callbackId;
|
||||
this.limit = 1;
|
||||
this.duration = 0.0f;
|
||||
this.results = new JSONArray();
|
||||
|
||||
JSONObject options = args.optJSONObject(0);
|
||||
if (options != null) {
|
||||
limit = options.optLong("limit", 1);
|
||||
duration = options.optDouble("duration", 0.0f);
|
||||
}
|
||||
|
||||
if (action.equals("getFormatData")) {
|
||||
try {
|
||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||
return new PluginResult(PluginResult.Status.OK, obj);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("captureAudio")) {
|
||||
this.captureAudio();
|
||||
}
|
||||
else if (action.equals("captureImage")) {
|
||||
this.captureImage();
|
||||
}
|
||||
else if (action.equals("captureVideo")) {
|
||||
this.captureVideo(duration);
|
||||
}
|
||||
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the media data file data depending on it's mime type
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param mimeType of the file
|
||||
* @return a MediaFileData object
|
||||
*/
|
||||
private JSONObject getFormatData(String filePath, String mimeType) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
// setup defaults
|
||||
obj.put("height", 0);
|
||||
obj.put("width", 0);
|
||||
obj.put("bitrate", 0);
|
||||
obj.put("duration", 0);
|
||||
obj.put("codecs", "");
|
||||
|
||||
if (mimeType.equals("image/jpeg")) {
|
||||
obj = getImageData(filePath, obj);
|
||||
}
|
||||
else if (filePath.endsWith("audio/3gpp")) {
|
||||
obj = getAudioVideoData(filePath, obj, false);
|
||||
}
|
||||
else if (mimeType.equals("video/3gpp")) {
|
||||
obj = getAudioVideoData(filePath, obj, true);
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Error: setting media file data object");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
|
||||
obj.put("height", bitmap.getHeight());
|
||||
obj.put("width", bitmap.getWidth());
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @param video if true get video attributes as well
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
|
||||
MediaPlayer player = new MediaPlayer();
|
||||
try {
|
||||
player.setDataSource(filePath);
|
||||
player.prepare();
|
||||
obj.put("duration", player.getDuration());
|
||||
if (video) {
|
||||
obj.put("height", player.getVideoHeight());
|
||||
obj.put("width", player.getVideoWidth());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.d(LOG_TAG, "Error: loading video file");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture audio. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureAudio() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture images. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureImage() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = new File(Environment.getExternalStorageDirectory(), "Capture.jpg");
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture video. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureVideo(double duration) {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
// Introduced in API 8
|
||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the video view exits.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Result received okay
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// An audio clip was requested
|
||||
if (requestCode == CAPTURE_AUDIO) {
|
||||
// Get the uri of the audio clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for listening to audio
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more audio clips
|
||||
captureAudio();
|
||||
}
|
||||
} else if (requestCode == CAPTURE_IMAGE) {
|
||||
// For some reason if I try to do:
|
||||
// Uri data = intent.getData();
|
||||
// It crashes in the emulator and on my phone with a null pointer exception
|
||||
// To work around it I had to grab the code from CameraLauncher.java
|
||||
try {
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
|
||||
// Create entry in media store for image
|
||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri uri = null;
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
System.out.println("Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
System.out.println("Can't write to internal media storage.");
|
||||
this.fail("Error capturing image - no media storage found.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.ctx.getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
|
||||
os.close();
|
||||
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
|
||||
// Add image to results
|
||||
results.put(createMediaFile(uri));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more images
|
||||
captureImage();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.fail("Error capturing image.");
|
||||
}
|
||||
} else if (requestCode == CAPTURE_VIDEO) {
|
||||
// Get the uri of the video clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If canceled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
}
|
||||
// user canceled the action
|
||||
else {
|
||||
this.fail("Canceled.");
|
||||
}
|
||||
}
|
||||
// If something else
|
||||
else {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
}
|
||||
// something bad happened
|
||||
else {
|
||||
this.fail("Did not complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSONObject that represents a File from the Uri
|
||||
*
|
||||
* @param data the Uri of the audio/image/video
|
||||
* @return a JSONObject that represents a File
|
||||
*/
|
||||
private JSONObject createMediaFile(Uri data) {
|
||||
File fp = new File(getRealPathFromURI(data));
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
// File properties
|
||||
obj.put("name", fp.getName());
|
||||
obj.put("fullPath", fp.getAbsolutePath());
|
||||
obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath()));
|
||||
obj.put("lastModifiedDate", fp.lastModified());
|
||||
obj.put("size", fp.length());
|
||||
} catch (JSONException e) {
|
||||
// this will never happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the media store to find out what the file path is for the Uri we supply
|
||||
*
|
||||
* @param contentUri the Uri of the audio/image/video
|
||||
* @return the full path to the file
|
||||
*/
|
||||
private String getRealPathFromURI(Uri contentUri) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = this.ctx.managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error message to JavaScript.
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
public void fail(String err) {
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
}
|
||||
}
|
||||
@@ -46,14 +46,7 @@ public class ContactManager extends Plugin {
|
||||
return new PluginResult(status, res, "navigator.service.contacts.cast");
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
if (contactAccessor.save(args.getJSONObject(0))) {
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
else {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("code", 0);
|
||||
return new PluginResult(PluginResult.Status.ERROR, r);
|
||||
}
|
||||
return new PluginResult(status, contactAccessor.save(args.getJSONObject(0)));
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
if (contactAccessor.remove(args.getString(0))) {
|
||||
|
||||
@@ -20,7 +20,7 @@ import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "0.9.5"; // PhoneGap version
|
||||
public static String phonegapVersion = "0.9.4"; // PhoneGap version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
|
||||
@@ -10,19 +10,18 @@ package com.phonegap;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import android.app.AlertDialog;
|
||||
import android.widget.EditText;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Picture;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
@@ -32,6 +31,7 @@ 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;
|
||||
@@ -111,8 +111,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
protected WebView appView;
|
||||
protected WebViewClient webViewClient;
|
||||
|
||||
protected LinearLayout root;
|
||||
public boolean bound = false;
|
||||
private LinearLayout root;
|
||||
boolean bound = false;
|
||||
public CallbackServer callbackServer;
|
||||
protected PluginManager pluginManager;
|
||||
protected boolean cancelLoadUrl = false;
|
||||
@@ -125,8 +125,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
private String baseUrl;
|
||||
|
||||
// Plugin to call when activity result is received
|
||||
protected Plugin activityResultCallback = null;
|
||||
protected boolean activityResultKeepRunning;
|
||||
private Plugin activityResultCallback = null;
|
||||
private boolean activityResultKeepRunning;
|
||||
|
||||
// Flag indicates that a loadUrl timeout occurred
|
||||
private int loadUrlTimeout = 0;
|
||||
@@ -202,11 +202,11 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
WebViewReflect.checkCompatibility();
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
this.appView.setWebChromeClient(new GapClient(DroidGap.this));
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("2.")) {
|
||||
this.appView.setWebChromeClient(new EclairClient(DroidGap.this));
|
||||
}
|
||||
else {
|
||||
this.appView.setWebChromeClient(new EclairClient(DroidGap.this));
|
||||
this.appView.setWebChromeClient(new GapClient(DroidGap.this));
|
||||
}
|
||||
|
||||
this.setWebViewClient(this.appView, new GapViewClient(this));
|
||||
@@ -235,8 +235,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Bind PhoneGap objects to JavaScript
|
||||
this.bindBrowser(this.appView);
|
||||
|
||||
// Add web view but make it invisible while loading URL
|
||||
this.appView.setVisibility(View.INVISIBLE);
|
||||
// Add web view
|
||||
root.addView(this.appView);
|
||||
setContentView(root);
|
||||
|
||||
@@ -287,7 +286,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.addService("Storage", "com.phonegap.Storage");
|
||||
this.addService("Temperature", "com.phonegap.TempListener");
|
||||
this.addService("FileTransfer", "com.phonegap.FileTransfer");
|
||||
this.addService("Capture", "com.phonegap.Capture");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,7 +302,8 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If spashscreen
|
||||
this.splashscreen = this.getIntegerProperty("splashscreen", 0);
|
||||
if (this.splashscreen != 0) {
|
||||
root.setBackgroundResource(this.splashscreen);
|
||||
this.appView.setBackgroundColor(0);
|
||||
this.appView.setBackgroundResource(splashscreen);
|
||||
}
|
||||
|
||||
// If hideLoadingDialogOnPageLoad
|
||||
@@ -601,19 +600,16 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
if (this.appView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send pause event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning) {
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onPause();
|
||||
|
||||
// Send pause event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.appView.pauseTimers();
|
||||
}
|
||||
@@ -625,12 +621,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (this.appView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Send resume event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};");
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning || this.activityResultKeepRunning) {
|
||||
@@ -644,6 +634,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Forward to plugins
|
||||
this.pluginManager.onResume();
|
||||
|
||||
// Send resume event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onResume.fire();}catch(e){};");
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
@@ -656,14 +649,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
if (this.appView != null) {
|
||||
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
// Send destroy event to JavaScript
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onDestroy.fire();}catch(e){};");
|
||||
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
@@ -673,7 +661,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -785,7 +772,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
// Calling PluginManager.exec() to call a native service using
|
||||
// prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
||||
if (defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) {
|
||||
if (defaultValue.substring(0, 4).equals("gap:")) {
|
||||
JSONArray array;
|
||||
try {
|
||||
array = new JSONArray(defaultValue.substring(4));
|
||||
@@ -826,37 +813,18 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
// Show dialog
|
||||
else {
|
||||
final JsPromptResult res = result;
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
|
||||
dlg.setMessage(message);
|
||||
final EditText input = new EditText(this.ctx);
|
||||
dlg.setView(input);
|
||||
dlg.setCancelable(false);
|
||||
dlg.setPositiveButton(android.R.string.ok,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
String usertext = input.getText().toString();
|
||||
res.confirm(usertext);
|
||||
}
|
||||
});
|
||||
dlg.setNegativeButton(android.R.string.cancel,
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
res.cancel();
|
||||
}
|
||||
});
|
||||
dlg.create();
|
||||
dlg.show();
|
||||
}
|
||||
//@TODO:
|
||||
result.confirm("");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WebChromeClient that extends GapClient with additional support for Android 2.X
|
||||
*/
|
||||
public class EclairClient extends GapClient {
|
||||
public final class EclairClient extends GapClient {
|
||||
|
||||
private String TAG = "PhoneGapLog";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
@@ -910,12 +878,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Instructs the client to show a prompt to ask the user to set the Geolocation permission state for the specified origin.
|
||||
*
|
||||
* @param origin
|
||||
* @param callback
|
||||
*/
|
||||
public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
|
||||
// TODO Auto-generated method stub
|
||||
super.onGeolocationPermissionsShowPrompt(origin, callback);
|
||||
@@ -986,38 +948,19 @@ public class DroidGap extends PhonegapActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If sms:5551212?body=This is the message
|
||||
else if (url.startsWith("sms:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
|
||||
// Get address
|
||||
String address = null;
|
||||
int parmIndex = url.indexOf('?');
|
||||
if (parmIndex == -1) {
|
||||
address = url.substring(4);
|
||||
}
|
||||
else {
|
||||
address = url.substring(4, parmIndex);
|
||||
|
||||
// If body, then set sms body
|
||||
Uri uri = Uri.parse(url);
|
||||
String query = uri.getQuery();
|
||||
if (query != null) {
|
||||
if (query.startsWith("body=")) {
|
||||
intent.putExtra("sms_body", query.substring(5));
|
||||
}
|
||||
}
|
||||
}
|
||||
intent.setData(Uri.parse("sms:"+address));
|
||||
intent.putExtra("address", address);
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error sending sms "+url+":"+ e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// If sms:5551212
|
||||
else if (url.startsWith("sms:")) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
intent.putExtra("address", url.substring(4));
|
||||
intent.setType("vnd.android-dir/mms-sms");
|
||||
startActivity(intent);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error sending sms "+url+":"+ e.toString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// All else
|
||||
else {
|
||||
@@ -1067,8 +1010,16 @@ public class DroidGap extends PhonegapActivity {
|
||||
// from the JS side when the JS gets to that code.
|
||||
appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
|
||||
// Make app view visible
|
||||
appView.setVisibility(View.VISIBLE);
|
||||
// If splash screen is showing, clear it
|
||||
if (this.ctx.splashscreen != 0) {
|
||||
this.ctx.splashscreen = 0;
|
||||
appView.setPictureListener(new PictureListener(){
|
||||
public void onNewPicture(WebView viewtwo, Picture picture) {
|
||||
appView.setBackgroundResource(0);
|
||||
appView.setPictureListener(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Stop "app loading" spinner if showing
|
||||
if (this.ctx.hideLoadingDialogOnPageLoad) {
|
||||
@@ -1115,9 +1066,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (this.appView == null) {
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
// If back key
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
|
||||
@@ -272,7 +272,7 @@ public class FileTransfer extends Plugin {
|
||||
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("Content-Disposition: form-data; name=\"" + key.toString() + "\"; ");
|
||||
dos.writeBytes(LINE_END + LINE_END);
|
||||
dos.writeBytes(params.getString(key.toString()));
|
||||
dos.writeBytes(LINE_END);
|
||||
@@ -315,13 +315,7 @@ public class FileTransfer extends Plugin {
|
||||
|
||||
//------------------ read the SERVER RESPONSE
|
||||
StringBuffer responseString = new StringBuffer("");
|
||||
DataInputStream inStream;
|
||||
try {
|
||||
inStream = new DataInputStream ( conn.getInputStream() );
|
||||
} catch(FileNotFoundException e) {
|
||||
throw new IOException("Received error from server");
|
||||
}
|
||||
|
||||
DataInputStream inStream = new DataInputStream ( conn.getInputStream() );
|
||||
String line;
|
||||
while (( line = inStream.readLine()) != null) {
|
||||
responseString.append(line);
|
||||
|
||||
@@ -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-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
@@ -97,23 +97,48 @@ public class FileUtils extends Plugin {
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("readAsText")) {
|
||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, s);
|
||||
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")) {
|
||||
String s = this.readAsDataURL(args.getString(0));
|
||||
return new PluginResult(status, s);
|
||||
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")) {
|
||||
this.writeAsText(args.getString(0), args.getString(1), args.getBoolean(2));
|
||||
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")) {
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
|
||||
return new PluginResult(status, fileSize);
|
||||
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")) {
|
||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
||||
return new PluginResult(status, fileSize);
|
||||
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);
|
||||
@@ -811,19 +836,49 @@ public class FileUtils extends Plugin {
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("testSaveLocationExists")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("getFreeDiskSpace")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("testFileExists")) {
|
||||
return true;
|
||||
}
|
||||
else if (action.equals("testDirectoryExists")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
if (action.equals("readAsText")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@@ -887,7 +942,7 @@ public class FileUtils extends Plugin {
|
||||
* @param filename
|
||||
* @return a mime type
|
||||
*/
|
||||
public static String getMimeType(String filename) {
|
||||
private String getMimeType(String filename) {
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
|
||||
}
|
||||
@@ -920,26 +975,16 @@ public class FileUtils extends Plugin {
|
||||
* @param offset The position to begin writing the file.
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
/**/
|
||||
public long write(String filename, String data, int offset) throws FileNotFoundException, IOException {
|
||||
boolean append = false;
|
||||
if (offset > 0) {
|
||||
this.truncateFile(filename, offset);
|
||||
append = true;
|
||||
}
|
||||
|
||||
byte [] rawData = data.getBytes();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
|
||||
FileOutputStream out = new FileOutputStream(filename, append);
|
||||
byte buff[] = new byte[rawData.length];
|
||||
in.read(buff, 0, buff.length);
|
||||
out.write(buff, 0, rawData.length);
|
||||
out.flush();
|
||||
out.close();
|
||||
public long write(String filename, String data, long offset) throws FileNotFoundException, IOException {
|
||||
RandomAccessFile file = new RandomAccessFile(filename, "rw");
|
||||
file.seek(offset);
|
||||
file.writeBytes(data);
|
||||
file.close();
|
||||
|
||||
return data.length();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Truncate the file to size
|
||||
*
|
||||
|
||||
@@ -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-2011, IBM Corporation
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
@@ -11,54 +11,21 @@ import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.*;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.util.Log;
|
||||
|
||||
public class NetworkManager extends Plugin {
|
||||
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
|
||||
public static final String WIFI = "wifi";
|
||||
public static final String WIMAX = "wimax";
|
||||
// mobile
|
||||
public static final String MOBILE = "mobile";
|
||||
// 2G network types
|
||||
public static final String GSM = "gsm";
|
||||
public static final String GPRS = "gprs";
|
||||
public static final String EDGE = "edge";
|
||||
// 3G network types
|
||||
public static final String CDMA = "cdma";
|
||||
public static final String UMTS = "umts";
|
||||
// 4G network types
|
||||
public static final String LTE = "lte";
|
||||
public static final String UMB = "umb";
|
||||
// return types
|
||||
public static final int TYPE_UNKNOWN = 0;
|
||||
public static final int TYPE_ETHERNET = 1;
|
||||
public static final int TYPE_WIFI = 2;
|
||||
public static final int TYPE_2G = 3;
|
||||
public static final int TYPE_3G = 4;
|
||||
public static final int TYPE_4G = 5;
|
||||
public static final int TYPE_NONE = 20;
|
||||
|
||||
private static final String LOG_TAG = "NetworkManager";
|
||||
private String connectionCallbackId;
|
||||
|
||||
ConnectivityManager sockMan;
|
||||
TelephonyManager telephonyManager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -74,18 +41,7 @@ public class NetworkManager extends Plugin {
|
||||
*/
|
||||
public void setContext(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.telephonyManager = ((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE));
|
||||
|
||||
// We need to listen to connectivity events to update navigator.connection
|
||||
IntentFilter intentFilter = new IntentFilter() ;
|
||||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
ctx.registerReceiver(new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
}
|
||||
}, intentFilter);
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,13 +68,6 @@ public class NetworkManager extends Plugin {
|
||||
int i = this.isReachable(args.getString(0), args.getBoolean(1));
|
||||
return new PluginResult(status, i);
|
||||
}
|
||||
else if (action.equals("getConnectionInfo")) {
|
||||
this.connectionCallbackId = callbackId;
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
PluginResult pluginResult = new PluginResult(status, this.getConnectionInfo(info));
|
||||
pluginResult.setKeepCallback(true);
|
||||
return pluginResult;
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
@@ -140,107 +89,7 @@ public class NetworkManager extends Plugin {
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
|
||||
/**
|
||||
* Updates the JavaScript side whenever the connection changes
|
||||
*
|
||||
* @param info the current active network info
|
||||
* @return
|
||||
*/
|
||||
private void updateConnectionInfo(NetworkInfo info) {
|
||||
JSONObject connection = this.getConnectionInfo(info);
|
||||
|
||||
// send update to javascript "navigator.connection"
|
||||
sendUpdate(connection);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the latest network connection information
|
||||
*
|
||||
* @param info the current active network info
|
||||
* @return a JSONObject that represents the network info
|
||||
*/
|
||||
private JSONObject getConnectionInfo(NetworkInfo info) {
|
||||
JSONObject connection = new JSONObject();
|
||||
|
||||
try {
|
||||
if (info != null) {
|
||||
// If we are not connected to any network set type to none
|
||||
if (!info.isConnected()) {
|
||||
connection.put("type", TYPE_NONE);
|
||||
connection.put("homeNW", null);
|
||||
connection.put("currentNW", null);
|
||||
}
|
||||
else {
|
||||
// If we are connected check which type
|
||||
// First off is wifi
|
||||
if (info.getTypeName().toLowerCase().equals(WIFI)) {
|
||||
connection.put("type", TYPE_WIFI);
|
||||
connection.put("homeNW", null);
|
||||
connection.put("currentNW", null);
|
||||
}
|
||||
// Otherwise it must be one of the mobile network protocols
|
||||
else {
|
||||
// Determine the correct type, 2G, 3G, 4G
|
||||
connection.put("type", getType(info));
|
||||
connection.put("homeNW", telephonyManager.getSimOperatorName());
|
||||
connection.put("currentNW", telephonyManager.getNetworkOperatorName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
// this should never happen
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
*
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(JSONObject connection) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, connection);
|
||||
result.setKeepCallback(true);
|
||||
this.success(result, this.connectionCallbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of connection
|
||||
*
|
||||
* @param info the network info so we can determine connection type.
|
||||
* @return the type of mobile network we are on
|
||||
*/
|
||||
private int getType(NetworkInfo info) {
|
||||
if (info != null) {
|
||||
String type = info.getTypeName();
|
||||
|
||||
if (type.toLowerCase().equals(MOBILE)) {
|
||||
type = info.getSubtypeName();
|
||||
if (type.toLowerCase().equals(GSM) ||
|
||||
type.toLowerCase().equals(GPRS) ||
|
||||
type.toLowerCase().equals(EDGE)) {
|
||||
return TYPE_2G;
|
||||
}
|
||||
else if (type.toLowerCase().equals(CDMA) ||
|
||||
type.toLowerCase().equals(UMTS)) {
|
||||
return TYPE_3G;
|
||||
}
|
||||
else if (type.toLowerCase().equals(LTE) ||
|
||||
type.toLowerCase().equals(UMB)) {
|
||||
return TYPE_4G;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Determine if a network connection exists.
|
||||
*
|
||||
* @return
|
||||
@@ -301,4 +150,4 @@ public class NetworkManager extends Plugin {
|
||||
|
||||
return reachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,21 +16,15 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.*;
|
||||
|
||||
/**
|
||||
* This class implements the HTML5 database support for Android 1.X devices. It
|
||||
* is not used for Android 2.X, since HTML5 database is built in to the browser.
|
||||
* This class implements the HTML5 database support for Android 1.X devices.
|
||||
* It is not used for Android 2.X, since HTML5 database is built in to the browser.
|
||||
*/
|
||||
public class Storage extends Plugin {
|
||||
|
||||
// Data Definition Language
|
||||
private static final String ALTER = "alter";
|
||||
private static final String CREATE = "create";
|
||||
private static final String DROP = "drop";
|
||||
private static final String TRUNCATE = "truncate";
|
||||
|
||||
SQLiteDatabase myDb = null; // Database object
|
||||
String path = null; // Database path
|
||||
String dbName = null; // Database name
|
||||
|
||||
SQLiteDatabase myDb = null; // Database object
|
||||
String path = null; // Database path
|
||||
String dbName = null; // Database name
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@@ -40,37 +34,29 @@ public class Storage extends Plugin {
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
* @param action
|
||||
* The action to execute.
|
||||
* @param args
|
||||
* JSONArry of arguments for the plugin.
|
||||
* @param callbackId
|
||||
* The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
* @param action The action to execute.
|
||||
* @param args JSONArry of arguments for the plugin.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
// TODO: Do we want to allow a user to do this, since they could get
|
||||
// to other app databases?
|
||||
// TODO: Do we want to allow a user to do this, since they could get to other app databases?
|
||||
if (action.equals("setStorage")) {
|
||||
this.setStorage(args.getString(0));
|
||||
} else if (action.equals("openDatabase")) {
|
||||
this.openDatabase(args.getString(0), args.getString(1),
|
||||
args.getString(2), args.getLong(3));
|
||||
} else if (action.equals("executeSql")) {
|
||||
String[] s = null;
|
||||
if (args.isNull(1)) {
|
||||
s = new String[0];
|
||||
} else {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
s = new String[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
s[i] = a.getString(i);
|
||||
}
|
||||
}
|
||||
else if (action.equals("openDatabase")) {
|
||||
this.openDatabase(args.getString(0), args.getString(1), args.getString(2), args.getLong(3));
|
||||
}
|
||||
else if (action.equals("executeSql")) {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
String[] s = new String[len];
|
||||
for (int i=0; i<len; i++) {
|
||||
s[i] = a.getString(i);
|
||||
}
|
||||
this.executeSql(args.getString(0), s, args.getString(2));
|
||||
}
|
||||
@@ -81,17 +67,15 @@ public class Storage extends Plugin {
|
||||
}
|
||||
|
||||
/**
|
||||
* Identifies if action to be executed returns a value and should be run
|
||||
* synchronously.
|
||||
* Identifies if action to be executed returns a value and should be run synchronously.
|
||||
*
|
||||
* @param action
|
||||
* The action to execute
|
||||
* @return T=returns value
|
||||
* @param action The action to execute
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clean up and close database.
|
||||
*/
|
||||
@@ -103,40 +87,33 @@ public class Storage extends Plugin {
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
// --------------------------------------------------------------------------
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set the application package for the database. Each application saves its
|
||||
* database files in a directory with the application package as part of the
|
||||
* file name.
|
||||
* Set the application package for the database. Each application saves its
|
||||
* database files in a directory with the application package as part of the file name.
|
||||
*
|
||||
* For example, application "com.phonegap.demo.Demo" would save its database
|
||||
* files in "/data/data/com.phonegap.demo/databases/" directory.
|
||||
*
|
||||
* @param appPackage
|
||||
* The application package.
|
||||
* @param appPackage The application package.
|
||||
*/
|
||||
public void setStorage(String appPackage) {
|
||||
this.path = "/data/data/" + appPackage + "/databases/";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Open database.
|
||||
*
|
||||
* @param db
|
||||
* The name of the database
|
||||
* @param version
|
||||
* The version
|
||||
* @param display_name
|
||||
* The display name
|
||||
* @param size
|
||||
* The size in bytes
|
||||
* @param db The name of the database
|
||||
* @param version The version
|
||||
* @param display_name The display name
|
||||
* @param size The size in bytes
|
||||
*/
|
||||
public void openDatabase(String db, String version, String display_name,
|
||||
long size) {
|
||||
|
||||
public void openDatabase(String db, String version, String display_name, long size) {
|
||||
|
||||
// If database is open, then close it
|
||||
if (this.myDb != null) {
|
||||
this.myDb.close();
|
||||
@@ -144,36 +121,27 @@ public class Storage extends Plugin {
|
||||
|
||||
// If no database path, generate from application package
|
||||
if (this.path == null) {
|
||||
Package pack = this.ctx.getClass().getPackage();
|
||||
String appPackage = pack.getName();
|
||||
this.setStorage(appPackage);
|
||||
Package pack = this.ctx.getClass().getPackage();
|
||||
String appPackage = pack.getName();
|
||||
this.setStorage(appPackage);
|
||||
}
|
||||
|
||||
|
||||
this.dbName = this.path + db + ".db";
|
||||
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute SQL statement.
|
||||
*
|
||||
* @param query
|
||||
* The SQL query
|
||||
* @param params
|
||||
* Parameters for the query
|
||||
* @param tx_id
|
||||
* Transaction id
|
||||
* @param query The SQL query
|
||||
* @param params Parameters for the query
|
||||
* @param tx_id Transaction id
|
||||
*/
|
||||
public void executeSql(String query, String[] params, String tx_id) {
|
||||
try {
|
||||
if (isDDL(query)) {
|
||||
this.myDb.execSQL(query);
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', '');");
|
||||
}
|
||||
else {
|
||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||
this.processResults(myCursor, tx_id);
|
||||
myCursor.close();
|
||||
}
|
||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||
this.processResults(myCursor, tx_id);
|
||||
myCursor.close();
|
||||
}
|
||||
catch (SQLiteException ex) {
|
||||
ex.printStackTrace();
|
||||
@@ -183,40 +151,24 @@ public class Storage extends Plugin {
|
||||
this.sendJavascript("droiddb.fail('" + ex.getMessage() + "','" + tx_id + "');");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see the the query is a Data Definintion command
|
||||
*
|
||||
* @param query to be executed
|
||||
* @return true if it is a DDL command, false otherwise
|
||||
*/
|
||||
private boolean isDDL(String query) {
|
||||
String cmd = query.toLowerCase();
|
||||
if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process query results.
|
||||
*
|
||||
* @param cur
|
||||
* Cursor into query results
|
||||
* @param tx_id
|
||||
* Transaction id
|
||||
* @param cur Cursor into query results
|
||||
* @param tx_id Transaction id
|
||||
*/
|
||||
public void processResults(Cursor cur, String tx_id) {
|
||||
|
||||
|
||||
String result = "[]";
|
||||
// If query result has rows
|
||||
|
||||
|
||||
if (cur.moveToFirst()) {
|
||||
JSONArray fullresult = new JSONArray();
|
||||
String key = "";
|
||||
String value = "";
|
||||
int colCount = cur.getColumnCount();
|
||||
|
||||
|
||||
// Build up JSON result object for each row
|
||||
do {
|
||||
JSONObject row = new JSONObject();
|
||||
@@ -227,20 +179,19 @@ public class Storage extends Plugin {
|
||||
row.put(key, value);
|
||||
}
|
||||
fullresult.put(row);
|
||||
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
} while (cur.moveToNext());
|
||||
|
||||
|
||||
result = fullresult.toString();
|
||||
}
|
||||
|
||||
|
||||
// Let JavaScript know that there are no more rows
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', " + result
|
||||
+ ");");
|
||||
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', "+result+");");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@ class Classic
|
||||
@android_sdk_path, @name, @pkg, @www, @path = a
|
||||
build
|
||||
end
|
||||
|
||||
|
||||
def build
|
||||
setup
|
||||
clobber
|
||||
@@ -16,12 +16,12 @@ class Classic
|
||||
copy_libs
|
||||
add_name_to_strings
|
||||
write_java
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def setup
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__).gsub(/lib$/,''))
|
||||
@framework_dir = File.join(@android_dir, "framework")
|
||||
@icon = File.join(@www, 'icon.png') unless !@icon.nil? && File.exists?(@icon)
|
||||
@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,
|
||||
@@ -31,14 +31,14 @@ class Classic
|
||||
@app_js_dir = ''
|
||||
@content = 'index.html'
|
||||
end
|
||||
|
||||
|
||||
# replaces @path with new android project
|
||||
def clobber
|
||||
FileUtils.rm_r(@path) if File.exists? @path
|
||||
FileUtils.mkdir_p @path
|
||||
end
|
||||
|
||||
# removes local.properties and recreates based on android_sdk_path
|
||||
|
||||
# removes local.properties and recreates based on android_sdk_path
|
||||
# then generates framework/phonegap.jar
|
||||
def build_jar
|
||||
%w(local.properties phonegap.js phonegap.jar).each do |f|
|
||||
@@ -46,7 +46,7 @@ class Classic
|
||||
end
|
||||
open(File.join(@framework_dir, "local.properties"), 'w') do |f|
|
||||
f.puts "sdk.dir=#{ @android_sdk_path }"
|
||||
end
|
||||
end
|
||||
Dir.chdir(@framework_dir)
|
||||
`ant jar`
|
||||
Dir.chdir(@android_dir)
|
||||
@@ -55,9 +55,9 @@ class Classic
|
||||
# runs android create project
|
||||
# TODO need to allow more flexible SDK targetting via config.xml
|
||||
def create_android
|
||||
IO.popen("android list targets") { |f|
|
||||
IO.popen("android list targets") { |f|
|
||||
targets = f.readlines(nil)[0].scan(/id\:.*$/)
|
||||
if (targets.length > 0)
|
||||
if (targets.length > 0)
|
||||
target_id = targets.last.match(/\d+/).to_a.first
|
||||
`android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }`
|
||||
else
|
||||
@@ -66,7 +66,7 @@ class Classic
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
# copies the project/www folder into tmp/android/www
|
||||
def include_www
|
||||
FileUtils.mkdir_p File.join(@path, "assets", "www")
|
||||
@@ -127,7 +127,7 @@ class Classic
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
|
||||
|
||||
# puts app name in strings
|
||||
def add_name_to_strings
|
||||
x = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
@@ -138,8 +138,8 @@ class Classic
|
||||
"
|
||||
open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f|
|
||||
f.puts x.gsub(' ','')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# create java source file
|
||||
def write_java
|
||||
@@ -164,7 +164,7 @@ class Classic
|
||||
FileUtils.mkdir_p(code_dir)
|
||||
open(File.join(code_dir, "#{ @name }.java"),'w') { |f| f.puts j }
|
||||
end
|
||||
|
||||
|
||||
# friendly output for now
|
||||
def msg
|
||||
puts "Created #{ @path }"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Create
|
||||
#
|
||||
#
|
||||
# Generates an Android project from a valid WWW directory and puts it in ../[PROJECT NAME]_android
|
||||
#
|
||||
class Create < Classic
|
||||
@@ -8,35 +8,35 @@ class Create < Classic
|
||||
read_config
|
||||
build
|
||||
end
|
||||
|
||||
|
||||
def guess_paths(path)
|
||||
# if no path is supplied uses current directory for project
|
||||
path = FileUtils.pwd if path.nil?
|
||||
|
||||
|
||||
# if a www is found use it for the project
|
||||
path = File.join(path, 'www') if File.exists? File.join(path, 'www')
|
||||
|
||||
|
||||
# defaults
|
||||
@name = path.split("/").last.gsub('-','').gsub(' ','') # no dashses nor spaces
|
||||
@path = File.join(path, '..', "#{ @name }_android")
|
||||
@www = path
|
||||
@pkg = "com.phonegap.#{ @name }"
|
||||
@www = path
|
||||
@pkg = "com.phonegap.#{ @name }"
|
||||
@android_sdk_path = Dir.getwd[0,1] != "/" ? `android-sdk-path.bat android.bat`.gsub('\\tools','').gsub('\\', '\\\\\\\\') : `which android`.gsub(/\/tools\/android$/,'').chomp
|
||||
@android_dir = File.expand_path(File.dirname(__FILE__).gsub('lib',''))
|
||||
@framework_dir = File.join(@android_dir, "framework")
|
||||
@icon = File.join(@www, 'icon.png')
|
||||
@app_js_dir = ''
|
||||
@content = 'index.html'
|
||||
|
||||
|
||||
# stop executation on errors
|
||||
raise "Expected index.html in the following folder #{ path }.\nThe path is expected to be the directory droidgap create is run from or specified as a command line arg like droidgap create my_path." unless File.exists? File.join(path, 'index.html')
|
||||
raise "Expected index.html in the following folder #{ path }.\nThe path is expected to be the directory droidgap create is run from or specified as a command line arg like droidgap create my_path." unless File.exists? File.join(path, 'index.html')
|
||||
raise 'Could not find android in your PATH!' if @android_sdk_path.empty?
|
||||
end
|
||||
|
||||
# reads in a config.xml file
|
||||
def read_config
|
||||
config_file = File.join(@www, 'config.xml')
|
||||
|
||||
|
||||
if File.exists?(config_file)
|
||||
require 'rexml/document'
|
||||
f = File.new config_file
|
||||
@@ -47,9 +47,9 @@ class Create < Classic
|
||||
@config[:icons] = {}
|
||||
defaultIconSize = 0
|
||||
doc.root.elements.each do |n|
|
||||
@config[:name] = n.text.gsub('-','').gsub(' ','') if n.name == 'name'
|
||||
@config[:description] = n.text if n.name == 'description'
|
||||
@config[:content] = n.attributes["src"] if n.name == 'content'
|
||||
@config[:name] = n.text.gsub('-','').gsub(' ','') if n.name == 'name'
|
||||
@config[:description] = n.text if n.name == 'description'
|
||||
@config[:content] = n.attributes["src"] if n.name == 'content'
|
||||
if n.name == 'icon'
|
||||
if n.attributes["width"] == '72' && n.attributes["height"] == '72'
|
||||
@config[:icons]["drawable-hdpi".to_sym] = n.attributes["src"]
|
||||
@@ -74,12 +74,12 @@ class Create < Classic
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
if n.name == "preference" && n.attributes["name"] == 'javascript_folder'
|
||||
@config[:js_dir] = n.attributes["value"]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# extract android specific stuff
|
||||
@config[:versionCode] = doc.elements["//android:versionCode"] ? doc.elements["//android:versionCode"].text : 3
|
||||
@config[:minSdkVersion] = doc.elements["//android:minSdkVersion"] ? doc.elements["//android:minSdkVersion"].text : 1
|
||||
@@ -92,6 +92,6 @@ class Create < Classic
|
||||
@app_js_dir = @config[:js_dir] ? @config[:js_dir] : ''
|
||||
# sets the start page
|
||||
@content = @config[:content] ? @config[:content] : 'index.html'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user