mirror of
https://github.com/apache/cordova-android.git
synced 2025-02-20 23:56:20 +08:00
Merge github.com:phonegap/phonegap-android
This commit is contained in:
commit
c067299ace
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,7 +1,11 @@
|
||||
.DS_Store
|
||||
default.properties
|
||||
gen
|
||||
assets/www/phonegap.js
|
||||
local.properties
|
||||
framework/phonegap.jar
|
||||
framework/phonegap-*.jar
|
||||
framework/bin
|
||||
framework/assets/www/.DS_Store
|
||||
framework/assets/www/phonegap-*.js
|
||||
.DS_Store
|
||||
|
@ -5,8 +5,8 @@
|
||||
<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.4.min.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0rc2.js"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="main.js"></script>
|
||||
|
||||
</head>
|
||||
<body onload="init();" id="stage" class="theme">
|
||||
@ -31,8 +31,8 @@
|
||||
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
|
||||
<a href="#" class="btn large" onclick="get_contacts();">Get Phone's Contacts</a>
|
||||
<a href="#" class="btn large" onclick="check_network();">Check Network</a>
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<div id="viewport" class="viewport" style="display: none;">
|
||||
<img style="width:60px;height:60px" id="test_img" src="" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -88,16 +88,6 @@ function close() {
|
||||
viewport.style.display = "none";
|
||||
}
|
||||
|
||||
// This is just to do this.
|
||||
function readFile() {
|
||||
navigator.file.read('/sdcard/phonegap.txt', fail, fail);
|
||||
}
|
||||
|
||||
function writeFile() {
|
||||
navigator.file.write('foo.txt', "This is a test of writing to a file",
|
||||
fail, fail);
|
||||
}
|
||||
|
||||
function contacts_success(contacts) {
|
||||
alert(contacts.length
|
||||
+ ' contacts returned.'
|
||||
@ -109,27 +99,24 @@ function get_contacts() {
|
||||
var obj = new ContactFindOptions();
|
||||
obj.filter = "";
|
||||
obj.multiple = true;
|
||||
obj.limit = 5;
|
||||
navigator.service.contacts.find(
|
||||
[ "displayName", "name" ], contacts_success,
|
||||
fail, obj);
|
||||
}
|
||||
|
||||
var networkReachableCallback = function(reachability) {
|
||||
// There is no consistency on the format of reachability
|
||||
var networkState = reachability.code || reachability;
|
||||
|
||||
var currentState = {};
|
||||
currentState[NetworkStatus.NOT_REACHABLE] = 'No network connection';
|
||||
currentState[NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK] = 'Carrier data connection';
|
||||
currentState[NetworkStatus.REACHABLE_VIA_WIFI_NETWORK] = 'WiFi connection';
|
||||
|
||||
confirm("Connection type:\n" + currentState[networkState]);
|
||||
};
|
||||
|
||||
function check_network() {
|
||||
navigator.network.isReachable("www.mobiledevelopersolutions.com",
|
||||
networkReachableCallback, {});
|
||||
var networkState = navigator.network.connection.type;
|
||||
|
||||
var states = {};
|
||||
states[Connection.UNKNOWN] = 'Unknown connection';
|
||||
states[Connection.ETHERNET] = 'Ethernet connection';
|
||||
states[Connection.WIFI] = 'WiFi connection';
|
||||
states[Connection.CELL_2G] = 'Cell 2G connection';
|
||||
states[Connection.CELL_3G] = 'Cell 3G connection';
|
||||
states[Connection.CELL_4G] = 'Cell 4G connection';
|
||||
states[Connection.NONE] = 'No network connection';
|
||||
|
||||
confirm('Connection type:\n ' + states[networkState]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
@ -5,34 +5,45 @@
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true"
|
||||
android:xlargeScreens="true"
|
||||
android:resizeable="true"
|
||||
android:anyDensity="true"
|
||||
/>
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" />
|
||||
|
||||
<application android:icon="@drawable/icon" android:label="@string/app_name"
|
||||
android:debuggable="true">
|
||||
<activity android:name=".StandAlone"
|
||||
<activity android:name=".StandAlone" android:windowSoftInputMode="adjustPan"
|
||||
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name="com.phonegap.DroidGap" android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
<intent-filter>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
|
||||
<uses-sdk android:minSdkVersion="2" />
|
||||
</manifest>
|
||||
|
@ -3,21 +3,25 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
function Acceleration(x, y, z) {
|
||||
if (!PhoneGap.hasResource("accelerometer")) {
|
||||
PhoneGap.addResource("accelerometer");
|
||||
|
||||
/** @constructor */
|
||||
var Acceleration = function(x, y, z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.timestamp = new Date().getTime();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class provides access to device accelerometer data.
|
||||
* @constructor
|
||||
*/
|
||||
function Accelerometer() {
|
||||
var Accelerometer = function() {
|
||||
|
||||
/**
|
||||
* The last known acceleration. type=Acceleration()
|
||||
@ -28,7 +32,7 @@ function Accelerometer() {
|
||||
* List of accelerometer watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
}
|
||||
};
|
||||
|
||||
Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
@ -119,3 +123,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.accelerometer = new Accelerometer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -6,10 +6,15 @@
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("app")) {
|
||||
PhoneGap.addResource("app");
|
||||
(function() {
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @constructor
|
||||
*/
|
||||
function App() {}
|
||||
var App = function() {};
|
||||
|
||||
/**
|
||||
* Clear the resource cache.
|
||||
@ -20,7 +25,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
|
||||
@ -30,7 +35,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});
|
||||
@ -54,23 +59,13 @@ App.prototype.clearHistory = function() {
|
||||
PhoneGap.exec(null, null, "App", "clearHistory", []);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
App.prototype.addService = function(serviceType, className) {
|
||||
PhoneGap.exec(null, null, "App", "addService", [serviceType, className]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
*
|
||||
*
|
||||
* Note: The user should not have to call this method. Instead, when the user
|
||||
* registers for the "backbutton" event, this is automatically done.
|
||||
*
|
||||
*
|
||||
* @param override T=override, F=cancel override
|
||||
*/
|
||||
App.prototype.overrideBackbutton = function(override) {
|
||||
@ -85,5 +80,7 @@ App.prototype.exitApp = function() {
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.app = window.app = new App();
|
||||
navigator.app = new App();
|
||||
});
|
||||
}());
|
||||
}
|
||||
|
@ -3,15 +3,18 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("camera")) {
|
||||
PhoneGap.addResource("camera");
|
||||
|
||||
/**
|
||||
* This class provides access to the device camera.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
Camera = function() {
|
||||
var Camera = function() {
|
||||
this.successCallback = null;
|
||||
this.errorCallback = null;
|
||||
this.options = null;
|
||||
@ -91,3 +94,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.camera = new Camera();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
191
framework/assets/js/capture.js
Normal file
191
framework/assets/js/capture.js
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("capture")) {
|
||||
PhoneGap.addResource("capture");
|
||||
|
||||
/**
|
||||
* Represents a single file.
|
||||
*
|
||||
* name {DOMString} name of the file, without path information
|
||||
* fullPath {DOMString} the full path of the file, including the name
|
||||
* type {DOMString} mime type
|
||||
* lastModifiedDate {Date} last modified date
|
||||
* size {Number} size of the file in bytes
|
||||
*/
|
||||
var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
|
||||
this.name = name || null;
|
||||
this.fullPath = fullPath || null;
|
||||
this.type = type || null;
|
||||
this.lastModifiedDate = lastModifiedDate || null;
|
||||
this.size = size || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch device camera application for recording video(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
*/
|
||||
MediaFile.prototype.getFormatData = function(successCallback, errorCallback){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
|
||||
};
|
||||
|
||||
/**
|
||||
* MediaFileData encapsulates format information of a media file.
|
||||
*
|
||||
* @param {DOMString} codecs
|
||||
* @param {long} bitrate
|
||||
* @param {long} height
|
||||
* @param {long} width
|
||||
* @param {float} duration
|
||||
*/
|
||||
var MediaFileData = function(codecs, bitrate, height, width, duration){
|
||||
this.codecs = codecs || null;
|
||||
this.bitrate = bitrate || 0;
|
||||
this.height = height || 0;
|
||||
this.width = width || 0;
|
||||
this.duration = duration || 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* The CaptureError interface encapsulates all errors in the Capture API.
|
||||
*/
|
||||
var CaptureError = function(){
|
||||
this.code = null;
|
||||
};
|
||||
|
||||
// Capture error codes
|
||||
CaptureError.CAPTURE_INTERNAL_ERR = 0;
|
||||
CaptureError.CAPTURE_APPLICATION_BUSY = 1;
|
||||
CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
|
||||
CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
|
||||
CaptureError.CAPTURE_NOT_SUPPORTED = 20;
|
||||
|
||||
/**
|
||||
* The Capture interface exposes an interface to the camera and microphone of the hosting device.
|
||||
*/
|
||||
var Capture = function(){
|
||||
this.supportedAudioModes = [];
|
||||
this.supportedImageModes = [];
|
||||
this.supportedVideoModes = [];
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch audio recorder application for recording audio clip(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureAudioOptions} options
|
||||
*/
|
||||
Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureAudio", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch camera application for taking image(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureImageOptions} options
|
||||
*/
|
||||
Capture.prototype.captureImage = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureImage", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch camera application for taking image(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureImageOptions} options
|
||||
*/
|
||||
Capture.prototype._castMediaFile = function(pluginResult){
|
||||
var mediaFiles = [];
|
||||
var i;
|
||||
for (i = 0; i < pluginResult.message.length; i++) {
|
||||
var mediaFile = new MediaFile();
|
||||
mediaFile.name = pluginResult.message[i].name;
|
||||
mediaFile.fullPath = pluginResult.message[i].fullPath;
|
||||
mediaFile.type = pluginResult.message[i].type;
|
||||
mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
|
||||
mediaFile.size = pluginResult.message[i].size;
|
||||
mediaFiles.push(mediaFile);
|
||||
}
|
||||
pluginResult.message = mediaFiles;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* Launch device camera application for recording video(s).
|
||||
*
|
||||
* @param {Function} successCB
|
||||
* @param {Function} errorCB
|
||||
* @param {CaptureVideoOptions} options
|
||||
*/
|
||||
Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
|
||||
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureVideo", [options]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates a set of parameters that the capture device supports.
|
||||
*/
|
||||
var ConfigurationData = function(){
|
||||
// The ASCII-encoded string in lower case representing the media type.
|
||||
this.type = null;
|
||||
// The height attribute represents height of the image or video in pixels.
|
||||
// In the case of a sound clip this attribute has value 0.
|
||||
this.height = 0;
|
||||
// The width attribute represents width of the image or video in pixels.
|
||||
// In the case of a sound clip this attribute has value 0
|
||||
this.width = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all image capture operation configuration options.
|
||||
*/
|
||||
var CaptureImageOptions = function(){
|
||||
// Upper limit of images user can take. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// The selected image mode. Must match with one of the elements in supportedImageModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all video capture operation configuration options.
|
||||
*/
|
||||
var CaptureVideoOptions = function(){
|
||||
// Upper limit of videos user can record. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// Maximum duration of a single video clip in seconds.
|
||||
this.duration = 0;
|
||||
// The selected video mode. Must match with one of the elements in supportedVideoModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Encapsulates all audio capture operation configuration options.
|
||||
*/
|
||||
var CaptureAudioOptions = function(){
|
||||
// Upper limit of sound clips user can record. Value must be equal or greater than 1.
|
||||
this.limit = 1;
|
||||
// Maximum duration of a single sound clip in seconds.
|
||||
this.duration = 0;
|
||||
// The selected audio mode. Must match with one of the elements in supportedAudioModes array.
|
||||
this.mode = null;
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function(){
|
||||
if (typeof navigator.device === "undefined") {
|
||||
navigator.device = window.device = new Device();
|
||||
}
|
||||
if (typeof navigator.device.capture === "undefined") {
|
||||
navigator.device.capture = window.device.capture = new Capture();
|
||||
}
|
||||
});
|
||||
}
|
@ -3,14 +3,17 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("compass")) {
|
||||
PhoneGap.addResource("compass");
|
||||
|
||||
/**
|
||||
* This class provides access to device Compass data.
|
||||
* @constructor
|
||||
*/
|
||||
function Compass() {
|
||||
var Compass = function() {
|
||||
/**
|
||||
* The last known Compass position.
|
||||
*/
|
||||
@ -20,7 +23,7 @@ function Compass() {
|
||||
* List of compass watch timers
|
||||
*/
|
||||
this.timers = {};
|
||||
}
|
||||
};
|
||||
|
||||
Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
|
||||
|
||||
@ -113,3 +116,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.compass = new Compass();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3,31 +3,32 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
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 {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 {Array.<ContactField>} phoneNumbers array of phone numbers
|
||||
* @param {Array.<ContactField>} emails array of email addresses
|
||||
* @param {Array.<ContactAddress>} addresses array of addresses
|
||||
* @param {Array.<ContactField>} ims instant messaging user ids
|
||||
* @param {Array.<ContactOrganization>} organizations
|
||||
* @param {DOMString} birthday contact's birthday
|
||||
* @param {DOMString} gender contact's gender
|
||||
* @param {DOMString} note user notes about contact
|
||||
* @param {ContactField[]} photos
|
||||
* @param {ContactField[]} categories
|
||||
* @param {ContactField[]} urls contact's web sites
|
||||
* @param {DOMString} timezone the contacts time zone
|
||||
* @param {Array.<ContactField>} photos
|
||||
* @param {Array.<ContactField>} categories
|
||||
* @param {Array.<ContactField>} urls contact's web sites
|
||||
*/
|
||||
var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
|
||||
ims, organizations, revision, birthday, gender, note, photos, categories, urls, timezone) {
|
||||
ims, organizations, birthday, note, photos, categories, urls) {
|
||||
this.id = id || null;
|
||||
this.rawId = null;
|
||||
this.displayName = displayName || null;
|
||||
@ -38,19 +39,17 @@ var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, a
|
||||
this.addresses = addresses || null; // ContactAddress[]
|
||||
this.ims = ims || null; // ContactField[]
|
||||
this.organizations = organizations || null; // ContactOrganization[]
|
||||
this.revision = revision || null;
|
||||
this.birthday = birthday || null;
|
||||
this.gender = gender || null;
|
||||
this.note = note || null;
|
||||
this.photos = photos || null; // ContactField[]
|
||||
this.categories = categories || null; // ContactField[]
|
||||
this.urls = urls || null; // ContactField[]
|
||||
this.timezone = timezone || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* ContactError.
|
||||
* An error code assigned by an implementation when an error has occurred
|
||||
* An error code assigned by an implementation when an error has occurreds
|
||||
* @constructor
|
||||
*/
|
||||
var ContactError = function() {
|
||||
this.code=null;
|
||||
@ -61,11 +60,10 @@ var ContactError = function() {
|
||||
*/
|
||||
ContactError.UNKNOWN_ERROR = 0;
|
||||
ContactError.INVALID_ARGUMENT_ERROR = 1;
|
||||
ContactError.NOT_FOUND_ERROR = 2;
|
||||
ContactError.TIMEOUT_ERROR = 3;
|
||||
ContactError.PENDING_OPERATION_ERROR = 4;
|
||||
ContactError.IO_ERROR = 5;
|
||||
ContactError.NOT_SUPPORTED_ERROR = 6;
|
||||
ContactError.TIMEOUT_ERROR = 2;
|
||||
ContactError.PENDING_OPERATION_ERROR = 3;
|
||||
ContactError.IO_ERROR = 4;
|
||||
ContactError.NOT_SUPPORTED_ERROR = 5;
|
||||
ContactError.PERMISSION_DENIED_ERROR = 20;
|
||||
|
||||
/**
|
||||
@ -76,7 +74,7 @@ ContactError.PERMISSION_DENIED_ERROR = 20;
|
||||
Contact.prototype.remove = function(successCB, errorCB) {
|
||||
if (this.id === null) {
|
||||
var errorObj = new ContactError();
|
||||
errorObj.code = ContactError.NOT_FOUND_ERROR;
|
||||
errorObj.code = ContactError.UNKNOWN_ERROR;
|
||||
errorCB(errorObj);
|
||||
}
|
||||
else {
|
||||
@ -149,6 +147,7 @@ Contact.prototype.save = function(successCB, errorCB) {
|
||||
|
||||
/**
|
||||
* Contact name.
|
||||
* @constructor
|
||||
* @param formatted
|
||||
* @param familyName
|
||||
* @param givenName
|
||||
@ -167,6 +166,7 @@ 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
|
||||
@ -181,6 +181,7 @@ 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
|
||||
@ -189,8 +190,10 @@ var ContactField = function(type, value, pref) {
|
||||
* @param postalCode
|
||||
* @param country
|
||||
*/
|
||||
var ContactAddress = function(formatted, streetAddress, locality, region, postalCode, country) {
|
||||
var ContactAddress = function(pref, type, formatted, streetAddress, locality, region, postalCode, country) {
|
||||
this.id = null;
|
||||
this.pref = pref || null;
|
||||
this.type = type || null;
|
||||
this.formatted = formatted || null;
|
||||
this.streetAddress = streetAddress || null;
|
||||
this.locality = locality || null;
|
||||
@ -201,6 +204,7 @@ 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
|
||||
@ -210,8 +214,10 @@ var ContactAddress = function(formatted, streetAddress, locality, region, postal
|
||||
* @param location
|
||||
* @param desc
|
||||
*/
|
||||
var ContactOrganization = function(name, dept, title) {
|
||||
var ContactOrganization = function(pref, type, name, dept, title) {
|
||||
this.id = null;
|
||||
this.pref = pref || null;
|
||||
this.type = type || null;
|
||||
this.name = name || null;
|
||||
this.department = dept || null;
|
||||
this.title = title || null;
|
||||
@ -219,6 +225,7 @@ var ContactOrganization = function(name, dept, title) {
|
||||
|
||||
/**
|
||||
* Represents a group of Contacts.
|
||||
* @constructor
|
||||
*/
|
||||
var Contacts = function() {
|
||||
this.inProgress = false;
|
||||
@ -233,7 +240,16 @@ var Contacts = function() {
|
||||
* @return array of Contacts matching search criteria
|
||||
*/
|
||||
Contacts.prototype.find = function(fields, successCB, errorCB, options) {
|
||||
PhoneGap.exec(successCB, errorCB, "Contacts", "search", [fields, options]);
|
||||
if (successCB === null) {
|
||||
throw new TypeError("You must specify a success callback for the find command.");
|
||||
}
|
||||
if (fields === null || fields === "undefined" || fields.length === "undefined" || fields.length <= 0) {
|
||||
if (typeof errorCB === "function") {
|
||||
errorCB({"code": ContactError.INVALID_ARGUMENT_ERROR});
|
||||
}
|
||||
} else {
|
||||
PhoneGap.exec(successCB, errorCB, "Contacts", "search", [fields, options]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -255,10 +271,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
|
||||
* navigator.service.contacts.find but before the find methods success call back.
|
||||
*
|
||||
* This function returns and array of contacts. It is required as we need to convert raw
|
||||
* JSON objects into concrete Contact objects. Currently this method is called after
|
||||
* navigator.contacts.find but before the find methods success call back.
|
||||
*
|
||||
* @param jsonArray an array of JSON Objects that need to be converted to Contact objects.
|
||||
* @returns an array of Contact objects
|
||||
*/
|
||||
@ -266,7 +282,7 @@ Contacts.prototype.cast = function(pluginResult) {
|
||||
var contacts = [];
|
||||
var i;
|
||||
for (i=0; i<pluginResult.message.length; i++) {
|
||||
contacts.push(navigator.service.contacts.create(pluginResult.message[i]));
|
||||
contacts.push(navigator.contacts.create(pluginResult.message[i]));
|
||||
}
|
||||
pluginResult.message = contacts;
|
||||
return pluginResult;
|
||||
@ -274,24 +290,21 @@ 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
|
||||
*/
|
||||
var ContactFindOptions = function(filter, multiple, updatedSince) {
|
||||
var ContactFindOptions = function(filter, multiple) {
|
||||
this.filter = filter || '';
|
||||
this.multiple = multiple || true;
|
||||
this.updatedSince = updatedSince || '';
|
||||
this.multiple = multiple || false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the contact interface into the browser.
|
||||
*/
|
||||
PhoneGap.addConstructor(function() {
|
||||
if(typeof navigator.service === "undefined") {
|
||||
navigator.service = {};
|
||||
}
|
||||
if(typeof navigator.service.contacts === "undefined") {
|
||||
navigator.service.contacts = new Contacts();
|
||||
if(typeof navigator.contacts === "undefined") {
|
||||
navigator.contacts = new Contacts();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3,11 +3,17 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
// TODO: Needs to be commented
|
||||
|
||||
if (!PhoneGap.hasResource("crypto")) {
|
||||
PhoneGap.addResource("crypto");
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var Crypto = function() {
|
||||
};
|
||||
|
||||
@ -34,4 +40,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.Crypto = new Crypto();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -6,12 +6,15 @@
|
||||
* 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
|
||||
*/
|
||||
function Device() {
|
||||
var Device = function() {
|
||||
this.available = PhoneGap.available;
|
||||
this.platform = null;
|
||||
this.version = null;
|
||||
@ -35,7 +38,7 @@ function Device() {
|
||||
console.log("Error initializing PhoneGap: " + e);
|
||||
alert("Error initializing PhoneGap: "+e);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get device info
|
||||
@ -69,7 +72,7 @@ Device.prototype.getInfo = function(successCallback, errorCallback) {
|
||||
*/
|
||||
Device.prototype.overrideBackButton = function() {
|
||||
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
|
||||
app.overrideBackbutton(true);
|
||||
navigator.app.overrideBackbutton(true);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -80,7 +83,7 @@ Device.prototype.overrideBackButton = function() {
|
||||
*/
|
||||
Device.prototype.resetBackButton = function() {
|
||||
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
|
||||
app.overrideBackbutton(false);
|
||||
navigator.app.overrideBackbutton(false);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -91,9 +94,12 @@ Device.prototype.resetBackButton = function() {
|
||||
*/
|
||||
Device.prototype.exitApp = function() {
|
||||
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
|
||||
app.exitApp();
|
||||
navigator.app.exitApp();
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
navigator.device = window.device = new Device();
|
||||
if (typeof navigator.device === "undefined") {
|
||||
navigator.device = window.device = new Device();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -3,45 +3,46 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class provides generic read and write access to the mobile device file system.
|
||||
* They are not used to read files from a server.
|
||||
*/
|
||||
if (!PhoneGap.hasResource("file")) {
|
||||
PhoneGap.addResource("file");
|
||||
|
||||
/**
|
||||
* This class provides some useful information about a file.
|
||||
* This is the fields returned when navigator.fileMgr.getFileProperties()
|
||||
* This is the fields returned when navigator.fileMgr.getFileProperties()
|
||||
* is called.
|
||||
* @constructor
|
||||
*/
|
||||
function FileProperties(filePath) {
|
||||
var FileProperties = function(filePath) {
|
||||
this.filePath = filePath;
|
||||
this.size = 0;
|
||||
this.lastModifiedDate = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @constructor
|
||||
* @param name {DOMString} name of the file, without path information
|
||||
* @param fullPath {DOMString} the full path of the file, including the name
|
||||
* @param type {DOMString} mime type
|
||||
* @param lastModifiedDate {Date} last modified date
|
||||
* @param size {Number} size of the file in bytes
|
||||
*/
|
||||
function File(name, fullPath, type, lastModifiedDate, size) {
|
||||
var File = 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;
|
||||
}
|
||||
};
|
||||
|
||||
function FileError() {
|
||||
/** @constructor */
|
||||
var FileError = function() {
|
||||
this.code = null;
|
||||
}
|
||||
};
|
||||
|
||||
// File error codes
|
||||
// Found in DOMException
|
||||
@ -64,8 +65,9 @@ FileError.PATH_EXISTS_ERR = 12;
|
||||
// File manager
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function FileMgr() {
|
||||
}
|
||||
/** @constructor */
|
||||
var FileMgr = function() {
|
||||
};
|
||||
|
||||
FileMgr.prototype.getFileProperties = function(filePath) {
|
||||
return PhoneGap.exec(null, null, "File", "getFileProperties", [filePath]);
|
||||
@ -90,10 +92,6 @@ FileMgr.prototype.getFreeDiskSpace = function(successCallback, errorCallback) {
|
||||
return PhoneGap.exec(successCallback, errorCallback, "File", "getFreeDiskSpace", []);
|
||||
};
|
||||
|
||||
FileMgr.prototype.writeAsText = function(fileName, data, append, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "writeAsText", [fileName, data, append]);
|
||||
};
|
||||
|
||||
FileMgr.prototype.write = function(fileName, data, position, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "write", [fileName, data, position]);
|
||||
};
|
||||
@ -128,8 +126,9 @@ PhoneGap.addConstructor(function() {
|
||||
* For Android:
|
||||
* The root directory is the root of the file system.
|
||||
* To read from the SD card, the file name is "sdcard/my_file.txt"
|
||||
* @constructor
|
||||
*/
|
||||
function FileReader() {
|
||||
var FileReader = function() {
|
||||
this.fileName = "";
|
||||
|
||||
this.readyState = 0;
|
||||
@ -147,7 +146,7 @@ function FileReader() {
|
||||
this.onerror = null; // When the read has failed (see errors).
|
||||
this.onloadend = null; // When the request has completed (either in success or failure).
|
||||
this.onabort = null; // When the read has been aborted. For instance, by invoking the abort() method.
|
||||
}
|
||||
};
|
||||
|
||||
// States
|
||||
FileReader.EMPTY = 0;
|
||||
@ -166,7 +165,7 @@ FileReader.prototype.abort = function() {
|
||||
var error = new FileError();
|
||||
error.code = error.ABORT_ERR;
|
||||
this.error = error;
|
||||
|
||||
|
||||
// If error callback
|
||||
if (typeof this.onerror === "function") {
|
||||
this.onerror({"type":"error", "target":this});
|
||||
@ -225,6 +224,7 @@ FileReader.prototype.readAsText = function(file, encoding) {
|
||||
|
||||
// If onload callback
|
||||
if (typeof me.onload === "function") {
|
||||
me.onload({"type":"load", "target":me});
|
||||
}
|
||||
|
||||
// DONE state
|
||||
@ -245,7 +245,7 @@ FileReader.prototype.readAsText = function(file, encoding) {
|
||||
}
|
||||
|
||||
// Save error
|
||||
me.error = e;
|
||||
me.error = e;
|
||||
|
||||
// If onerror callback
|
||||
if (typeof me.onerror === "function") {
|
||||
@ -375,11 +375,12 @@ FileReader.prototype.readAsArrayBuffer = function(file) {
|
||||
* For Android:
|
||||
* The root directory is the root of the file system.
|
||||
* To write to the SD card, the file name is "sdcard/my_file.txt"
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* @param file {File} File object containing file properties
|
||||
* @param append if true write to the end of the file, otherwise overwrite the file
|
||||
*/
|
||||
function FileWriter(file) {
|
||||
var FileWriter = function(file) {
|
||||
this.fileName = "";
|
||||
this.length = 0;
|
||||
if (file) {
|
||||
@ -403,7 +404,7 @@ function FileWriter(file) {
|
||||
this.onwriteend = null; // When the request has completed (either in success or failure).
|
||||
this.onabort = null; // When the write has been aborted. For instance, by invoking the abort() method.
|
||||
this.onerror = null; // When the write has failed (see errors).
|
||||
}
|
||||
};
|
||||
|
||||
// States
|
||||
FileWriter.INIT = 0;
|
||||
@ -417,13 +418,13 @@ FileWriter.prototype.abort = function() {
|
||||
// check for invalid state
|
||||
if (this.readyState === FileWriter.DONE || this.readyState === FileWriter.INIT) {
|
||||
throw FileError.INVALID_STATE_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
// set error
|
||||
var error = new FileError(), evt;
|
||||
error.code = error.ABORT_ERR;
|
||||
this.error = error;
|
||||
|
||||
|
||||
// If error callback
|
||||
if (typeof this.onerror === "function") {
|
||||
this.onerror({"type":"error", "target":this});
|
||||
@ -432,7 +433,7 @@ FileWriter.prototype.abort = function() {
|
||||
if (typeof this.onabort === "function") {
|
||||
this.oneabort({"type":"abort", "target":this});
|
||||
}
|
||||
|
||||
|
||||
this.readyState = FileWriter.DONE;
|
||||
|
||||
// If write end callback
|
||||
@ -443,7 +444,7 @@ FileWriter.prototype.abort = function() {
|
||||
|
||||
/**
|
||||
* Writes data to the file
|
||||
*
|
||||
*
|
||||
* @param text to be written
|
||||
*/
|
||||
FileWriter.prototype.write = function(text) {
|
||||
@ -473,10 +474,10 @@ FileWriter.prototype.write = function(text) {
|
||||
return;
|
||||
}
|
||||
|
||||
// So if the user wants to keep appending to the file
|
||||
me.length = Math.max(me.length, me.position + r);
|
||||
// position always increases by bytes written because file would be extended
|
||||
me.position += r;
|
||||
// The length of the file is now where we are done writing.
|
||||
me.length = me.position;
|
||||
|
||||
// If onwrite callback
|
||||
if (typeof me.onwrite === "function") {
|
||||
@ -521,13 +522,13 @@ FileWriter.prototype.write = function(text) {
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Moves the file pointer to the location specified.
|
||||
*
|
||||
* If the offset is a negative number the position of the file
|
||||
* pointer is rewound. If the offset is greater than the file
|
||||
* size the position is set to the end of the file.
|
||||
*
|
||||
*
|
||||
* If the offset is a negative number the position of the file
|
||||
* pointer is rewound. If the offset is greater than the file
|
||||
* size the position is set to the end of the file.
|
||||
*
|
||||
* @param offset is the location to move the file pointer to.
|
||||
*/
|
||||
FileWriter.prototype.seek = function(offset) {
|
||||
@ -539,12 +540,12 @@ FileWriter.prototype.seek = function(offset) {
|
||||
if (!offset) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// See back from end of file.
|
||||
if (offset < 0) {
|
||||
this.position = Math.max(offset + this.length, 0);
|
||||
}
|
||||
// Offset is bigger then file size so set position
|
||||
// Offset is bigger then file size so set position
|
||||
// to the end of the file.
|
||||
else if (offset > this.length) {
|
||||
this.position = this.length;
|
||||
@ -553,12 +554,12 @@ FileWriter.prototype.seek = function(offset) {
|
||||
// to start writing.
|
||||
else {
|
||||
this.position = offset;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Truncates the file to the size specified.
|
||||
*
|
||||
*
|
||||
* @param size to chop the file at.
|
||||
*/
|
||||
FileWriter.prototype.truncate = function(size) {
|
||||
@ -633,169 +634,69 @@ FileWriter.prototype.truncate = function(size) {
|
||||
);
|
||||
};
|
||||
|
||||
function LocalFileSystem() {
|
||||
};
|
||||
|
||||
// File error codes
|
||||
LocalFileSystem.TEMPORARY = 0;
|
||||
LocalFileSystem.PERSISTENT = 1;
|
||||
LocalFileSystem.RESOURCE = 2;
|
||||
LocalFileSystem.APPLICATION = 3;
|
||||
|
||||
/**
|
||||
* Requests a filesystem in which to store application data.
|
||||
*
|
||||
* @param {int} type of file system being requested
|
||||
* @param {Function} successCallback is called with the new FileSystem
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {
|
||||
if (type < 0 || type > 3) {
|
||||
if (typeof errorCallback == "function") {
|
||||
errorCallback({
|
||||
"code": FileError.SYNTAX_ERR
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "requestFileSystem", [type, size]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DOMString} uri referring to a local file in a filesystem
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "resolveLocalFileSystemURI", [uri]);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
|
||||
* @returns an entry
|
||||
*/
|
||||
LocalFileSystem.prototype._castFS = function(pluginResult) {
|
||||
var entry = null;
|
||||
entry = new DirectoryEntry();
|
||||
entry.isDirectory = pluginResult.message.root.isDirectory;
|
||||
entry.isFile = pluginResult.message.root.isFile;
|
||||
entry.name = pluginResult.message.root.name;
|
||||
entry.fullPath = pluginResult.message.root.fullPath;
|
||||
pluginResult.message.root = entry;
|
||||
return pluginResult;
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype._castEntry = function(pluginResult) {
|
||||
var entry = null;
|
||||
if (pluginResult.message.isDirectory) {
|
||||
console.log("This is a dir");
|
||||
entry = new DirectoryEntry();
|
||||
}
|
||||
else if (pluginResult.message.isFile) {
|
||||
console.log("This is a file");
|
||||
entry = new FileEntry();
|
||||
}
|
||||
entry.isDirectory = pluginResult.message.isDirectory;
|
||||
entry.isFile = pluginResult.message.isFile;
|
||||
entry.name = pluginResult.message.name;
|
||||
entry.fullPath = pluginResult.message.fullPath;
|
||||
pluginResult.message = entry;
|
||||
return pluginResult;
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype._castEntries = function(pluginResult) {
|
||||
var entries = pluginResult.message;
|
||||
var retVal = [];
|
||||
for (i=0; i<entries.length; i++) {
|
||||
retVal.push(window.localFileSystem._createEntry(entries[i]));
|
||||
}
|
||||
pluginResult.message = retVal;
|
||||
return pluginResult;
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype._createEntry = function(castMe) {
|
||||
var entry = null;
|
||||
if (castMe.isDirectory) {
|
||||
console.log("This is a dir");
|
||||
entry = new DirectoryEntry();
|
||||
}
|
||||
else if (castMe.isFile) {
|
||||
console.log("This is a file");
|
||||
entry = new FileEntry();
|
||||
}
|
||||
entry.isDirectory = castMe.isDirectory;
|
||||
entry.isFile = castMe.isFile;
|
||||
entry.name = castMe.name;
|
||||
entry.fullPath = castMe.fullPath;
|
||||
return entry;
|
||||
|
||||
}
|
||||
|
||||
LocalFileSystem.prototype._castDate = function(pluginResult) {
|
||||
if (pluginResult.message.modificationTime) {
|
||||
var modTime = new Date(pluginResult.message.modificationTime);
|
||||
pluginResult.message.modificationTime = modTime;
|
||||
}
|
||||
else if (pluginResult.message.lastModifiedDate) {
|
||||
var file = new File();
|
||||
file.size = pluginResult.message.size;
|
||||
file.type = pluginResult.message.type;
|
||||
file.name = pluginResult.message.name;
|
||||
file.fullPath = pluginResult.message.fullPath;
|
||||
file.lastModifedDate = new Date(pluginResult.message.lastModifiedDate);
|
||||
pluginResult.message = file;
|
||||
}
|
||||
|
||||
return pluginResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information about the state of the file or directory
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* {Date} modificationTime (readonly)
|
||||
*/
|
||||
function Metadata() {
|
||||
var Metadata = function() {
|
||||
this.modificationTime=null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Supplies arguments to methods that lookup or create files and directories
|
||||
*
|
||||
* @param {boolean} create file or directory if it doesn't exist
|
||||
*
|
||||
* @constructor
|
||||
* @param {boolean} create file or directory if it doesn't exist
|
||||
* @param {boolean} exclusive if true the command will fail if the file or directory exists
|
||||
*/
|
||||
function Flags(create, exclusive) {
|
||||
var Flags = function(create, exclusive) {
|
||||
this.create = create || false;
|
||||
this.exclusive = exclusive || false;
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface representing a file system
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* {DOMString} name the unique name of the file system (readonly)
|
||||
* {DirectoryEntry} root directory of the file system (readonly)
|
||||
*/
|
||||
function FileSystem() {
|
||||
var FileSystem = function() {
|
||||
this.name = null;
|
||||
this.root = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface that lists the files and directories in a directory.
|
||||
* @constructor
|
||||
*/
|
||||
var DirectoryReader = function(fullPath){
|
||||
this.fullPath = fullPath || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of entries from a directory.
|
||||
*
|
||||
* @param {Function} successCallback is called with a list of entries
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "readEntries", [this.fullPath]);
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface representing a directory on the file system.
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* {boolean} isFile always false (readonly)
|
||||
* {boolean} isDirectory always true (readonly)
|
||||
* {DOMString} name of the directory, excluding the path leading to it (readonly)
|
||||
* {DOMString} fullPath the absolute full path to the directory (readonly)
|
||||
* {FileSystem} filesystem on which the directory resides (readonly)
|
||||
*/
|
||||
function DirectoryEntry() {
|
||||
var DirectoryEntry = function() {
|
||||
this.isFile = false;
|
||||
this.isDirectory = true;
|
||||
this.name = null;
|
||||
@ -805,7 +706,7 @@ function DirectoryEntry() {
|
||||
|
||||
/**
|
||||
* Copies a directory to a new location
|
||||
*
|
||||
*
|
||||
* @param {DirectoryEntry} parent the directory to which to copy the entry
|
||||
* @param {DOMString} newName the new name of the entry, defaults to the current name
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
@ -817,7 +718,7 @@ DirectoryEntry.prototype.copyTo = function(parent, newName, successCallback, err
|
||||
|
||||
/**
|
||||
* Looks up the metadata of the entry
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with a Metadata object
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -827,7 +728,7 @@ DirectoryEntry.prototype.getMetadata = function(successCallback, errorCallback)
|
||||
|
||||
/**
|
||||
* Gets the parent of the entry
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with a parent entry
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -837,7 +738,7 @@ DirectoryEntry.prototype.getParent = function(successCallback, errorCallback) {
|
||||
|
||||
/**
|
||||
* Moves a directory to a new location
|
||||
*
|
||||
*
|
||||
* @param {DirectoryEntry} parent the directory to which to move the entry
|
||||
* @param {DOMString} newName the new name of the entry, defaults to the current name
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
@ -849,7 +750,7 @@ DirectoryEntry.prototype.moveTo = function(parent, newName, successCallback, err
|
||||
|
||||
/**
|
||||
* Removes the entry
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with no parameters
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -859,7 +760,7 @@ DirectoryEntry.prototype.remove = function(successCallback, errorCallback) {
|
||||
|
||||
/**
|
||||
* Returns a URI that can be used to identify this entry.
|
||||
*
|
||||
*
|
||||
* @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
|
||||
* @return uri
|
||||
*/
|
||||
@ -876,7 +777,7 @@ DirectoryEntry.prototype.createReader = function(successCallback, errorCallback)
|
||||
|
||||
/**
|
||||
* Creates or looks up a directory
|
||||
*
|
||||
*
|
||||
* @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a directory
|
||||
* @param {Flags} options to create or excluively create the directory
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
@ -888,7 +789,7 @@ DirectoryEntry.prototype.getDirectory = function(path, options, successCallback,
|
||||
|
||||
/**
|
||||
* Creates or looks up a file
|
||||
*
|
||||
*
|
||||
* @param {DOMString} path either a relative or absolute path from this directory in which to look up or create a file
|
||||
* @param {Flags} options to create or excluively create the file
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
@ -900,7 +801,7 @@ DirectoryEntry.prototype.getFile = function(path, options, successCallback, erro
|
||||
|
||||
/**
|
||||
* Deletes a directory and all of it's contents
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with no parameters
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -908,33 +809,17 @@ DirectoryEntry.prototype.removeRecursively = function(successCallback, errorCall
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "removeRecursively", [this.fullPath]);
|
||||
};
|
||||
|
||||
/**
|
||||
* An interface that lists the files and directories in a directory.
|
||||
*/
|
||||
function DirectoryReader(fullPath){
|
||||
this.fullPath = fullPath || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a list of entries from a directory.
|
||||
*
|
||||
* @param {Function} successCallback is called with a list of entries
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
DirectoryReader.prototype.readEntries = function(successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "readEntries", [this.fullPath]);
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface representing a directory on the file system.
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* {boolean} isFile always true (readonly)
|
||||
* {boolean} isDirectory always false (readonly)
|
||||
* {DOMString} name of the file, excluding the path leading to it (readonly)
|
||||
* {DOMString} fullPath the absolute full path to the file (readonly)
|
||||
* {FileSystem} filesystem on which the directory resides (readonly)
|
||||
*/
|
||||
function FileEntry() {
|
||||
var FileEntry = function() {
|
||||
this.isFile = true;
|
||||
this.isDirectory = false;
|
||||
this.name = null;
|
||||
@ -944,7 +829,7 @@ function FileEntry() {
|
||||
|
||||
/**
|
||||
* Copies a file to a new location
|
||||
*
|
||||
*
|
||||
* @param {DirectoryEntry} parent the directory to which to copy the entry
|
||||
* @param {DOMString} newName the new name of the entry, defaults to the current name
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
@ -956,7 +841,7 @@ FileEntry.prototype.copyTo = function(parent, newName, successCallback, errorCal
|
||||
|
||||
/**
|
||||
* Looks up the metadata of the entry
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with a Metadata object
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -966,7 +851,7 @@ FileEntry.prototype.getMetadata = function(successCallback, errorCallback) {
|
||||
|
||||
/**
|
||||
* Gets the parent of the entry
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with a parent entry
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -976,7 +861,7 @@ FileEntry.prototype.getParent = function(successCallback, errorCallback) {
|
||||
|
||||
/**
|
||||
* Moves a directory to a new location
|
||||
*
|
||||
*
|
||||
* @param {DirectoryEntry} parent the directory to which to move the entry
|
||||
* @param {DOMString} newName the new name of the entry, defaults to the current name
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
@ -988,7 +873,7 @@ FileEntry.prototype.moveTo = function(parent, newName, successCallback, errorCal
|
||||
|
||||
/**
|
||||
* Removes the entry
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with no parameters
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -998,7 +883,7 @@ FileEntry.prototype.remove = function(successCallback, errorCallback) {
|
||||
|
||||
/**
|
||||
* Returns a URI that can be used to identify this entry.
|
||||
*
|
||||
*
|
||||
* @param {DOMString} mimeType for a FileEntry, the mime type to be used to interpret the file, when loaded through this URI.
|
||||
* @return uri
|
||||
*/
|
||||
@ -1008,29 +893,31 @@ FileEntry.prototype.toURI = function(mimeType) {
|
||||
|
||||
/**
|
||||
* Creates a new FileWriter associated with the file that this FileEntry represents.
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with the new FileWriter
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
FileEntry.prototype.createWriter = function(successCallback, errorCallback) {
|
||||
var writer = new FileWriter(this.fullPath);
|
||||
|
||||
if (writer.fileName == null || writer.fileName == "") {
|
||||
if (typeof errorCallback == "function") {
|
||||
errorCallback({
|
||||
"code": FileError.INVALID_STATE_ERR
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof successCallback == "function") {
|
||||
successCallback(writer);
|
||||
}
|
||||
this.file(function(filePointer) {
|
||||
var writer = new FileWriter(filePointer);
|
||||
|
||||
if (writer.fileName === null || writer.fileName === "") {
|
||||
if (typeof errorCallback == "function") {
|
||||
errorCallback({
|
||||
"code": FileError.INVALID_STATE_ERR
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof successCallback == "function") {
|
||||
successCallback(writer);
|
||||
}
|
||||
}, errorCallback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a File that represents the current state of the file that this FileEntry represents.
|
||||
*
|
||||
*
|
||||
* @param {Function} successCallback is called with the new File object
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
@ -1038,6 +925,127 @@ FileEntry.prototype.file = function(successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "getFileMetadata", [this.fullPath]);
|
||||
};
|
||||
|
||||
/** @constructor */
|
||||
var LocalFileSystem = function() {
|
||||
};
|
||||
|
||||
// File error codes
|
||||
LocalFileSystem.TEMPORARY = 0;
|
||||
LocalFileSystem.PERSISTENT = 1;
|
||||
LocalFileSystem.RESOURCE = 2;
|
||||
LocalFileSystem.APPLICATION = 3;
|
||||
|
||||
/**
|
||||
* Requests a filesystem in which to store application data.
|
||||
*
|
||||
* @param {int} type of file system being requested
|
||||
* @param {Function} successCallback is called with the new FileSystem
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
LocalFileSystem.prototype.requestFileSystem = function(type, size, successCallback, errorCallback) {
|
||||
if (type < 0 || type > 3) {
|
||||
if (typeof errorCallback == "function") {
|
||||
errorCallback({
|
||||
"code": FileError.SYNTAX_ERR
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "requestFileSystem", [type, size]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DOMString} uri referring to a local file in a filesystem
|
||||
* @param {Function} successCallback is called with the new entry
|
||||
* @param {Function} errorCallback is called with a FileError
|
||||
*/
|
||||
LocalFileSystem.prototype.resolveLocalFileSystemURI = function(uri, successCallback, errorCallback) {
|
||||
PhoneGap.exec(successCallback, errorCallback, "File", "resolveLocalFileSystemURI", [uri]);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 a JSON Objects that need to be converted to DirectoryEntry or FileEntry objects.
|
||||
* @returns an entry
|
||||
*/
|
||||
LocalFileSystem.prototype._castFS = function(pluginResult) {
|
||||
var entry = null;
|
||||
entry = new DirectoryEntry();
|
||||
entry.isDirectory = pluginResult.message.root.isDirectory;
|
||||
entry.isFile = pluginResult.message.root.isFile;
|
||||
entry.name = pluginResult.message.root.name;
|
||||
entry.fullPath = pluginResult.message.root.fullPath;
|
||||
pluginResult.message.root = entry;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype._castEntry = function(pluginResult) {
|
||||
var entry = null;
|
||||
if (pluginResult.message.isDirectory) {
|
||||
console.log("This is a dir");
|
||||
entry = new DirectoryEntry();
|
||||
}
|
||||
else if (pluginResult.message.isFile) {
|
||||
console.log("This is a file");
|
||||
entry = new FileEntry();
|
||||
}
|
||||
entry.isDirectory = pluginResult.message.isDirectory;
|
||||
entry.isFile = pluginResult.message.isFile;
|
||||
entry.name = pluginResult.message.name;
|
||||
entry.fullPath = pluginResult.message.fullPath;
|
||||
pluginResult.message = entry;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype._castEntries = function(pluginResult) {
|
||||
var entries = pluginResult.message;
|
||||
var retVal = [];
|
||||
for (var i=0; i<entries.length; i++) {
|
||||
retVal.push(window.localFileSystem._createEntry(entries[i]));
|
||||
}
|
||||
pluginResult.message = retVal;
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype._createEntry = function(castMe) {
|
||||
var entry = null;
|
||||
if (castMe.isDirectory) {
|
||||
console.log("This is a dir");
|
||||
entry = new DirectoryEntry();
|
||||
}
|
||||
else if (castMe.isFile) {
|
||||
console.log("This is a file");
|
||||
entry = new FileEntry();
|
||||
}
|
||||
entry.isDirectory = castMe.isDirectory;
|
||||
entry.isFile = castMe.isFile;
|
||||
entry.name = castMe.name;
|
||||
entry.fullPath = castMe.fullPath;
|
||||
return entry;
|
||||
};
|
||||
|
||||
LocalFileSystem.prototype._castDate = function(pluginResult) {
|
||||
if (pluginResult.message.modificationTime) {
|
||||
var modTime = new Date(pluginResult.message.modificationTime);
|
||||
pluginResult.message.modificationTime = modTime;
|
||||
}
|
||||
else if (pluginResult.message.lastModifiedDate) {
|
||||
var file = new File();
|
||||
file.size = pluginResult.message.size;
|
||||
file.type = pluginResult.message.type;
|
||||
file.name = pluginResult.message.name;
|
||||
file.fullPath = pluginResult.message.fullPath;
|
||||
file.lastModifiedDate = new Date(pluginResult.message.lastModifiedDate);
|
||||
pluginResult.message = file;
|
||||
}
|
||||
return pluginResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add the FileSystem interface into the browser.
|
||||
*/
|
||||
@ -1048,3 +1056,4 @@ PhoneGap.addConstructor(function() {
|
||||
if(typeof window.requestFileSystem == "undefined") window.requestFileSystem = pgLocalFileSystem.requestFileSystem;
|
||||
if(typeof window.resolveLocalFileSystemURI == "undefined") window.resolveLocalFileSystemURI = pgLocalFileSystem.resolveLocalFileSystemURI;
|
||||
});
|
||||
}
|
||||
|
@ -1,44 +1,50 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("filetransfer")) {
|
||||
PhoneGap.addResource("filetransfer");
|
||||
|
||||
/**
|
||||
* FileTransfer uploads a file to a remote server.
|
||||
* @constructor
|
||||
*/
|
||||
function FileTransfer() {}
|
||||
var FileTransfer = function() {};
|
||||
|
||||
/**
|
||||
* FileUploadResult
|
||||
* @constructor
|
||||
*/
|
||||
function FileUploadResult() {
|
||||
var FileUploadResult = function() {
|
||||
this.bytesSent = 0;
|
||||
this.responseCode = null;
|
||||
this.response = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* FileTransferError
|
||||
* @constructor
|
||||
*/
|
||||
function FileTransferError() {
|
||||
var FileTransferError = function() {
|
||||
this.code = null;
|
||||
}
|
||||
};
|
||||
|
||||
FileTransferError.FILE_NOT_FOUND_ERR = 1;
|
||||
FileTransferError.INVALID_URL_ERR = 2;
|
||||
FileTransferError.CONNECTION_ERR = 3;
|
||||
|
||||
/**
|
||||
* Given an absolute file path, uploads a file on the device to a remote server
|
||||
* 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) {
|
||||
|
||||
@ -58,20 +64,22 @@ 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.
|
||||
*/
|
||||
function FileUploadOptions(fileKey, fileName, mimeType, params) {
|
||||
var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
|
||||
this.fileKey = fileKey || null;
|
||||
this.fileName = fileName || null;
|
||||
this.mimeType = mimeType || null;
|
||||
this.params = params || null;
|
||||
};
|
||||
}
|
||||
|
@ -3,32 +3,36 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("geolocation")) {
|
||||
PhoneGap.addResource("geolocation");
|
||||
|
||||
/**
|
||||
* This class provides access to device GPS data.
|
||||
* @constructor
|
||||
*/
|
||||
function Geolocation() {
|
||||
var Geolocation = function() {
|
||||
|
||||
// The last known GPS position.
|
||||
this.lastPosition = null;
|
||||
|
||||
// Geolocation listeners
|
||||
this.listeners = {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Position error object
|
||||
*
|
||||
* @constructor
|
||||
* @param code
|
||||
* @param message
|
||||
*/
|
||||
function PositionError(code, message) {
|
||||
var PositionError = function(code, message) {
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
};
|
||||
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
@ -191,4 +195,4 @@ PhoneGap.addConstructor(function() {
|
||||
Geolocation.usingPhoneGap = true;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -3,66 +3,16 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
if (!PhoneGap.hasResource("media")) {
|
||||
PhoneGap.addResource("media");
|
||||
|
||||
/**
|
||||
* This class provides access to the device media, interfaces to both sound and video
|
||||
*
|
||||
* @constructor
|
||||
* @param src The file name or url to play
|
||||
* @param successCallback The callback to be called when the file is done playing or recording.
|
||||
* successCallback() - OPTIONAL
|
||||
@ -73,7 +23,7 @@ PhoneGap.Media.onStatus = function(id, msg, value) {
|
||||
* @param positionCallback The callback to be called when media position has changed.
|
||||
* positionCallback(long position) - OPTIONAL
|
||||
*/
|
||||
Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
|
||||
var Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
|
||||
|
||||
// successCallback optional
|
||||
if (successCallback && (typeof successCallback !== "function")) {
|
||||
@ -128,10 +78,10 @@ Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
|
||||
* This class contains information about any Media errors.
|
||||
* @constructor
|
||||
*/
|
||||
function MediaError() {
|
||||
var MediaError = function() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
}
|
||||
};
|
||||
|
||||
MediaError.MEDIA_ERR_ABORTED = 1;
|
||||
MediaError.MEDIA_ERR_NETWORK = 2;
|
||||
@ -152,6 +102,13 @@ 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.
|
||||
*/
|
||||
@ -171,8 +128,6 @@ Media.prototype.getDuration = function() {
|
||||
|
||||
/**
|
||||
* Get position of audio.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Media.prototype.getCurrentPosition = function(success, fail) {
|
||||
PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]);
|
||||
@ -199,3 +154,58 @@ 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,62 +3,80 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("network")) {
|
||||
PhoneGap.addResource("network");
|
||||
|
||||
/**
|
||||
* This class contains information about any NetworkStatus.
|
||||
* This class contains information about the current network Connection.
|
||||
* @constructor
|
||||
*/
|
||||
function NetworkStatus() {
|
||||
//this.code = null;
|
||||
//this.message = "";
|
||||
}
|
||||
var Connection = function() {
|
||||
this.type = null;
|
||||
this._firstRun = true;
|
||||
this._timer = null;
|
||||
this.timeout = 500;
|
||||
|
||||
NetworkStatus.NOT_REACHABLE = 0;
|
||||
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
|
||||
/**
|
||||
* This class provides access to device Network data (reachability).
|
||||
* @constructor
|
||||
*/
|
||||
function Network() {
|
||||
/**
|
||||
* The last known Network status.
|
||||
* { hostName: string, ipAddress: string,
|
||||
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
|
||||
*/
|
||||
this.lastReachability = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the geolocation framework when the reachability status has changed.
|
||||
* @param {Reachibility} reachability The current reachability status.
|
||||
*/
|
||||
// TODO: Callback from native code not implemented for Android
|
||||
Network.prototype.updateReachability = function(reachability) {
|
||||
this.lastReachability = reachability;
|
||||
var me = this;
|
||||
this.getInfo(
|
||||
function(type) {
|
||||
// Need to send events if we are on or offline
|
||||
if (type == "none") {
|
||||
// set a timer if still offline at the end of timer send the offline event
|
||||
me._timer = setTimeout(function(){
|
||||
me.type = type;
|
||||
PhoneGap.fireEvent('offline');
|
||||
me._timer = null;
|
||||
}, me.timeout);
|
||||
} else {
|
||||
// If there is a current offline event pending clear it
|
||||
if (me._timer != null) {
|
||||
clearTimeout(me._timer);
|
||||
me._timer = null;
|
||||
}
|
||||
me.type = type;
|
||||
PhoneGap.fireEvent('online');
|
||||
}
|
||||
|
||||
// should only fire this once
|
||||
if (me._firstRun) {
|
||||
me._firstRun = false;
|
||||
PhoneGap.onPhoneGapConnectionReady.fire();
|
||||
}
|
||||
},
|
||||
function(e) {
|
||||
console.log("Error initializing Network Connection: " + e);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine if a URI is reachable over the network.
|
||||
Connection.UNKNOWN = "unknown";
|
||||
Connection.ETHERNET = "ethernet";
|
||||
Connection.WIFI = "wifi";
|
||||
Connection.CELL_2G = "2g";
|
||||
Connection.CELL_3G = "3g";
|
||||
Connection.CELL_4G = "4g";
|
||||
Connection.NONE = "none";
|
||||
|
||||
* @param {Object} uri
|
||||
* @param {Function} callback
|
||||
* @param {Object} options (isIpAddress:boolean)
|
||||
/**
|
||||
* Get connection info
|
||||
*
|
||||
* @param {Function} successCallback The function to call when the Connection data is available
|
||||
* @param {Function} errorCallback The function to call when there is an error getting the Connection data. (OPTIONAL)
|
||||
*/
|
||||
Network.prototype.isReachable = function(uri, callback, options) {
|
||||
var isIpAddress = false;
|
||||
if (options && options.isIpAddress) {
|
||||
isIpAddress = options.isIpAddress;
|
||||
}
|
||||
PhoneGap.exec(callback, null, "Network Status", "isReachable", [uri, isIpAddress]);
|
||||
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();
|
||||
navigator.network = new Object();
|
||||
}
|
||||
if (typeof navigator.network.connection === "undefined") {
|
||||
navigator.network.connection = new Connection();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -3,14 +3,18 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("notification")) {
|
||||
PhoneGap.addResource("notification");
|
||||
|
||||
/**
|
||||
* This class provides access to notifications on the device.
|
||||
* @constructor
|
||||
*/
|
||||
function Notification() {
|
||||
}
|
||||
var Notification = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Open a native alert dialog, with a customizable title and button text.
|
||||
@ -115,4 +119,4 @@ PhoneGap.addConstructor(function() {
|
||||
navigator.notification = new Notification();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* 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:
|
||||
@ -18,6 +19,8 @@
|
||||
* 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
|
||||
@ -26,10 +29,11 @@
|
||||
* Listeners can be registered as:
|
||||
* document.addEventListener("deviceready", myDeviceReadyListener, false);
|
||||
* document.addEventListener("resume", myResumeListener, false);
|
||||
* document.addEventListener("pause", myPauseListener, false);
|
||||
*/
|
||||
|
||||
if (typeof(DeviceInfo) !== 'object') {
|
||||
DeviceInfo = {};
|
||||
var DeviceInfo = {};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,9 +49,34 @@ 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)
|
||||
{
|
||||
@ -59,7 +88,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.
|
||||
@ -70,7 +99,7 @@ PhoneGap.Channel.prototype.subscribe = function(f, c, g) {
|
||||
if (f === null) { return; }
|
||||
|
||||
var func = f;
|
||||
if (typeof c === "object" && f instanceof Function) { func = PhoneGap.close(c, f); }
|
||||
if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); }
|
||||
|
||||
g = g || func.observer_guid || f.observer_guid || this.guid++;
|
||||
func.observer_guid = g;
|
||||
@ -91,7 +120,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" && typeof f === "function") { f = PhoneGap.close(c, f); }
|
||||
f.apply(this, this.fireArgs);
|
||||
} else {
|
||||
g = this.subscribe(m);
|
||||
@ -99,16 +128,16 @@ 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) {
|
||||
if (g instanceof Function) { g = g.observer_guid; }
|
||||
if (typeof g === "function") { g = g.observer_guid; }
|
||||
this.handlers[g] = null;
|
||||
delete this.handlers[g];
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Calls all functions subscribed to this channel.
|
||||
*/
|
||||
PhoneGap.Channel.prototype.fire = function(e) {
|
||||
@ -118,7 +147,7 @@ PhoneGap.Channel.prototype.fire = function(e) {
|
||||
for (item in this.handlers) {
|
||||
if (this.handlers.hasOwnProperty(item)) {
|
||||
handler = this.handlers[item];
|
||||
if (handler instanceof Function) {
|
||||
if (typeof handler === "function") {
|
||||
rv = (handler.apply(this, arguments) === false);
|
||||
fail = fail || rv;
|
||||
}
|
||||
@ -201,7 +230,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');
|
||||
@ -229,6 +258,12 @@ 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.
|
||||
@ -241,8 +276,19 @@ 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(); }
|
||||
|
||||
@ -254,7 +300,7 @@ PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady');
|
||||
|
||||
|
||||
// Array of channels that must fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady];
|
||||
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady, PhoneGap.onPhoneGapConnectionReady];
|
||||
|
||||
// Hashtable of user defined channels that must also fire before "deviceready" is fired
|
||||
PhoneGap.deviceReadyChannelsMap = {};
|
||||
@ -278,7 +324,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) {
|
||||
@ -298,10 +344,13 @@ PhoneGap.Channel.join(function() {
|
||||
}
|
||||
else {
|
||||
var polling = prompt("usePolling", "gap_callbackServer:");
|
||||
PhoneGap.UsePolling = polling;
|
||||
if (polling == "true") {
|
||||
PhoneGap.UsePolling = true;
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
else {
|
||||
PhoneGap.UsePolling = false;
|
||||
PhoneGap.JSCallback();
|
||||
}
|
||||
}
|
||||
@ -316,10 +365,6 @@ 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();
|
||||
|
||||
PhoneGap.onDeviceReady.fire();
|
||||
|
||||
// Fire the onresume event, since first one happens before JavaScript is loaded
|
||||
@ -347,18 +392,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;
|
||||
|
||||
@ -368,7 +413,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);
|
||||
};
|
||||
@ -387,14 +432,14 @@ PhoneGap.fireEvent = function(type) {
|
||||
* The restriction on ours is that it must be an array of simple types.
|
||||
*
|
||||
* @param args
|
||||
* @return
|
||||
* @return {String}
|
||||
*/
|
||||
PhoneGap.stringify = function(args) {
|
||||
if (typeof JSON === "undefined") {
|
||||
var s = "[";
|
||||
var i, type, start, name, nameType, a;
|
||||
for (i = 0; i < args.length; i++) {
|
||||
if (args[i] != null) {
|
||||
if (args[i] !== null) {
|
||||
if (i > 0) {
|
||||
s = s + ",";
|
||||
}
|
||||
@ -419,7 +464,7 @@ PhoneGap.stringify = function(args) {
|
||||
// don't copy the functions
|
||||
s = s + '""';
|
||||
} else if (args[i][name] instanceof Object) {
|
||||
s = s + this.stringify(args[i][name]);
|
||||
s = s + PhoneGap.stringify(args[i][name]);
|
||||
} else {
|
||||
s = s + '"' + args[i][name] + '"';
|
||||
}
|
||||
@ -445,41 +490,41 @@ PhoneGap.stringify = function(args) {
|
||||
* Does a deep clone of the object.
|
||||
*
|
||||
* @param obj
|
||||
* @return
|
||||
* @return {Object}
|
||||
*/
|
||||
PhoneGap.clone = function(obj) {
|
||||
var i, retVal;
|
||||
if(!obj) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(obj instanceof Array){
|
||||
retVal = [];
|
||||
for(i = 0; i < obj.length; ++i){
|
||||
retVal.push(PhoneGap.clone(obj[i]));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
if (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 (typeof obj === "function") {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if(!(obj instanceof Object)){
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (obj instanceof Date) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
retVal = {};
|
||||
for(i in obj){
|
||||
if(!(i in retVal) || retVal[i] !== obj[i]) {
|
||||
retVal[i] = PhoneGap.clone(obj[i]);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
retVal = {};
|
||||
for(i in obj){
|
||||
if(!(i in retVal) || retVal[i] !== obj[i]) {
|
||||
retVal[i] = PhoneGap.clone(obj[i]);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
PhoneGap.callbackId = 0;
|
||||
@ -499,7 +544,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 ""
|
||||
@ -510,7 +555,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 {String[]} [args] Zero or more arguments to pass to the method
|
||||
* @param {Array.<String>} [args] Zero or more arguments to pass to the method
|
||||
*/
|
||||
PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
try {
|
||||
@ -518,13 +563,13 @@ PhoneGap.exec = function(success, fail, service, action, args) {
|
||||
if (success || fail) {
|
||||
PhoneGap.callbacks[callbackId] = {success:success, fail:fail};
|
||||
}
|
||||
|
||||
var r = prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
||||
|
||||
|
||||
var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));
|
||||
|
||||
// If a result was returned
|
||||
if (r.length > 0) {
|
||||
eval("var v="+r+";");
|
||||
|
||||
|
||||
// If status is OK, then return value back to caller
|
||||
if (v.status === PhoneGap.callbackStatus.OK) {
|
||||
|
||||
@ -547,7 +592,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];
|
||||
@ -600,7 +645,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];
|
||||
@ -624,7 +669,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];
|
||||
@ -690,12 +735,17 @@ 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();
|
||||
@ -708,10 +758,16 @@ 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) {
|
||||
|
||||
var msg = xmlhttp.responseText;
|
||||
// Need to url decode the response
|
||||
var msg = decodeURIComponent(xmlhttp.responseText);
|
||||
setTimeout(function() {
|
||||
try {
|
||||
var t = eval(msg);
|
||||
@ -745,13 +801,11 @@ PhoneGap.JSCallback = function() {
|
||||
console.log("JSCallback Error: Bad request. Stopping callbacks.");
|
||||
}
|
||||
|
||||
// If error, restart callback server
|
||||
// If error, revert to polling
|
||||
else {
|
||||
console.log("JSCallback Error: Request failed.");
|
||||
prompt("restartServer", "gap_callbackServer:");
|
||||
PhoneGap.JSCallbackPort = null;
|
||||
PhoneGap.JSCallbackToken = null;
|
||||
setTimeout(PhoneGap.JSCallback, 100);
|
||||
PhoneGap.UsePolling = true;
|
||||
PhoneGap.JSCallbackPolling();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -780,12 +834,17 @@ 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();
|
||||
@ -813,7 +872,7 @@ PhoneGap.JSCallbackPolling = function() {
|
||||
/**
|
||||
* Create a UUID
|
||||
*
|
||||
* @return
|
||||
* @return {String}
|
||||
*/
|
||||
PhoneGap.createUUID = function() {
|
||||
return PhoneGap.UUIDcreatePart(4) + '-' +
|
||||
@ -855,7 +914,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') {
|
||||
@ -864,3 +923,5 @@ PhoneGap.includeJavascript = function(jsfile, successCallback) {
|
||||
el.src = jsfile;
|
||||
id.appendChild(el);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -3,9 +3,12 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
if (!PhoneGap.hasResource("position")) {
|
||||
PhoneGap.addResource("position");
|
||||
|
||||
/**
|
||||
* This class contains position information.
|
||||
* @param {Object} lat
|
||||
@ -17,12 +20,13 @@
|
||||
* @param {Object} vel
|
||||
* @constructor
|
||||
*/
|
||||
function Position(coords, timestamp) {
|
||||
var Position = function(coords, timestamp) {
|
||||
this.coords = coords;
|
||||
this.timestamp = (timestamp !== 'undefined') ? timestamp : new Date().getTime();
|
||||
}
|
||||
};
|
||||
|
||||
function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/** @constructor */
|
||||
var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/**
|
||||
* The latitude of the position.
|
||||
*/
|
||||
@ -50,14 +54,14 @@ function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
|
||||
/**
|
||||
* The altitude accuracy of the position.
|
||||
*/
|
||||
this.altitudeAccuracy = (altacc !== 'undefined') ? altacc : null;
|
||||
}
|
||||
this.altitudeAccuracy = (altacc !== 'undefined') ? altacc : null;
|
||||
};
|
||||
|
||||
/**
|
||||
* This class specifies the options for requesting position data.
|
||||
* @constructor
|
||||
*/
|
||||
function PositionOptions() {
|
||||
var PositionOptions = function() {
|
||||
/**
|
||||
* Specifies the desired position accuracy.
|
||||
*/
|
||||
@ -67,18 +71,19 @@ function PositionOptions() {
|
||||
* is called.
|
||||
*/
|
||||
this.timeout = 10000;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class contains information about any GSP errors.
|
||||
* @constructor
|
||||
*/
|
||||
function PositionError() {
|
||||
var PositionError = function() {
|
||||
this.code = null;
|
||||
this.message = "";
|
||||
}
|
||||
};
|
||||
|
||||
PositionError.UNKNOWN_ERROR = 0;
|
||||
PositionError.PERMISSION_DENIED = 1;
|
||||
PositionError.POSITION_UNAVAILABLE = 2;
|
||||
PositionError.TIMEOUT = 3;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -12,9 +12,42 @@
|
||||
* 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 = {};
|
||||
@ -99,9 +132,40 @@ 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() {
|
||||
|
||||
@ -116,37 +180,6 @@ 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.
|
||||
@ -162,7 +195,7 @@ DroidDB_Tx.prototype.queryComplete = function(id) {
|
||||
var i;
|
||||
for (i in this.queryList) {
|
||||
if (this.queryList.hasOwnProperty(i)) {
|
||||
count++;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count === 0) {
|
||||
@ -198,35 +231,6 @@ 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
|
||||
*
|
||||
@ -254,31 +258,33 @@ DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCa
|
||||
PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL result set that is returned to user.
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
DroidDB_Result = function() {
|
||||
this.rows = new DroidDB_Rows();
|
||||
var DatabaseShell = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* SQL result set object
|
||||
* PRIVATE METHOD
|
||||
*/
|
||||
DroidDB_Rows = function() {
|
||||
this.resultSet = []; // results array
|
||||
this.length = 0; // number of rows
|
||||
};
|
||||
|
||||
/**
|
||||
* Get item from SQL result set
|
||||
* Start a transaction.
|
||||
* Does not support rollback in event of failure.
|
||||
*
|
||||
* @param row The row number to return
|
||||
* @return The row object
|
||||
* @param process {Function} The transaction function
|
||||
* @param successCallback {Function}
|
||||
* @param errorCallback {Function}
|
||||
*/
|
||||
DroidDB_Rows.prototype.item = function(row) {
|
||||
return this.resultSet[row];
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -290,22 +296,24 @@ DroidDB_Rows.prototype.item = function(row) {
|
||||
* @param size Database size in bytes
|
||||
* @return Database object
|
||||
*/
|
||||
DroidDB_openDatabase = function(name, version, display_name, size) {
|
||||
var DroidDB_openDatabase = function(name, version, display_name, size) {
|
||||
PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
|
||||
var db = new DatabaseShell();
|
||||
return db;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
|
||||
* TODO: Do similar for sessionStorage.
|
||||
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
|
||||
* TODO: Do similar for sessionStorage.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
var CupcakeLocalStorage = function() {
|
||||
try {
|
||||
|
||||
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
|
||||
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
|
||||
var storage = {};
|
||||
this.length = 0;
|
||||
function setLength (length) {
|
||||
@ -323,8 +331,8 @@ var CupcakeLocalStorage = function() {
|
||||
setLength(result.rows.length);
|
||||
PhoneGap.initializationComplete("cupcakeStorage");
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
function (err) {
|
||||
alert(err.message);
|
||||
}
|
||||
@ -341,7 +349,7 @@ var CupcakeLocalStorage = function() {
|
||||
}
|
||||
);
|
||||
};
|
||||
this.getItem = function(key) {
|
||||
this.getItem = function(key) {
|
||||
return storage[key];
|
||||
};
|
||||
this.removeItem = function(key) {
|
||||
@ -374,22 +382,47 @@ var CupcakeLocalStorage = function() {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
} catch(e) {
|
||||
alert("Database error "+e+".");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
PhoneGap.addConstructor(function() {
|
||||
if (typeof window.openDatabase === "undefined") {
|
||||
var setupDroidDB = function() {
|
||||
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
|
||||
window.droiddb = new DroidDB();
|
||||
}
|
||||
if (typeof window.openDatabase === "undefined") {
|
||||
setupDroidDB();
|
||||
} else {
|
||||
window.openDatabase_orig = window.openDatabase;
|
||||
window.openDatabase = function(name, version, desc, size){
|
||||
// Some versions of Android will throw a SECURITY_ERR so we need
|
||||
// to catch the exception and seutp our own DB handling.
|
||||
var db = null;
|
||||
try {
|
||||
db = window.openDatabase_orig(name, version, desc, size);
|
||||
}
|
||||
catch (ex) {
|
||||
db = null;
|
||||
}
|
||||
|
||||
if (db == null) {
|
||||
setupDroidDB();
|
||||
return DroidDB_openDatabase(name, version, desc, size);
|
||||
}
|
||||
else {
|
||||
return db;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window.localStorage === "undefined") {
|
||||
navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
|
||||
PhoneGap.waitForInitialization("cupcakeStorage");
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title></title>
|
||||
<script src="phonegap.0.9.4.min.js"></script>
|
||||
<script src="phonegap-1.0.0rc2.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -40,6 +40,9 @@
|
||||
This file is an integral part of the build system for your application and
|
||||
should be checked in in Version Control Systems. -->
|
||||
<property file="default.properties" />
|
||||
|
||||
<!-- We need to setup the double quote. -->
|
||||
<property name="dblQuote">"</property>
|
||||
|
||||
<!-- Custom Android task to deal with the project target, and import the proper rules.
|
||||
This requires ant 1.6.0 or above. -->
|
||||
@ -71,88 +74,57 @@
|
||||
-->
|
||||
<setup />
|
||||
|
||||
<target name="check-javascript" depends="build-javascript">
|
||||
<delete dir="assets/lib"/>
|
||||
<mkdir dir="assets/lib"/>
|
||||
<echo file="assets/lib/lint.js">var alert=function(){},device={},Element={},debug={};</echo>
|
||||
<concat destfile="assets/lib/phonegap-lint.js" append="true">
|
||||
<fileset dir="assets/lib">
|
||||
<include name="lint.js" />
|
||||
</fileset>
|
||||
<fileset dir="assets/www">
|
||||
<include name="phonegap.${version}.js" />
|
||||
</fileset>
|
||||
</concat>
|
||||
<target name="check-javascript" depends="build-javascript">
|
||||
<delete dir="assets/lib"/>
|
||||
<mkdir dir="assets/lib"/>
|
||||
<echo file="assets/lib/lint.js">var alert=function(){},device={},Element={},debug={};</echo>
|
||||
<concat destfile="assets/lib/phonegap-lint.js" append="true">
|
||||
<fileset dir="assets/lib">
|
||||
<include name="lint.js" />
|
||||
</fileset>
|
||||
<fileset dir="assets/www">
|
||||
<include name="phonegap-${version}.js" />
|
||||
</fileset>
|
||||
</concat>
|
||||
|
||||
<exec executable="cmd" os="Windows 7">
|
||||
<arg value="/c"/>
|
||||
<arg value="java"/>
|
||||
<arg value="-cp"/>
|
||||
<arg value="${basedir}/util/js.jar"/>
|
||||
<arg value="org.mozilla.javascript.tools.shell.Main"/>
|
||||
<arg value="${basedir}/util/jslint.js"/>
|
||||
<arg value="${basedir}/js/lib/phonegap-lint.js"/>
|
||||
</exec>
|
||||
<exec executable="java" os="Mac OS X">
|
||||
<arg value="-cp"/>
|
||||
<arg value="../util/js.jar"/>
|
||||
<arg value="org.mozilla.javascript.tools.shell.Main"/>
|
||||
<arg value="../util/jslint.js"/>
|
||||
<arg value="assets/lib/phonegap-lint.js"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- Combine JavaScript files into one phonegap.js file.
|
||||
This task does not create a compressed JavaScript file. -->
|
||||
<target name="build-uncompressed-javascript">
|
||||
|
||||
<!-- Clean up existing files -->
|
||||
<delete file="assets/www/phonegap.${version}.min.js"/>
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
<delete file="assets/www/phonegap.${version}.js"/>
|
||||
|
||||
<!-- Create uncompressed JS file -->
|
||||
<concat destfile="assets/www/phonegap.${version}.js">
|
||||
<fileset dir="assets/js" includes="phonegap.js.base" />
|
||||
<fileset dir="assets/js" includes="*.js" />
|
||||
</concat>
|
||||
|
||||
<!-- update project files to reference phonegap-x.x.x.js -->
|
||||
<replaceregexp match="phonegap(.*)\.js" replace="phonegap.${version}.js" byline="true">
|
||||
<fileset file="assets/www/index.html" />
|
||||
<fileset file="../example/index.html" />
|
||||
</replaceregexp>
|
||||
<exec executable="cmd" os="Windows 7">
|
||||
<arg value="/c"/>
|
||||
<arg value="java"/>
|
||||
<arg value="-cp"/>
|
||||
<arg value="${basedir}/util/js.jar"/>
|
||||
<arg value="org.mozilla.javascript.tools.shell.Main"/>
|
||||
<arg value="${basedir}/util/jslint.js"/>
|
||||
<arg value="${basedir}/js/lib/phonegap-lint.js"/>
|
||||
</exec>
|
||||
<exec executable="java" os="Mac OS X">
|
||||
<arg value="-cp"/>
|
||||
<arg value="../util/js.jar"/>
|
||||
<arg value="org.mozilla.javascript.tools.shell.Main"/>
|
||||
<arg value="../util/jslint.js"/>
|
||||
<arg value="assets/lib/phonegap-lint.js"/>
|
||||
</exec>
|
||||
</target>
|
||||
|
||||
<!-- Combine JavaScript files into one phonegap-uncompressed.js file.
|
||||
Compress this file using yuicompressor to create phonegap.js. -->
|
||||
|
||||
<!-- Combine JavaScript files into one phonegap-uncompressed.js file. -->
|
||||
<target name="build-javascript">
|
||||
|
||||
<!-- Clean up existing files -->
|
||||
<delete file="assets/www/phonegap_${version}.min.js"/>
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
<delete file="assets/www/phonegap_${version}.js"/>
|
||||
|
||||
<!-- Create uncompressed JS file -->
|
||||
<concat destfile="assets/www/phonegap.${version}.js">
|
||||
<concat destfile="assets/www/phonegap-${version}.js">
|
||||
<fileset dir="assets/js" includes="phonegap.js.base" />
|
||||
<fileset dir="assets/js" includes="*.js" />
|
||||
</concat>
|
||||
|
||||
<!-- Compress JS file -->
|
||||
<java jar="${basedir}/../util/yuicompressor/yuicompressor-2.4.2.jar" fork="true" failonerror="true">
|
||||
<arg line="--nomunge -o assets/www/phonegap-tmp.js assets/www/phonegap.${version}.js"/>
|
||||
</java>
|
||||
<concat destfile="assets/www/phonegap.${version}.min.js">
|
||||
<fileset dir="assets/js" includes="header.txt" />
|
||||
<fileset dir="assets/www" includes="phonegap-tmp.js" />
|
||||
</concat>
|
||||
|
||||
<!-- update project files to reference phonegap-x.x.x.min.js -->
|
||||
<replaceregexp match="phonegap(.*)\.js" replace="phonegap.${version}.min.js" byline="true">
|
||||
<replaceregexp match="phonegap(.*)\.js" replace="phonegap-${version}.js" byline="true">
|
||||
<fileset file="assets/www/index.html" />
|
||||
<fileset file="../example/index.html" />
|
||||
</replaceregexp>
|
||||
<replaceregexp match="phonegapVersion = [\u0022].*[\u0022];" replace="phonegapVersion = ${dblQuote}${version}${dblQuote};" byline="true">
|
||||
<fileset file="src/com/phonegap/Device.java" />
|
||||
</replaceregexp>
|
||||
|
||||
<!-- Delete temp file -->
|
||||
<delete file="assets/www/phonegap-tmp.js"/>
|
||||
@ -160,14 +132,9 @@
|
||||
|
||||
<!-- Build PhoneGap jar file that includes all native code, and PhoneGap JS file
|
||||
that includes all JavaScript code.
|
||||
|
||||
The default is to compress the JavaScript code using yuicompressor.
|
||||
|
||||
If you want uncompressed JavaScript, change
|
||||
"build-javascript" => "build-uncompressed-javascript".
|
||||
-->
|
||||
<target name="jar" depends="build-javascript, compile">
|
||||
<jar jarfile="phonegap.${version}.jar" basedir="bin/classes" excludes="com/phonegap/R.class,com/phonegap/R$*.class"/>
|
||||
<jar jarfile="phonegap-${version}.jar" basedir="bin/classes" excludes="com/phonegap/R.class,com/phonegap/R$*.class"/>
|
||||
</target>
|
||||
|
||||
<target name="phonegap_debug" depends="build-javascript, debug">
|
||||
|
@ -10,5 +10,5 @@
|
||||
# Indicates whether an apk should be generated for each density.
|
||||
split.density=false
|
||||
# Project target.
|
||||
target=android-8
|
||||
target=android-12
|
||||
apk-configurations=
|
||||
|
@ -1,27 +0,0 @@
|
||||
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||
*
|
||||
* This class was automatically generated by the
|
||||
* aapt tool from the resource data it found. It
|
||||
* should not be modified by hand.
|
||||
*/
|
||||
|
||||
package com.phonegap;
|
||||
|
||||
public final class R {
|
||||
public static final class attr {
|
||||
}
|
||||
public static final class drawable {
|
||||
public static final int icon=0x7f020000;
|
||||
public static final 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;
|
||||
}
|
||||
}
|
20
framework/res/xml/plugins.xml
Executable file
20
framework/res/xml/plugins.xml
Executable file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<plugins>
|
||||
<plugin name="App" value="com.phonegap.App"/>
|
||||
<plugin name="Geolocation" value="com.phonegap.GeoBroker"/>
|
||||
<plugin name="Device" value="com.phonegap.Device"/>
|
||||
<plugin name="Accelerometer" value="com.phonegap.AccelListener"/>
|
||||
<plugin name="Compass" value="com.phonegap.CompassListener"/>
|
||||
<plugin name="Media" value="com.phonegap.AudioHandler"/>
|
||||
<plugin name="Camera" value="com.phonegap.CameraLauncher"/>
|
||||
<plugin name="Contacts" value="com.phonegap.ContactManager"/>
|
||||
<plugin name="Crypto" value="com.phonegap.CryptoHandler"/>
|
||||
<plugin name="File" value="com.phonegap.FileUtils"/>
|
||||
<plugin name="Location" value="com.phonegap.GeoBroker"/>
|
||||
<plugin name="Network Status" value="com.phonegap.NetworkManager"/>
|
||||
<plugin name="Notification" value="com.phonegap.Notification"/>
|
||||
<plugin name="Storage" value="com.phonegap.Storage"/>
|
||||
<plugin name="Temperature" value="com.phonegap.TempListener"/>
|
||||
<plugin name="FileTransfer" value="com.phonegap.FileTransfer"/>
|
||||
<plugin name="Capture" value="com.phonegap.Capture"/>
|
||||
</plugins>
|
@ -12,6 +12,7 @@ import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This class exposes methods in DroidGap that can be called from JavaScript.
|
||||
@ -42,9 +43,6 @@ public class App extends Plugin {
|
||||
}
|
||||
else if (action.equals("clearHistory")) {
|
||||
this.clearHistory();
|
||||
}
|
||||
else if (action.equals("addService")) {
|
||||
this.addService(args.getString(0), args.getString(1));
|
||||
}
|
||||
else if (action.equals("overrideBackbutton")) {
|
||||
this.overrideBackbutton(args.getBoolean(0));
|
||||
@ -83,8 +81,11 @@ public class App extends Plugin {
|
||||
public void loadUrl(String url, JSONObject props) throws JSONException {
|
||||
System.out.println("App.loadUrl("+url+","+props+")");
|
||||
int wait = 0;
|
||||
|
||||
boolean usePhoneGap = true;
|
||||
boolean clearPrev = false;
|
||||
|
||||
// If there are properties, then set them on the Activity
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
if (props != null) {
|
||||
JSONArray keys = props.names();
|
||||
for (int i=0; i<keys.length(); i++) {
|
||||
@ -92,31 +93,42 @@ public class App extends Plugin {
|
||||
if (key.equals("wait")) {
|
||||
wait = props.getInt(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("usephonegap")) {
|
||||
usePhoneGap = props.getBoolean(key);
|
||||
}
|
||||
else if (key.equalsIgnoreCase("clearprev")) {
|
||||
clearPrev = props.getBoolean(key);
|
||||
}
|
||||
else {
|
||||
Object value = props.get(key);
|
||||
if (value == null) {
|
||||
|
||||
|
||||
}
|
||||
else if (value.getClass().equals(String.class)) {
|
||||
this.ctx.getIntent().putExtra(key, (String)value);
|
||||
params.put(key, (String)value);
|
||||
}
|
||||
else if (value.getClass().equals(Boolean.class)) {
|
||||
this.ctx.getIntent().putExtra(key, (Boolean)value);
|
||||
params.put(key, (Boolean)value);
|
||||
}
|
||||
else if (value.getClass().equals(Integer.class)) {
|
||||
this.ctx.getIntent().putExtra(key, (Integer)value);
|
||||
params.put(key, (Integer)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If wait property, then delay loading
|
||||
|
||||
if (wait > 0) {
|
||||
((DroidGap)this.ctx).loadUrl(url, wait);
|
||||
}
|
||||
else {
|
||||
((DroidGap)this.ctx).loadUrl(url);
|
||||
try {
|
||||
synchronized(this) {
|
||||
this.wait(wait);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
((DroidGap)this.ctx).showWebPage(url, usePhoneGap, clearPrev, params);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,16 +145,6 @@ public class App extends Plugin {
|
||||
((DroidGap)this.ctx).clearHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
* @param serviceType
|
||||
* @param className
|
||||
*/
|
||||
public void addService(String serviceType, String className) {
|
||||
this.ctx.addService(serviceType, className);
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the default behavior of the Android back button.
|
||||
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
|
||||
|
@ -63,6 +63,9 @@ 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));
|
||||
}
|
||||
@ -70,12 +73,12 @@ public class AudioHandler extends Plugin {
|
||||
this.stopPlayingAudio(args.getString(0));
|
||||
}
|
||||
else if (action.equals("getCurrentPositionAudio")) {
|
||||
long l = this.getCurrentPositionAudio(args.getString(0));
|
||||
return new PluginResult(status, l);
|
||||
float f = this.getCurrentPositionAudio(args.getString(0));
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("getDurationAudio")) {
|
||||
long l = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, l);
|
||||
float f = this.getDurationAudio(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, f);
|
||||
}
|
||||
else if (action.equals("release")) {
|
||||
boolean b = this.release(args.getString(0));
|
||||
@ -181,6 +184,20 @@ 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.
|
||||
*
|
||||
@ -213,10 +230,10 @@ public class AudioHandler extends Plugin {
|
||||
* @param id The id of the audio player
|
||||
* @return position in msec
|
||||
*/
|
||||
public long getCurrentPositionAudio(String id) {
|
||||
public float getCurrentPositionAudio(String id) {
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
if (audio != null) {
|
||||
return(audio.getCurrentPosition());
|
||||
return(audio.getCurrentPosition()/1000.0f);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -228,7 +245,7 @@ public class AudioHandler extends Plugin {
|
||||
* @param file The name of the audio file.
|
||||
* @return The duration in msec.
|
||||
*/
|
||||
public long getDurationAudio(String id, String file) {
|
||||
public float getDurationAudio(String id, String file) {
|
||||
|
||||
// Get audio file
|
||||
AudioPlayer audio = this.players.get(id);
|
||||
|
@ -15,6 +15,7 @@ import android.media.MediaPlayer.OnErrorListener;
|
||||
import android.media.MediaRecorder;
|
||||
import android.media.MediaPlayer.OnCompletionListener;
|
||||
import android.media.MediaPlayer.OnPreparedListener;
|
||||
import android.os.Environment;
|
||||
|
||||
/**
|
||||
* This class implements the audio playback and recording capabilities used by PhoneGap.
|
||||
@ -53,7 +54,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
private String id; // The id of this player (used to identify Media object in JavaScript)
|
||||
private int state = MEDIA_NONE; // State of recording or playback
|
||||
private String audioFile = null; // File name to play or record to
|
||||
private long duration = -1; // Duration of audio
|
||||
private float duration = -1; // Duration of audio
|
||||
|
||||
private MediaRecorder recorder = null; // Audio recording object
|
||||
private String tempFile = null; // Temporary recording file name
|
||||
@ -70,10 +71,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
public AudioPlayer(AudioHandler handler, String id) {
|
||||
this.handler = handler;
|
||||
this.id = id;
|
||||
|
||||
// YES, I know this is bad, but I can't do it the right way because Google didn't have the
|
||||
// foresight to add android.os.environment.getExternalDataDirectory until Android 2.2
|
||||
this.tempFile = "/sdcard/tmprecording.mp3";
|
||||
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -209,7 +207,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
this.mPlayer.prepare();
|
||||
|
||||
// Get duration
|
||||
this.duration = this.mPlayer.getDuration();
|
||||
this.duration = getDurationInSeconds();
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -233,6 +231,15 @@ 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.
|
||||
*/
|
||||
@ -310,7 +317,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
* -1=can't be determined
|
||||
* -2=not allowed
|
||||
*/
|
||||
public long getDuration(String file) {
|
||||
public float getDuration(String file) {
|
||||
|
||||
// Can't get duration of recording
|
||||
if (this.recorder != null) {
|
||||
@ -353,7 +360,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
}
|
||||
|
||||
// Save off duration
|
||||
this.duration = this.mPlayer.getDuration();
|
||||
this.duration = getDurationInSeconds();
|
||||
this.prepareOnly = false;
|
||||
|
||||
// Send status notification to JavaScript
|
||||
@ -361,6 +368,15 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* By default Android returns the length of audio in mills but we want seconds
|
||||
*
|
||||
* @return length of clip in seconds
|
||||
*/
|
||||
private float getDurationInSeconds() {
|
||||
return (this.mPlayer.getDuration() / 1000.0f);
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to be invoked when there has been an error during an asynchronous operation
|
||||
* (other errors will throw exceptions at method call time).
|
||||
|
@ -11,10 +11,14 @@ import java.io.BufferedReader;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap.
|
||||
* The CallbackServer class implements an XHR server and a polling server with a list of JavaScript
|
||||
@ -41,6 +45,8 @@ import java.util.LinkedList;
|
||||
*/
|
||||
public class CallbackServer implements Runnable {
|
||||
|
||||
private static final String LOG_TAG = "CallbackServer";
|
||||
|
||||
/**
|
||||
* The list of JavaScript statements to be sent to JavaScript.
|
||||
*/
|
||||
@ -221,7 +227,11 @@ public class CallbackServer implements Runnable {
|
||||
}
|
||||
else {
|
||||
//System.out.println("CallbackServer -- sending item");
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript();
|
||||
response = "HTTP/1.1 200 OK\r\n\r\n";
|
||||
String js = this.getJavascript();
|
||||
if (js != null) {
|
||||
response += encode(js, "UTF-8");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -317,4 +327,81 @@ public class CallbackServer implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/* The Following code has been modified from original implementation of URLEncoder */
|
||||
|
||||
/* start */
|
||||
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
static final String digits = "0123456789ABCDEF";
|
||||
|
||||
/**
|
||||
* This will encode the return value to JavaScript. We revert the encoding for
|
||||
* common characters that don't require encoding to reduce the size of the string
|
||||
* being passed to JavaScript.
|
||||
*
|
||||
* @param s to be encoded
|
||||
* @param enc encoding type
|
||||
* @return encoded string
|
||||
*/
|
||||
public static String encode(String s, String enc) throws UnsupportedEncodingException {
|
||||
if (s == null || enc == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
// check for UnsupportedEncodingException
|
||||
"".getBytes(enc);
|
||||
|
||||
// Guess a bit bigger for encoded form
|
||||
StringBuilder buf = new StringBuilder(s.length() + 16);
|
||||
int start = -1;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char ch = s.charAt(i);
|
||||
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|
||||
|| (ch >= '0' && ch <= '9')
|
||||
|| " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) {
|
||||
if (start >= 0) {
|
||||
convert(s.substring(start, i), buf, enc);
|
||||
start = -1;
|
||||
}
|
||||
if (ch != ' ') {
|
||||
buf.append(ch);
|
||||
} else {
|
||||
buf.append(' ');
|
||||
}
|
||||
} else {
|
||||
if (start < 0) {
|
||||
start = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (start >= 0) {
|
||||
convert(s.substring(start, s.length()), buf, enc);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException {
|
||||
byte[] bytes = s.getBytes(enc);
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
buf.append('%');
|
||||
buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
|
||||
buf.append(digits.charAt(bytes[j] & 0xf));
|
||||
}
|
||||
}
|
||||
|
||||
/* end */
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import android.graphics.Bitmap.CompressFormat;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* This class launches the camera view, allows the user to take a picture, closes the camera view,
|
||||
@ -119,14 +120,14 @@ public class CameraLauncher extends Plugin {
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
// TODO: What if there isn't any external storage?
|
||||
File photo = new File(Environment.getExternalStorageDirectory(), "Pic.jpg");
|
||||
File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.jpg");
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA+1)*16 + returnType+1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Get image from photo library.
|
||||
*
|
||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||
@ -161,12 +162,18 @@ public class CameraLauncher extends Plugin {
|
||||
|
||||
// If CAMERA
|
||||
if (srcType == CAMERA) {
|
||||
|
||||
// If image available
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
try {
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
Bitmap bitmap;
|
||||
try {
|
||||
bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Uri uri = intent.getData();
|
||||
android.content.ContentResolver resolver = this.ctx.getContentResolver();
|
||||
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
|
||||
}
|
||||
|
||||
// If sending base64 image back
|
||||
if (destType == DATA_URL) {
|
||||
|
359
framework/src/com/phonegap/Capture.java
Normal file
359
framework/src/com/phonegap/Capture.java
Normal file
@ -0,0 +1,359 @@
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.MediaPlayer;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
public class Capture extends Plugin {
|
||||
|
||||
private static final String _DATA = "_data"; // The column name where the file path is stored
|
||||
private static final int CAPTURE_AUDIO = 0; // Constant for capture audio
|
||||
private static final int CAPTURE_IMAGE = 1; // Constant for capture image
|
||||
private static final int CAPTURE_VIDEO = 2; // Constant for capture video
|
||||
private static final String LOG_TAG = "Capture";
|
||||
private String callbackId; // The ID of the callback to be invoked with our result
|
||||
private long limit; // the number of pics/vids/clips to take
|
||||
private double duration; // optional duration parameter for video recording
|
||||
private JSONArray results; // The array of results to be returned to the user
|
||||
private Uri imageUri; // Uri of captured image
|
||||
|
||||
@Override
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
this.callbackId = callbackId;
|
||||
this.limit = 1;
|
||||
this.duration = 0.0f;
|
||||
this.results = new JSONArray();
|
||||
|
||||
JSONObject options = args.optJSONObject(0);
|
||||
if (options != null) {
|
||||
limit = options.optLong("limit", 1);
|
||||
duration = options.optDouble("duration", 0.0f);
|
||||
}
|
||||
|
||||
if (action.equals("getFormatData")) {
|
||||
try {
|
||||
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
|
||||
return new PluginResult(PluginResult.Status.OK, obj);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.ERROR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("captureAudio")) {
|
||||
this.captureAudio();
|
||||
}
|
||||
else if (action.equals("captureImage")) {
|
||||
this.captureImage();
|
||||
}
|
||||
else if (action.equals("captureVideo")) {
|
||||
this.captureVideo(duration);
|
||||
}
|
||||
|
||||
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
|
||||
r.setKeepCallback(true);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the media data file data depending on it's mime type
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param mimeType of the file
|
||||
* @return a MediaFileData object
|
||||
*/
|
||||
private JSONObject getFormatData(String filePath, String mimeType) {
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
// setup defaults
|
||||
obj.put("height", 0);
|
||||
obj.put("width", 0);
|
||||
obj.put("bitrate", 0);
|
||||
obj.put("duration", 0);
|
||||
obj.put("codecs", "");
|
||||
|
||||
// If the mimeType isn't set the rest will fail
|
||||
// so let's see if we can determine it.
|
||||
if (mimeType == null || mimeType.equals("")) {
|
||||
mimeType = FileUtils.getMimeType(filePath);
|
||||
}
|
||||
|
||||
if (mimeType.equals("image/jpeg") || filePath.endsWith(".jpg")) {
|
||||
obj = getImageData(filePath, obj);
|
||||
}
|
||||
else if (filePath.endsWith("audio/3gpp")) {
|
||||
obj = getAudioVideoData(filePath, obj, false);
|
||||
}
|
||||
else if (mimeType.equals("video/3gpp")) {
|
||||
obj = getAudioVideoData(filePath, obj, true);
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Error: setting media file data object");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
|
||||
obj.put("height", bitmap.getHeight());
|
||||
obj.put("width", bitmap.getWidth());
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Image specific attributes
|
||||
*
|
||||
* @param filePath path to the file
|
||||
* @param obj represents the Media File Data
|
||||
* @param video if true get video attributes as well
|
||||
* @return a JSONObject that represents the Media File Data
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
|
||||
MediaPlayer player = new MediaPlayer();
|
||||
try {
|
||||
player.setDataSource(filePath);
|
||||
player.prepare();
|
||||
obj.put("duration", player.getDuration());
|
||||
if (video) {
|
||||
obj.put("height", player.getVideoHeight());
|
||||
obj.put("width", player.getVideoWidth());
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
Log.d(LOG_TAG, "Error: loading video file");
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture audio. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureAudio() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture images. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureImage() {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
|
||||
|
||||
// Specify file so that large image is captured and returned
|
||||
File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Capture.jpg");
|
||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
||||
this.imageUri = Uri.fromFile(photo);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up an intent to capture video. Result handled by onActivityResult()
|
||||
*/
|
||||
private void captureVideo(double duration) {
|
||||
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
|
||||
// Introduced in API 8
|
||||
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
|
||||
|
||||
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the video view exits.
|
||||
*
|
||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||
* allowing you to identify who this result came from.
|
||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||
* @throws JSONException
|
||||
*/
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
|
||||
// Result received okay
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
// An audio clip was requested
|
||||
if (requestCode == CAPTURE_AUDIO) {
|
||||
// Get the uri of the audio clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for listening to audio
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more audio clips
|
||||
captureAudio();
|
||||
}
|
||||
} else if (requestCode == CAPTURE_IMAGE) {
|
||||
// For some reason if I try to do:
|
||||
// Uri data = intent.getData();
|
||||
// It crashes in the emulator and on my phone with a null pointer exception
|
||||
// To work around it I had to grab the code from CameraLauncher.java
|
||||
try {
|
||||
// Read in bitmap of captured image
|
||||
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
|
||||
|
||||
// Create entry in media store for image
|
||||
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
|
||||
Uri uri = null;
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
System.out.println("Can't write to external media storage.");
|
||||
try {
|
||||
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
System.out.println("Can't write to internal media storage.");
|
||||
this.fail("Error capturing image - no media storage found.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Add compressed version of captured image to returned media store Uri
|
||||
OutputStream os = this.ctx.getContentResolver().openOutputStream(uri);
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
|
||||
os.close();
|
||||
|
||||
bitmap.recycle();
|
||||
bitmap = null;
|
||||
System.gc();
|
||||
|
||||
// Add image to results
|
||||
results.put(createMediaFile(uri));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing image
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more images
|
||||
captureImage();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
this.fail("Error capturing image.");
|
||||
}
|
||||
} else if (requestCode == CAPTURE_VIDEO) {
|
||||
// Get the uri of the video clip
|
||||
Uri data = intent.getData();
|
||||
// create a file object from the uri
|
||||
results.put(createMediaFile(data));
|
||||
|
||||
if (results.length() >= limit) {
|
||||
// Send Uri back to JavaScript for viewing video
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
} else {
|
||||
// still need to capture more video clips
|
||||
captureVideo(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
// If canceled
|
||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
}
|
||||
// user canceled the action
|
||||
else {
|
||||
this.fail("Canceled.");
|
||||
}
|
||||
}
|
||||
// If something else
|
||||
else {
|
||||
// If we have partial results send them back to the user
|
||||
if (results.length() > 0) {
|
||||
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
|
||||
}
|
||||
// something bad happened
|
||||
else {
|
||||
this.fail("Did not complete!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a JSONObject that represents a File from the Uri
|
||||
*
|
||||
* @param data the Uri of the audio/image/video
|
||||
* @return a JSONObject that represents a File
|
||||
*/
|
||||
private JSONObject createMediaFile(Uri data) {
|
||||
File fp = new File(getRealPathFromURI(data));
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
// File properties
|
||||
obj.put("name", fp.getName());
|
||||
obj.put("fullPath", fp.getAbsolutePath());
|
||||
obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath()));
|
||||
obj.put("lastModifiedDate", fp.lastModified());
|
||||
obj.put("size", fp.length());
|
||||
} catch (JSONException e) {
|
||||
// this will never happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queries the media store to find out what the file path is for the Uri we supply
|
||||
*
|
||||
* @param contentUri the Uri of the audio/image/video
|
||||
* @return the full path to the file
|
||||
*/
|
||||
private String getRealPathFromURI(Uri contentUri) {
|
||||
String[] proj = { _DATA };
|
||||
Cursor cursor = this.ctx.managedQuery(contentUri, proj, null, null, null);
|
||||
int column_index = cursor.getColumnIndexOrThrow(_DATA);
|
||||
cursor.moveToFirst();
|
||||
return cursor.getString(column_index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send error message to JavaScript.
|
||||
*
|
||||
* @param err
|
||||
*/
|
||||
public void fail(String err) {
|
||||
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
|
||||
}
|
||||
}
|
@ -24,7 +24,6 @@
|
||||
|
||||
package com.phonegap;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.HashMap;
|
||||
|
||||
import android.app.Activity;
|
||||
@ -44,53 +43,9 @@ import org.json.JSONObject;
|
||||
*/
|
||||
public abstract class ContactAccessor {
|
||||
|
||||
/**
|
||||
* Static singleton instance of {@link ContactAccessor} holding the
|
||||
* SDK-specific implementation of the class.
|
||||
*/
|
||||
private static ContactAccessor sInstance;
|
||||
protected final String LOG_TAG = "ContactsAccessor";
|
||||
protected Activity mApp;
|
||||
protected WebView mView;
|
||||
|
||||
public static ContactAccessor getInstance(WebView view, Activity app) {
|
||||
if (sInstance == null) {
|
||||
String className;
|
||||
|
||||
/*
|
||||
* Check the version of the SDK we are running on. Choose an
|
||||
* implementation class designed for that version of the SDK.
|
||||
*
|
||||
* Unfortunately we have to use strings to represent the class
|
||||
* names. If we used the conventional ContactAccessorSdk5.class.getName()
|
||||
* syntax, we would get a ClassNotFoundException at runtime on pre-Eclair SDKs.
|
||||
* Using the above syntax would force Dalvik to load the class and try to
|
||||
* resolve references to all other classes it uses. Since the pre-Eclair
|
||||
* does not have those classes, the loading of ContactAccessorSdk5 would fail.
|
||||
*/
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
className = "com.phonegap.ContactAccessorSdk3_4";
|
||||
} else {
|
||||
className = "com.phonegap.ContactAccessorSdk5";
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the required class by name and instantiate it.
|
||||
*/
|
||||
try {
|
||||
Class<? extends ContactAccessor> clazz =
|
||||
Class.forName(className).asSubclass(ContactAccessor.class);
|
||||
// Grab constructor for contactsmanager class dynamically.
|
||||
Constructor<? extends ContactAccessor> classConstructor = clazz.getConstructor(Class.forName("android.webkit.WebView"), Class.forName("android.app.Activity"));
|
||||
sInstance = classConstructor.newInstance(view, app);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the data associated with the key is required to
|
||||
@ -114,51 +69,65 @@ public abstract class ContactAccessor {
|
||||
|
||||
String key;
|
||||
try {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
if (key.startsWith("displayName")) {
|
||||
map.put("displayName", true);
|
||||
}
|
||||
else if (key.startsWith("name")) {
|
||||
map.put("name", true);
|
||||
}
|
||||
else if (key.startsWith("nickname")) {
|
||||
map.put("nickname", true);
|
||||
}
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
map.put("phoneNumbers", true);
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
map.put("emails", true);
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
map.put("addresses", true);
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
map.put("ims", true);
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
map.put("organizations", true);
|
||||
}
|
||||
else if (key.startsWith("birthday")) {
|
||||
map.put("birthday", true);
|
||||
}
|
||||
else if (key.startsWith("anniversary")) {
|
||||
map.put("anniversary", true);
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
map.put("note", true);
|
||||
}
|
||||
else if (key.startsWith("relationships")) {
|
||||
map.put("relationships", true);
|
||||
}
|
||||
else if (key.startsWith("urls")) {
|
||||
map.put("urls", true);
|
||||
}
|
||||
else if (key.startsWith("photos")) {
|
||||
map.put("photos", true);
|
||||
}
|
||||
}
|
||||
if (fields.length() == 1 && fields.getString(0).equals("*")) {
|
||||
map.put("displayName", true);
|
||||
map.put("name", true);
|
||||
map.put("nickname", true);
|
||||
map.put("phoneNumbers", true);
|
||||
map.put("emails", true);
|
||||
map.put("addresses", true);
|
||||
map.put("ims", true);
|
||||
map.put("organizations", true);
|
||||
map.put("birthday", true);
|
||||
map.put("note", true);
|
||||
map.put("urls", true);
|
||||
map.put("photos", true);
|
||||
map.put("categories", true);
|
||||
}
|
||||
else {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
if (key.startsWith("displayName")) {
|
||||
map.put("displayName", true);
|
||||
}
|
||||
else if (key.startsWith("name")) {
|
||||
map.put("name", true);
|
||||
}
|
||||
else if (key.startsWith("nickname")) {
|
||||
map.put("nickname", true);
|
||||
}
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
map.put("phoneNumbers", true);
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
map.put("emails", true);
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
map.put("addresses", true);
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
map.put("ims", true);
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
map.put("organizations", true);
|
||||
}
|
||||
else if (key.startsWith("birthday")) {
|
||||
map.put("birthday", true);
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
map.put("note", true);
|
||||
}
|
||||
else if (key.startsWith("urls")) {
|
||||
map.put("urls", true);
|
||||
}
|
||||
else if (key.startsWith("photos")) {
|
||||
map.put("photos", true);
|
||||
}
|
||||
else if (key.startsWith("categories")) {
|
||||
map.put("categories", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
@ -178,11 +147,13 @@ public abstract class ContactAccessor {
|
||||
protected String getJsonString(JSONObject obj, String property) {
|
||||
String value = null;
|
||||
try {
|
||||
if (obj != null) {
|
||||
value = obj.getString(property);
|
||||
if (value.equals("null")) {
|
||||
Log.d(LOG_TAG, property + " is string called 'null'");
|
||||
value = null;
|
||||
}
|
||||
if (value.equals("null")) {
|
||||
Log.d(LOG_TAG, property + " is string called 'null'");
|
||||
value = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not get = " + e.getMessage());
|
||||
@ -194,13 +165,19 @@ public abstract class ContactAccessor {
|
||||
* Handles adding a JSON Contact object into the database.
|
||||
* @return TODO
|
||||
*/
|
||||
public abstract boolean save(JSONObject contact);
|
||||
public abstract String save(JSONObject contact);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
*/
|
||||
public abstract JSONArray search(JSONArray filter, JSONObject options);
|
||||
|
||||
/**
|
||||
* Handles searching through SDK-specific contacts API.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public abstract JSONObject getContactById(String id) throws JSONException;
|
||||
|
||||
/**
|
||||
* Handles removing a contact from the database.
|
||||
*/
|
||||
|
@ -1,827 +0,0 @@
|
||||
// Taken from Android tutorials
|
||||
/*
|
||||
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.Contacts;
|
||||
import android.provider.Contacts.ContactMethods;
|
||||
import android.provider.Contacts.ContactMethodsColumns;
|
||||
import android.provider.Contacts.Organizations;
|
||||
import android.provider.Contacts.People;
|
||||
import android.provider.Contacts.Phones;
|
||||
import android.util.Log;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* An implementation of {@link ContactAccessor} that uses legacy Contacts API.
|
||||
* These APIs are deprecated and should not be used unless we are running on a
|
||||
* pre-Eclair SDK.
|
||||
* <p>
|
||||
* There are several reasons why we wouldn't want to use this class on an Eclair device:
|
||||
* <ul>
|
||||
* <li>It would see at most one account, namely the first Google account created on the device.
|
||||
* <li>It would work through a compatibility layer, which would make it inherently less efficient.
|
||||
* <li>Not relevant to this particular example, but it would not have access to new kinds
|
||||
* of data available through current APIs.
|
||||
* </ul>
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ContactAccessorSdk3_4 extends ContactAccessor {
|
||||
private static final String PEOPLE_ID_EQUALS = "people._id = ?";
|
||||
/**
|
||||
* A static map that converts the JavaScript property name to Android database column name.
|
||||
*/
|
||||
private static final Map<String, String> dbMap = new HashMap<String, String>();
|
||||
static {
|
||||
dbMap.put("id", People._ID);
|
||||
dbMap.put("displayName", People.DISPLAY_NAME);
|
||||
dbMap.put("phoneNumbers", Phones.NUMBER);
|
||||
dbMap.put("phoneNumbers.value", Phones.NUMBER);
|
||||
dbMap.put("emails", ContactMethods.DATA);
|
||||
dbMap.put("emails.value", ContactMethods.DATA);
|
||||
dbMap.put("addresses", ContactMethodsColumns.DATA);
|
||||
dbMap.put("addresses.formatted", ContactMethodsColumns.DATA);
|
||||
dbMap.put("ims", ContactMethodsColumns.DATA);
|
||||
dbMap.put("ims.value", ContactMethodsColumns.DATA);
|
||||
dbMap.put("organizations", Organizations.COMPANY);
|
||||
dbMap.put("organizations.name", Organizations.COMPANY);
|
||||
dbMap.put("organizations.title", Organizations.TITLE);
|
||||
dbMap.put("note", People.NOTES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an contact accessor.
|
||||
*/
|
||||
public ContactAccessorSdk3_4(WebView view, Activity app)
|
||||
{
|
||||
mApp = app;
|
||||
mView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* This method takes the fields required and search options in order to produce an
|
||||
* array of contacts that matches the criteria provided.
|
||||
* @param fields an array of items to be used as search criteria
|
||||
* @param options that can be applied to contact searching
|
||||
* @return an array of contacts
|
||||
*/
|
||||
public JSONArray search(JSONArray fields, JSONObject options) {
|
||||
String searchTerm = "";
|
||||
int limit = Integer.MAX_VALUE;
|
||||
boolean multiple = true;
|
||||
|
||||
if (options != null) {
|
||||
searchTerm = options.optString("filter");
|
||||
if (searchTerm.length()==0) {
|
||||
searchTerm = "%";
|
||||
}
|
||||
else {
|
||||
searchTerm = "%" + searchTerm + "%";
|
||||
}
|
||||
try {
|
||||
multiple = options.getBoolean("multiple");
|
||||
if (!multiple) {
|
||||
limit = 1;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
// Multiple was not specified so we assume the default is true.
|
||||
}
|
||||
}
|
||||
else {
|
||||
searchTerm = "%";
|
||||
}
|
||||
|
||||
ContentResolver cr = mApp.getContentResolver();
|
||||
|
||||
Set<String> contactIds = buildSetOfContactIds(fields, searchTerm);
|
||||
HashMap<String,Boolean> populate = buildPopulationSet(fields);
|
||||
|
||||
Iterator<String> it = contactIds.iterator();
|
||||
|
||||
JSONArray contacts = new JSONArray();
|
||||
JSONObject contact;
|
||||
String contactId;
|
||||
int pos = 0;
|
||||
while (it.hasNext() && (pos < limit)) {
|
||||
contact = new JSONObject();
|
||||
try {
|
||||
contactId = it.next();
|
||||
contact.put("id", contactId);
|
||||
|
||||
// Do query for name and note
|
||||
Cursor cur = cr.query(People.CONTENT_URI,
|
||||
new String[] {People.DISPLAY_NAME, People.NOTES},
|
||||
PEOPLE_ID_EQUALS,
|
||||
new String[] {contactId},
|
||||
null);
|
||||
cur.moveToFirst();
|
||||
|
||||
if (isRequired("displayName",populate)) {
|
||||
contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME)));
|
||||
}
|
||||
if (isRequired("phoneNumbers",populate)) {
|
||||
contact.put("phoneNumbers", phoneQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("emails",populate)) {
|
||||
contact.put("emails", emailQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("addresses",populate)) {
|
||||
contact.put("addresses", addressQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("organizations",populate)) {
|
||||
contact.put("organizations", organizationQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("ims",populate)) {
|
||||
contact.put("ims", imQuery(cr, contactId));
|
||||
}
|
||||
if (isRequired("note",populate)) {
|
||||
contact.put("note", cur.getString(cur.getColumnIndex(People.NOTES)));
|
||||
}
|
||||
// nickname
|
||||
// urls
|
||||
// relationship
|
||||
// birthdays
|
||||
// anniversary
|
||||
|
||||
pos++;
|
||||
cur.close();
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
contacts.put(contact);
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the database using the search term to build up a list of contact ID's
|
||||
* matching the search term
|
||||
* @param fields
|
||||
* @param searchTerm
|
||||
* @return a set of contact ID's
|
||||
*/
|
||||
private Set<String> buildSetOfContactIds(JSONArray fields, String searchTerm) {
|
||||
Set<String> contactIds = new HashSet<String>();
|
||||
|
||||
String key;
|
||||
try {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
if (key.startsWith("displayName")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
People.CONTENT_URI,
|
||||
People._ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
// else if (key.startsWith("name")) {
|
||||
// Log.d(LOG_TAG, "Doing " + key + " query");
|
||||
// doQuery(searchTerm, contactIds,
|
||||
// ContactsContract.Data.CONTENT_URI,
|
||||
// ContactsContract.Data.CONTACT_ID,
|
||||
// dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?",
|
||||
// new String[] {searchTerm, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE});
|
||||
// }
|
||||
else if (key.startsWith("phoneNumbers")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
Phones.CONTENT_URI,
|
||||
Phones.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
else if (key.startsWith("emails")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
ContactMethods.CONTENT_EMAIL_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
|
||||
new String[] {searchTerm, ContactMethods.CONTENT_EMAIL_ITEM_TYPE});
|
||||
}
|
||||
else if (key.startsWith("addresses")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
ContactMethods.CONTENT_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
|
||||
new String[] {searchTerm, ContactMethods.CONTENT_POSTAL_ITEM_TYPE});
|
||||
}
|
||||
else if (key.startsWith("ims")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
ContactMethods.CONTENT_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
|
||||
new String[] {searchTerm, ContactMethods.CONTENT_IM_ITEM_TYPE});
|
||||
}
|
||||
else if (key.startsWith("organizations")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
Organizations.CONTENT_URI,
|
||||
ContactMethods.PERSON_ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
else if (key.startsWith("note")) {
|
||||
doQuery(searchTerm, contactIds,
|
||||
People.CONTENT_URI,
|
||||
People._ID,
|
||||
dbMap.get(key) + " LIKE ?",
|
||||
new String[] {searchTerm});
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
|
||||
return contactIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method so we don't duplicate code in doQuery
|
||||
* @param searchTerm
|
||||
* @param contactIds
|
||||
* @param uri
|
||||
* @param projection
|
||||
* @param selection
|
||||
* @param selectionArgs
|
||||
*/
|
||||
private void doQuery(String searchTerm, Set<String> contactIds,
|
||||
Uri uri, String projection, String selection, String[] selectionArgs) {
|
||||
ContentResolver cr = mApp.getContentResolver();
|
||||
|
||||
Cursor cursor = cr.query(
|
||||
uri,
|
||||
null,
|
||||
selection,
|
||||
selectionArgs,
|
||||
null);
|
||||
|
||||
while (cursor.moveToNext()) {
|
||||
contactIds.add(cursor.getString(cursor.getColumnIndex(projection)));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactFields
|
||||
*/
|
||||
private JSONArray imQuery(ContentResolver cr, String contactId) {
|
||||
String imWhere = ContactMethods.PERSON_ID
|
||||
+ " = ? AND " + ContactMethods.KIND + " = ?";
|
||||
String[] imWhereParams = new String[]{contactId, ContactMethods.CONTENT_IM_ITEM_TYPE};
|
||||
Cursor cursor = cr.query(ContactMethods.CONTENT_URI,
|
||||
null, imWhere, imWhereParams, null);
|
||||
JSONArray ims = new JSONArray();
|
||||
JSONObject im;
|
||||
while (cursor.moveToNext()) {
|
||||
im = new JSONObject();
|
||||
try{
|
||||
im.put("id", cursor.getString(
|
||||
cursor.getColumnIndex(ContactMethods._ID)));
|
||||
im.put("perf", false);
|
||||
im.put("value", cursor.getString(
|
||||
cursor.getColumnIndex(ContactMethodsColumns.DATA)));
|
||||
im.put("type", getContactType(cursor.getInt(
|
||||
cursor.getColumnIndex(ContactMethodsColumns.TYPE))));
|
||||
ims.put(im);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
cursor.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactOrganization JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactOrganization
|
||||
*/
|
||||
private JSONArray organizationQuery(ContentResolver cr, String contactId) {
|
||||
String orgWhere = ContactMethods.PERSON_ID + " = ?";
|
||||
String[] orgWhereParams = new String[]{contactId};
|
||||
Cursor cursor = cr.query(Organizations.CONTENT_URI,
|
||||
null, orgWhere, orgWhereParams, null);
|
||||
JSONArray organizations = new JSONArray();
|
||||
JSONObject organization;
|
||||
while (cursor.moveToNext()) {
|
||||
organization = new JSONObject();
|
||||
try{
|
||||
organization.put("id", cursor.getString(cursor.getColumnIndex(Organizations._ID)));
|
||||
organization.put("name", cursor.getString(cursor.getColumnIndex(Organizations.COMPANY)));
|
||||
organization.put("title", cursor.getString(cursor.getColumnIndex(Organizations.TITLE)));
|
||||
// organization.put("department", cursor.getString(cursor.getColumnIndex(Organizations)));
|
||||
organizations.put(organization);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return organizations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactAddress JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactAddress
|
||||
*/
|
||||
private JSONArray addressQuery(ContentResolver cr, String contactId) {
|
||||
String addrWhere = ContactMethods.PERSON_ID
|
||||
+ " = ? AND " + ContactMethods.KIND + " = ?";
|
||||
String[] addrWhereParams = new String[]{contactId,
|
||||
ContactMethods.CONTENT_POSTAL_ITEM_TYPE};
|
||||
Cursor cursor = cr.query(ContactMethods.CONTENT_URI,
|
||||
null, addrWhere, addrWhereParams, null);
|
||||
JSONArray addresses = new JSONArray();
|
||||
JSONObject address;
|
||||
while (cursor.moveToNext()) {
|
||||
address = new JSONObject();
|
||||
try{
|
||||
address.put("id", cursor.getString(cursor.getColumnIndex(ContactMethods._ID)));
|
||||
address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactMethodsColumns.DATA)));
|
||||
addresses.put(address);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactFields
|
||||
*/
|
||||
private JSONArray phoneQuery(ContentResolver cr, String contactId) {
|
||||
Cursor cursor = cr.query(
|
||||
Phones.CONTENT_URI,
|
||||
null,
|
||||
Phones.PERSON_ID +" = ?",
|
||||
new String[]{contactId}, null);
|
||||
JSONArray phones = new JSONArray();
|
||||
JSONObject phone;
|
||||
while (cursor.moveToNext()) {
|
||||
phone = new JSONObject();
|
||||
try{
|
||||
phone.put("id", cursor.getString(cursor.getColumnIndex(Phones._ID)));
|
||||
phone.put("perf", false);
|
||||
phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER)));
|
||||
phone.put("type", getPhoneType(cursor.getInt(cursor.getColumnIndex(Phones.TYPE))));
|
||||
phones.put(phone);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return phones;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactField JSONArray
|
||||
* @param cr database access object
|
||||
* @param contactId the ID to search the database for
|
||||
* @return a JSONArray representing a set of ContactFields
|
||||
*/
|
||||
private JSONArray emailQuery(ContentResolver cr, String contactId) {
|
||||
Cursor cursor = cr.query(
|
||||
ContactMethods.CONTENT_EMAIL_URI,
|
||||
null,
|
||||
ContactMethods.PERSON_ID +" = ?",
|
||||
new String[]{contactId}, null);
|
||||
JSONArray emails = new JSONArray();
|
||||
JSONObject email;
|
||||
while (cursor.moveToNext()) {
|
||||
email = new JSONObject();
|
||||
try{
|
||||
email.put("id", cursor.getString(cursor.getColumnIndex(ContactMethods._ID)));
|
||||
email.put("perf", false);
|
||||
email.put("value", cursor.getString(cursor.getColumnIndex(ContactMethods.DATA)));
|
||||
// TODO Find out why adding an email type throws and exception
|
||||
//email.put("type", cursor.getString(cursor.getColumnIndex(ContactMethods.TYPE)));
|
||||
emails.put(email);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
return emails;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will save a contact object into the devices contacts database.
|
||||
*
|
||||
* @param contact the contact to be saved.
|
||||
* @returns true if the contact is successfully saved, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean save(JSONObject contact) {
|
||||
ContentValues personValues = new ContentValues();
|
||||
|
||||
String id = getJsonString(contact, "id");
|
||||
|
||||
String name = getJsonString(contact, "displayName");
|
||||
if (name != null) {
|
||||
personValues.put(Contacts.People.NAME, name);
|
||||
}
|
||||
String note = getJsonString(contact, "note");
|
||||
if (note != null) {
|
||||
personValues.put(Contacts.People.NOTES, note);
|
||||
}
|
||||
|
||||
/* STARRED 0 = Contacts, 1 = Favorites */
|
||||
personValues.put(Contacts.People.STARRED, 0);
|
||||
|
||||
Uri newPersonUri;
|
||||
// Add new contact
|
||||
if (id == null) {
|
||||
newPersonUri = Contacts.People.createPersonInMyContactsGroup(mApp.getContentResolver(), personValues);
|
||||
}
|
||||
// modify existing contact
|
||||
else {
|
||||
newPersonUri = Uri.withAppendedPath(Contacts.People.CONTENT_URI, id);
|
||||
mApp.getContentResolver().update(newPersonUri, personValues, PEOPLE_ID_EQUALS, new String[]{id});
|
||||
}
|
||||
|
||||
if (newPersonUri != null) {
|
||||
// phoneNumbers
|
||||
savePhoneNumbers(contact, newPersonUri);
|
||||
// emails
|
||||
saveEntries(contact, newPersonUri, "emails", Contacts.KIND_EMAIL);
|
||||
// addresses
|
||||
saveAddresses(contact, newPersonUri);
|
||||
// organizations
|
||||
saveOrganizations(contact, newPersonUri);
|
||||
// ims
|
||||
saveEntries(contact, newPersonUri, "ims", Contacts.KIND_IM);
|
||||
|
||||
// Successfully create a Contact
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a JSON contact object and loops through the available organizations. If the
|
||||
* organization has an id that is not equal to null the organization will be updated in the database.
|
||||
* If the id is null then we treat it as a new organization.
|
||||
*
|
||||
* @param contact the contact to extract the organizations from
|
||||
* @param uri the base URI for this contact.
|
||||
*/
|
||||
private void saveOrganizations(JSONObject contact, Uri newPersonUri) {
|
||||
ContentValues values = new ContentValues();
|
||||
Uri orgUri = Uri.withAppendedPath(newPersonUri,
|
||||
Contacts.Organizations.CONTENT_DIRECTORY);
|
||||
String id = null;
|
||||
try {
|
||||
JSONArray orgs = contact.getJSONArray("organizations");
|
||||
if (orgs != null && orgs.length() > 0) {
|
||||
JSONObject org;
|
||||
for (int i=0; i<orgs.length(); i++) {
|
||||
org = orgs.getJSONObject(i);
|
||||
id = getJsonString(org, "id");
|
||||
values.put(Contacts.Organizations.COMPANY, getJsonString(org, "name"));
|
||||
values.put(Contacts.Organizations.TITLE, getJsonString(org, "title"));
|
||||
if (id == null) {
|
||||
Uri contactUpdate = mApp.getContentResolver().insert(orgUri, values);
|
||||
}
|
||||
else {
|
||||
Uri tempUri = Uri.withAppendedPath(orgUri, id);
|
||||
mApp.getContentResolver().update(tempUri, values, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not save organizations = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a JSON contact object and loops through the available addresses. If the
|
||||
* address has an id that is not equal to null the address will be updated in the database.
|
||||
* If the id is null then we treat it as a new address.
|
||||
*
|
||||
* @param contact the contact to extract the addresses from
|
||||
* @param uri the base URI for this contact.
|
||||
*/
|
||||
private void saveAddresses(JSONObject contact, Uri uri) {
|
||||
ContentValues values = new ContentValues();
|
||||
Uri newUri = Uri.withAppendedPath(uri,
|
||||
Contacts.People.ContactMethods.CONTENT_DIRECTORY);
|
||||
String id = null;
|
||||
try {
|
||||
JSONArray entries = contact.getJSONArray("addresses");
|
||||
if (entries != null && entries.length() > 0) {
|
||||
JSONObject entry;
|
||||
values.put(Contacts.ContactMethods.KIND, Contacts.KIND_POSTAL);
|
||||
for (int i=0; i<entries.length(); i++) {
|
||||
entry = entries.getJSONObject(i);
|
||||
id = getJsonString(entry, "id");
|
||||
|
||||
String address = getJsonString(entry, "formatted");
|
||||
if (address != null) {
|
||||
values.put(Contacts.ContactMethods.DATA, address);
|
||||
}
|
||||
else {
|
||||
values.put(Contacts.ContactMethods.DATA, createAddressString(entry));
|
||||
}
|
||||
|
||||
if (id == null) {
|
||||
Uri contactUpdate = mApp.getContentResolver().insert(newUri, values);
|
||||
}
|
||||
else {
|
||||
Uri tempUri = Uri.withAppendedPath(newUri, id);
|
||||
mApp.getContentResolver().update(tempUri, values, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not save address = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a ContactAddress JSON object and creates a fully
|
||||
* formatted address string.
|
||||
*
|
||||
* @param entry the full address object
|
||||
* @return a formatted address string
|
||||
*/
|
||||
private String createAddressString(JSONObject entry) {
|
||||
StringBuffer buffer = new StringBuffer("");
|
||||
if (getJsonString(entry, "locality") != null ) {
|
||||
buffer.append(getJsonString(entry, "locality"));
|
||||
}
|
||||
if (getJsonString(entry, "region") != null ) {
|
||||
if (buffer.length() > 0 ) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
buffer.append(getJsonString(entry, "region"));
|
||||
}
|
||||
if (getJsonString(entry, "postalCode") != null ) {
|
||||
if (buffer.length() > 0 ) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
buffer.append(getJsonString(entry, "postalCode"));
|
||||
}
|
||||
if (getJsonString(entry, "country") != null ) {
|
||||
if (buffer.length() > 0 ) {
|
||||
buffer.append(", ");
|
||||
}
|
||||
buffer.append(getJsonString(entry, "country"));
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a JSON contact object and loops through the available entries (Emails/IM's). If the
|
||||
* entry has an id that is not equal to null the entry will be updated in the database.
|
||||
* If the id is null then we treat it as a new entry.
|
||||
*
|
||||
* @param contact the contact to extract the entries from
|
||||
* @param uri the base URI for this contact.
|
||||
*/
|
||||
private void saveEntries(JSONObject contact, Uri uri, String dataType, int contactKind) {
|
||||
ContentValues values = new ContentValues();
|
||||
Uri newUri = Uri.withAppendedPath(uri,
|
||||
Contacts.People.ContactMethods.CONTENT_DIRECTORY);
|
||||
String id = null;
|
||||
|
||||
try {
|
||||
JSONArray entries = contact.getJSONArray(dataType);
|
||||
if (entries != null && entries.length() > 0) {
|
||||
JSONObject entry;
|
||||
values.put(Contacts.ContactMethods.KIND, contactKind);
|
||||
for (int i=0; i<entries.length(); i++) {
|
||||
entry = entries.getJSONObject(i);
|
||||
id = getJsonString(entry, "id");
|
||||
values.put(Contacts.ContactMethods.DATA, getJsonString(entry, "value"));
|
||||
values.put(Contacts.ContactMethods.TYPE, getContactType(getJsonString(entry, "type")));
|
||||
if (id==null) {
|
||||
Uri contactUpdate = mApp.getContentResolver().insert(newUri, values);
|
||||
}
|
||||
else {
|
||||
Uri tempUri = Uri.withAppendedPath(newUri, id);
|
||||
mApp.getContentResolver().update(tempUri, values, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not save " + dataType + " = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getContactType(String string) {
|
||||
int type = Contacts.ContactMethods.TYPE_OTHER;
|
||||
if (string!=null) {
|
||||
if ("home".equals(string.toLowerCase())) {
|
||||
return Contacts.ContactMethods.TYPE_HOME;
|
||||
}
|
||||
else if ("work".equals(string.toLowerCase())) {
|
||||
return Contacts.ContactMethods.TYPE_WORK;
|
||||
}
|
||||
else if ("other".equals(string.toLowerCase())) {
|
||||
return Contacts.ContactMethods.TYPE_OTHER;
|
||||
}
|
||||
else if ("custom".equals(string.toLowerCase())) {
|
||||
return Contacts.ContactMethods.TYPE_CUSTOM;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getContactType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case Contacts.ContactMethods.TYPE_CUSTOM:
|
||||
stringType = "custom";
|
||||
break;
|
||||
case Contacts.ContactMethods.TYPE_HOME:
|
||||
stringType = "home";
|
||||
break;
|
||||
case Contacts.ContactMethods.TYPE_WORK:
|
||||
stringType = "work";
|
||||
break;
|
||||
case Contacts.ContactMethods.TYPE_OTHER:
|
||||
default:
|
||||
stringType = "other";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a JSON contact object and loops through the available phone numbers. If the phone
|
||||
* number has an id that is not equal to null the phone number will be updated in the database.
|
||||
* If the id is null then we treat it as a new phone number.
|
||||
*
|
||||
* @param contact the contact to extract the phone numbers from
|
||||
* @param uri the base URI for this contact.
|
||||
*/
|
||||
private void savePhoneNumbers(JSONObject contact, Uri uri) {
|
||||
ContentValues values = new ContentValues();
|
||||
Uri phonesUri = Uri.withAppendedPath(uri,
|
||||
Contacts.People.Phones.CONTENT_DIRECTORY);
|
||||
String id = null;
|
||||
|
||||
try {
|
||||
JSONArray phones = contact.getJSONArray("phoneNumbers");
|
||||
if (phones != null && phones.length() > 0) {
|
||||
JSONObject phone;
|
||||
for (int i=0; i<phones.length(); i++) {
|
||||
phone = phones.getJSONObject(i);
|
||||
id = getJsonString(phone, "id");
|
||||
values.put(Contacts.Phones.NUMBER, getJsonString(phone, "value"));
|
||||
values.put(Contacts.Phones.TYPE, getPhoneType(getJsonString(phone, "type")));
|
||||
if (id==null) {
|
||||
Uri phoneUpdate = mApp.getContentResolver().insert(phonesUri, values);
|
||||
}
|
||||
else {
|
||||
Uri newUri = Uri.withAppendedPath(phonesUri, id);
|
||||
mApp.getContentResolver().update(newUri, values, null, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (JSONException e) {
|
||||
Log.d(LOG_TAG, "Could not save phones = " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getPhoneType(String string) {
|
||||
int type = Contacts.Phones.TYPE_OTHER;
|
||||
if ("home".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_HOME;
|
||||
}
|
||||
else if ("mobile".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_MOBILE;
|
||||
}
|
||||
else if ("work".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_WORK;
|
||||
}
|
||||
else if ("work fax".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_FAX_WORK;
|
||||
}
|
||||
else if ("home fax".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_FAX_HOME;
|
||||
}
|
||||
else if ("fax".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_FAX_WORK;
|
||||
}
|
||||
else if ("pager".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_PAGER;
|
||||
}
|
||||
else if ("other".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_OTHER;
|
||||
}
|
||||
else if ("custom".equals(string.toLowerCase())) {
|
||||
return Contacts.Phones.TYPE_CUSTOM;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getPhoneType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case Contacts.Phones.TYPE_CUSTOM:
|
||||
stringType = "custom";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_FAX_HOME:
|
||||
stringType = "home fax";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_FAX_WORK:
|
||||
stringType = "work fax";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_HOME:
|
||||
stringType = "home";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_MOBILE:
|
||||
stringType = "mobile";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_PAGER:
|
||||
stringType = "pager";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_WORK:
|
||||
stringType = "work";
|
||||
break;
|
||||
case Contacts.Phones.TYPE_OTHER:
|
||||
default:
|
||||
stringType = "custom";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* This method will remove a Contact from the database based on ID.
|
||||
* @param id the unique ID of the contact to remove
|
||||
*/
|
||||
public boolean remove(String id) {
|
||||
int result = mApp.getContentResolver().delete(People.CONTENT_URI,
|
||||
PEOPLE_ID_EQUALS,
|
||||
new String[] {id});
|
||||
|
||||
return (result > 0) ? true : false;
|
||||
}
|
||||
}
|
@ -46,6 +46,7 @@ import android.accounts.Account;
|
||||
import android.accounts.AccountManager;
|
||||
import android.app.Activity;
|
||||
import android.content.ContentProviderOperation;
|
||||
import android.content.ContentProviderResult;
|
||||
import android.content.ContentUris;
|
||||
import android.content.ContentValues;
|
||||
import android.content.OperationApplicationException;
|
||||
@ -88,7 +89,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
*/
|
||||
private static final Map<String, String> dbMap = new HashMap<String, String>();
|
||||
static {
|
||||
dbMap.put("id", ContactsContract.Contacts._ID);
|
||||
dbMap.put("id", ContactsContract.Data.CONTACT_ID);
|
||||
dbMap.put("displayName", ContactsContract.Contacts.DISPLAY_NAME);
|
||||
dbMap.put("name", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
|
||||
dbMap.put("name.formatted", ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
|
||||
@ -115,14 +116,12 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
dbMap.put("organizations.name", ContactsContract.CommonDataKinds.Organization.COMPANY);
|
||||
dbMap.put("organizations.department", ContactsContract.CommonDataKinds.Organization.DEPARTMENT);
|
||||
dbMap.put("organizations.title", ContactsContract.CommonDataKinds.Organization.TITLE);
|
||||
//dbMap.put("revision", null);
|
||||
dbMap.put("birthday", ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE);
|
||||
dbMap.put("note", ContactsContract.CommonDataKinds.Note.NOTE);
|
||||
dbMap.put("photos.value", ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
|
||||
//dbMap.put("categories.value", null);
|
||||
dbMap.put("urls", ContactsContract.CommonDataKinds.Website.URL);
|
||||
dbMap.put("urls.value", ContactsContract.CommonDataKinds.Website.URL);
|
||||
//dbMap.put("timezone", null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,9 +141,6 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
*/
|
||||
@Override
|
||||
public JSONArray search(JSONArray fields, JSONObject options) {
|
||||
long totalEnd;
|
||||
long totalStart = System.currentTimeMillis();
|
||||
|
||||
// Get the find options
|
||||
String searchTerm = "";
|
||||
int limit = Integer.MAX_VALUE;
|
||||
@ -180,7 +176,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
|
||||
// Build the ugly where clause and where arguments for one big query.
|
||||
WhereOptions whereOptions = buildWhereClause(fields, searchTerm);
|
||||
|
||||
|
||||
// Get all the id's where the search term matches the fields passed in.
|
||||
Cursor idCursor = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
new String[] { ContactsContract.Data.CONTACT_ID },
|
||||
@ -206,8 +202,49 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
idOptions.getWhereArgs(),
|
||||
ContactsContract.Data.CONTACT_ID + " ASC");
|
||||
|
||||
|
||||
//Log.d(LOG_TAG, "Cursor length = " + c.getCount());
|
||||
JSONArray contacts = populateContactArray(limit, populate, c);
|
||||
return contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* A special search that finds one contact by id
|
||||
*
|
||||
* @param id contact to find by id
|
||||
* @return a JSONObject representing the contact
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONObject getContactById(String id) throws JSONException {
|
||||
// Do the id query
|
||||
Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
|
||||
null,
|
||||
ContactsContract.Data.CONTACT_ID + " = ? ",
|
||||
new String[] { id },
|
||||
ContactsContract.Data.CONTACT_ID + " ASC");
|
||||
|
||||
JSONArray fields = new JSONArray();
|
||||
fields.put("*");
|
||||
|
||||
HashMap<String,Boolean> populate = buildPopulationSet(fields);
|
||||
|
||||
JSONArray contacts = populateContactArray(1, populate, c);
|
||||
|
||||
if (contacts.length() == 1) {
|
||||
return contacts.getJSONObject(0);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an array of contacts from the cursor you pass in
|
||||
*
|
||||
* @param limit max number of contacts for the array
|
||||
* @param populate whether or not you should populate a certain value
|
||||
* @param c the cursor
|
||||
* @return a JSONArray of contacts
|
||||
*/
|
||||
private JSONArray populateContactArray(int limit,
|
||||
HashMap<String, Boolean> populate, Cursor c) {
|
||||
|
||||
String contactId = "";
|
||||
String rawId = "";
|
||||
@ -264,16 +301,18 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
newContact = false;
|
||||
contact.put("id", contactId);
|
||||
contact.put("rawId", rawId);
|
||||
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)));
|
||||
}
|
||||
|
||||
// Grab the mimetype of the current row as it will be used in a lot of comparisons
|
||||
mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE));
|
||||
|
||||
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("name",populate)) {
|
||||
contact.put("name", nameQuery(c));
|
||||
}
|
||||
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
|
||||
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)));
|
||||
}
|
||||
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("name",populate)) {
|
||||
contact.put("name", nameQuery(c));
|
||||
}
|
||||
else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
|
||||
&& isRequired("phoneNumbers",populate)) {
|
||||
phones.put(phoneQuery(c));
|
||||
@ -332,12 +371,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
}
|
||||
}
|
||||
c.close();
|
||||
|
||||
|
||||
totalEnd = System.currentTimeMillis();
|
||||
Log.d(LOG_TAG,"Total time = " + (totalEnd-totalStart));
|
||||
return contacts;
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a where clause all all the ids passed into the method
|
||||
@ -416,15 +451,67 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
ArrayList<String> whereArgs = new ArrayList<String>();
|
||||
|
||||
WhereOptions options = new WhereOptions();
|
||||
|
||||
/*
|
||||
* Special case where the user wants all fields returned
|
||||
*/
|
||||
if (isWildCardSearch(fields)) {
|
||||
// Get all contacts with all properties
|
||||
if ("%".equals(searchTerm)) {
|
||||
options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )");
|
||||
options.setWhereArgs(new String[] {searchTerm});
|
||||
return options;
|
||||
} else {
|
||||
// Get all contacts that match the filter but return all properties
|
||||
where.add("(" + dbMap.get("displayName") + " LIKE ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
where.add("(" + dbMap.get("name") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("nickname") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("phoneNumbers") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("emails") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("addresses") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("ims") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("organizations") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("note") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
|
||||
where.add("(" + dbMap.get("urls") + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Special case for when the user wants all the contacts
|
||||
* Special case for when the user wants all the contacts but
|
||||
*/
|
||||
if ("%".equals(searchTerm)) {
|
||||
options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )");
|
||||
options.setWhereArgs(new String[] {searchTerm});
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
String key;
|
||||
try {
|
||||
@ -432,10 +519,14 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
for (int i=0; i<fields.length(); i++) {
|
||||
key = fields.getString(i);
|
||||
|
||||
if (key.startsWith("displayName")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
}
|
||||
if (key.equals("id")) {
|
||||
where.add("(" + dbMap.get(key) + " = ? )");
|
||||
whereArgs.add(searchTerm.substring(1, searchTerm.length()-1));
|
||||
}
|
||||
else if (key.startsWith("displayName")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? )");
|
||||
whereArgs.add(searchTerm);
|
||||
}
|
||||
else if (key.startsWith("name")) {
|
||||
where.add("(" + dbMap.get(key) + " LIKE ? AND "
|
||||
+ ContactsContract.Data.MIMETYPE + " = ? )");
|
||||
@ -521,6 +612,26 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* If the user passes in the '*' wildcard character for search then they want all fields for each contact
|
||||
*
|
||||
* @param fields
|
||||
* @return true if wildcard search requested, false otherwise
|
||||
*/
|
||||
private boolean isWildCardSearch(JSONArray fields) {
|
||||
// Only do a wildcard search if we are passed ["*"]
|
||||
if (fields.length() == 1) {
|
||||
try {
|
||||
if ("*".equals(fields.getString(0))) {
|
||||
return true;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ContactOrganization JSONObject
|
||||
* @param cursor the current database row
|
||||
* @return a JSONObject representing a ContactOrganization
|
||||
@ -529,6 +640,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
JSONObject organization = new JSONObject();
|
||||
try {
|
||||
organization.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization._ID)));
|
||||
organization.put("pref", false); // Android does not store pref attribute
|
||||
organization.put("type", getOrgType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TYPE))));
|
||||
organization.put("department", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.DEPARTMENT)));
|
||||
organization.put("name", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY)));
|
||||
organization.put("title", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TITLE)));
|
||||
@ -547,6 +660,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
JSONObject address = new JSONObject();
|
||||
try {
|
||||
address.put("id", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal._ID)));
|
||||
address.put("pref", false); // Android does not store pref attribute
|
||||
address.put("type", getAddressType(cursor.getInt(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Organization.TYPE))));
|
||||
address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS)));
|
||||
address.put("streetAddress", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.STREET)));
|
||||
address.put("locality", cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.StructuredPostal.CITY)));
|
||||
@ -693,9 +808,9 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
* This method will save a contact object into the devices contacts database.
|
||||
*
|
||||
* @param contact the contact to be saved.
|
||||
* @returns true if the contact is successfully saved, false otherwise.
|
||||
* @returns the id if the contact is successfully saved, null otherwise.
|
||||
*/
|
||||
public boolean save(JSONObject contact) {
|
||||
public String save(JSONObject contact) {
|
||||
AccountManager mgr = AccountManager.get(mApp);
|
||||
Account[] accounts = mgr.getAccounts();
|
||||
Account account = null;
|
||||
@ -730,8 +845,9 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
}
|
||||
}
|
||||
|
||||
if(account == null)
|
||||
return false;
|
||||
if(account == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String id = getJsonString(contact, "id");
|
||||
// Create new contact
|
||||
@ -751,7 +867,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
* @param contact the contact to be saved
|
||||
* @param account the account to be saved under
|
||||
*/
|
||||
private boolean modifyContact(String id, JSONObject contact, Account account) {
|
||||
private String modifyContact(String id, JSONObject contact, Account account) {
|
||||
// Get the RAW_CONTACT_ID which is needed to insert new values in an already existing contact.
|
||||
// But not needed to update existing values.
|
||||
int rawId = (new Integer(getJsonString(contact,"rawId"))).intValue();
|
||||
@ -892,6 +1008,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
|
||||
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
|
||||
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"));
|
||||
@ -908,6 +1025,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
.withSelection(ContactsContract.CommonDataKinds.StructuredPostal._ID + "=? AND " +
|
||||
ContactsContract.Data.MIMETYPE + "=?",
|
||||
new String[]{addressId, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE})
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")))
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"))
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"))
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"))
|
||||
@ -936,6 +1054,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
ContentValues contentValues = new ContentValues();
|
||||
contentValues.put(ContactsContract.Data.RAW_CONTACT_ID, rawId);
|
||||
contentValues.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"));
|
||||
contentValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"));
|
||||
@ -949,6 +1068,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
.withSelection(ContactsContract.CommonDataKinds.Organization._ID + "=? AND " +
|
||||
ContactsContract.Data.MIMETYPE + "=?",
|
||||
new String[]{orgId, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE})
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"))
|
||||
@ -1107,7 +1227,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
|
||||
//Modify contact
|
||||
try {
|
||||
mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
Log.e(LOG_TAG, Log.getStackTraceString(e), e);
|
||||
@ -1118,7 +1238,12 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
retVal = false;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
// if the save was a succes return the contact ID
|
||||
if (retVal) {
|
||||
return id;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1163,7 +1288,8 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.TYPE, getOrgType(getJsonString(org, "type")))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.DEPARTMENT, getJsonString(org, "department"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.COMPANY, getJsonString(org, "name"))
|
||||
.withValue(ContactsContract.CommonDataKinds.Organization.TITLE, getJsonString(org, "title"))
|
||||
.build());
|
||||
@ -1180,6 +1306,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
|
||||
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
|
||||
.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.TYPE, getAddressType(getJsonString(address, "type")))
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS, getJsonString(address, "formatted"))
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.STREET, getJsonString(address, "streetAddress"))
|
||||
.withValue(ContactsContract.CommonDataKinds.StructuredPostal.CITY, getJsonString(address, "locality"))
|
||||
@ -1294,7 +1421,7 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
* @param contact the contact to be saved
|
||||
* @param account the account to be saved under
|
||||
*/
|
||||
private boolean createNewContact(JSONObject contact, Account account) {
|
||||
private String createNewContact(JSONObject contact, Account account) {
|
||||
// Create a list of attributes to add to the contact database
|
||||
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
|
||||
|
||||
@ -1420,7 +1547,6 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
// Add urls
|
||||
JSONArray websites = null;
|
||||
try {
|
||||
@ -1462,19 +1588,21 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
Log.d(LOG_TAG, "Could not get photos");
|
||||
}
|
||||
|
||||
boolean retVal = true;
|
||||
String newId = null;
|
||||
//Add contact
|
||||
try {
|
||||
mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
ContentProviderResult[] cpResults = mApp.getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
|
||||
if (cpResults.length >= 0) {
|
||||
newId = cpResults[0].uri.getLastPathSegment();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
retVal = false;
|
||||
newId = null;
|
||||
} catch (OperationApplicationException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
retVal = false;
|
||||
newId = null;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
return newId;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1645,58 +1773,144 @@ public class ContactAccessorSdk5 extends ContactAccessor {
|
||||
return stringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getContactType(String string) {
|
||||
int type = ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
|
||||
if (string!=null) {
|
||||
if ("home".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_HOME;
|
||||
}
|
||||
else if ("work".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_WORK;
|
||||
}
|
||||
else if ("other".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
|
||||
}
|
||||
else if ("mobile".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_MOBILE;
|
||||
}
|
||||
else if ("custom".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getContactType(String string) {
|
||||
int type = ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
|
||||
if (string!=null) {
|
||||
if ("home".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_HOME;
|
||||
}
|
||||
else if ("work".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_WORK;
|
||||
}
|
||||
else if ("other".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_OTHER;
|
||||
}
|
||||
else if ("mobile".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_MOBILE;
|
||||
}
|
||||
else if ("custom".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getContactType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM:
|
||||
stringType = "custom";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_HOME:
|
||||
stringType = "home";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_WORK:
|
||||
stringType = "work";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE:
|
||||
stringType = "mobile";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_OTHER:
|
||||
default:
|
||||
stringType = "other";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getContactType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_CUSTOM:
|
||||
stringType = "custom";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_HOME:
|
||||
stringType = "home";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_WORK:
|
||||
stringType = "work";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_MOBILE:
|
||||
stringType = "mobile";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Email.TYPE_OTHER:
|
||||
default:
|
||||
stringType = "other";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getOrgType(String string) {
|
||||
int type = ContactsContract.CommonDataKinds.Organization.TYPE_OTHER;
|
||||
if (string!=null) {
|
||||
if ("work".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Organization.TYPE_WORK;
|
||||
}
|
||||
else if ("other".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Organization.TYPE_OTHER;
|
||||
}
|
||||
else if ("custom".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getOrgType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case ContactsContract.CommonDataKinds.Organization.TYPE_CUSTOM:
|
||||
stringType = "custom";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Organization.TYPE_WORK:
|
||||
stringType = "work";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.Organization.TYPE_OTHER:
|
||||
default:
|
||||
stringType = "other";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a string from the W3C Contact API to it's Android int value.
|
||||
* @param string
|
||||
* @return Android int value
|
||||
*/
|
||||
private int getAddressType(String string) {
|
||||
int type = ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER;
|
||||
if (string!=null) {
|
||||
if ("work".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK;
|
||||
}
|
||||
else if ("other".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER;
|
||||
}
|
||||
else if ("home".equals(string.toLowerCase())) {
|
||||
return ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* getPhoneType converts an Android phone type into a string
|
||||
* @param type
|
||||
* @return phone type as string.
|
||||
*/
|
||||
private String getAddressType(int type) {
|
||||
String stringType;
|
||||
switch (type) {
|
||||
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME:
|
||||
stringType = "home";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK:
|
||||
stringType = "work";
|
||||
break;
|
||||
case ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER:
|
||||
default:
|
||||
stringType = "other";
|
||||
break;
|
||||
}
|
||||
return stringType;
|
||||
}
|
||||
}
|
@ -16,9 +16,18 @@ import android.util.Log;
|
||||
|
||||
public class ContactManager extends Plugin {
|
||||
|
||||
private static ContactAccessor contactAccessor;
|
||||
private ContactAccessor contactAccessor;
|
||||
private static final String LOG_TAG = "Contact Query";
|
||||
|
||||
public static final int UNKNOWN_ERROR = 0;
|
||||
public static final int INVALID_ARGUMENT_ERROR = 1;
|
||||
public static final int TIMEOUT_ERROR = 2;
|
||||
public static final int PENDING_OPERATION_ERROR = 3;
|
||||
public static final int IO_ERROR = 4;
|
||||
public static final int NOT_SUPPORTED_ERROR = 5;
|
||||
public static final int PERMISSION_DENIED_ERROR = 20;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
@ -34,31 +43,57 @@ public class ContactManager extends Plugin {
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
if (contactAccessor == null) {
|
||||
contactAccessor = ContactAccessor.getInstance(webView, ctx);
|
||||
}
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
|
||||
/**
|
||||
* Check to see if we are on an Android 1.X device. If we are return an error as we
|
||||
* do not support this as of PhoneGap 1.0.
|
||||
*/
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
JSONObject res = null;
|
||||
try {
|
||||
res = new JSONObject();
|
||||
res.put("code", NOT_SUPPORTED_ERROR);
|
||||
res.put("message", "Contacts are not supported in Android 1.X devices");
|
||||
} catch (JSONException e) {
|
||||
// This should never happen
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
}
|
||||
return new PluginResult(PluginResult.Status.ERROR, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only create the contactAccessor after we check the Android version or the program will crash
|
||||
* older phones.
|
||||
*/
|
||||
if (this.contactAccessor == null) {
|
||||
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.ctx);
|
||||
}
|
||||
|
||||
try {
|
||||
if (action.equals("search")) {
|
||||
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
|
||||
return new PluginResult(status, res, "navigator.service.contacts.cast");
|
||||
return new PluginResult(status, res, "navigator.contacts.cast");
|
||||
}
|
||||
else if (action.equals("save")) {
|
||||
return new PluginResult(status, contactAccessor.save(args.getJSONObject(0)));
|
||||
String id = contactAccessor.save(args.getJSONObject(0));
|
||||
if (id != null) {
|
||||
JSONObject res = contactAccessor.getContactById(id);
|
||||
if (res != null) {
|
||||
return new PluginResult(status, res);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action.equals("remove")) {
|
||||
if (contactAccessor.remove(args.getString(0))) {
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
else {
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("code", 2);
|
||||
return new PluginResult(PluginResult.Status.ERROR, r);
|
||||
}
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
// If we get to this point an error has occurred
|
||||
JSONObject r = new JSONObject();
|
||||
r.put("code", UNKNOWN_ERROR);
|
||||
return new PluginResult(PluginResult.Status.ERROR, r);
|
||||
} catch (JSONException e) {
|
||||
Log.e(LOG_TAG, e.getMessage(), e);
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
|
34
framework/src/com/phonegap/Device.java
Executable file → Normal file
34
framework/src/com/phonegap/Device.java
Executable file → Normal file
@ -14,13 +14,11 @@ import org.json.JSONObject;
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
||||
public class Device extends Plugin {
|
||||
|
||||
public static String phonegapVersion = "0.9.4"; // PhoneGap version
|
||||
public static String phonegapVersion = "1.0.0rc2"; // PhoneGap version
|
||||
public static String platform = "Android"; // Device OS
|
||||
public static String uuid; // Device UUID
|
||||
|
||||
@ -116,34 +114,13 @@ public class Device extends Plugin {
|
||||
public String getPhonegapVersion() {
|
||||
return Device.phonegapVersion;
|
||||
}
|
||||
|
||||
public String getLine1Number(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getLine1Number();
|
||||
}
|
||||
|
||||
public String getDeviceId(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getDeviceId();
|
||||
}
|
||||
|
||||
public String getSimSerialNumber(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSimSerialNumber();
|
||||
}
|
||||
|
||||
public String getSubscriberId(){
|
||||
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
return operator.getSubscriberId();
|
||||
}
|
||||
|
||||
public String getModel()
|
||||
{
|
||||
public String getModel() {
|
||||
String model = android.os.Build.MODEL;
|
||||
return model;
|
||||
}
|
||||
public String getProductName()
|
||||
{
|
||||
|
||||
public String getProductName() {
|
||||
String productname = android.os.Build.PRODUCT;
|
||||
return productname;
|
||||
}
|
||||
@ -158,8 +135,7 @@ public class Device extends Plugin {
|
||||
return osversion;
|
||||
}
|
||||
|
||||
public String getSDKVersion()
|
||||
{
|
||||
public String getSDKVersion() {
|
||||
String sdkversion = android.os.Build.VERSION.SDK;
|
||||
return sdkversion;
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ package com.phonegap;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Environment;
|
||||
import android.os.StatFs;
|
||||
|
||||
@ -111,4 +112,31 @@ public class DirectoryManager {
|
||||
}
|
||||
return newPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if we can use the SD Card to store the temporary file. If not then use
|
||||
* the internal cache directory.
|
||||
*
|
||||
* @return the absolute path of where to store the file
|
||||
*/
|
||||
protected static String getTempDirectoryPath(Context ctx) {
|
||||
File cache = null;
|
||||
|
||||
// SD Card Mounted
|
||||
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
|
||||
"/Android/data/" + ctx.getPackageName() + "/cache/");
|
||||
}
|
||||
// Use internal storage
|
||||
else {
|
||||
cache = ctx.getCacheDir();
|
||||
}
|
||||
|
||||
// Create the cache directory if it doesn't exist
|
||||
if (!cache.exists()) {
|
||||
cache.mkdirs();
|
||||
}
|
||||
|
||||
return cache.getAbsolutePath();
|
||||
}
|
||||
}
|
@ -7,37 +7,51 @@
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.net.http.SslError;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.JsPromptResult;
|
||||
import android.webkit.JsResult;
|
||||
import android.webkit.SslErrorHandler;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.webkit.WebStorage;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
import android.webkit.GeolocationPermissions.Callback;
|
||||
import android.webkit.WebSettings.LayoutAlgorithm;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginManager;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.IPlugin;
|
||||
import com.phonegap.api.PluginManager;
|
||||
|
||||
/**
|
||||
* This class is the main Android activity that represents the PhoneGap
|
||||
@ -81,10 +95,6 @@ import com.phonegap.api.PhonegapActivity;
|
||||
* // (String - default=null)
|
||||
* super.setStringProperty("loadingDialog", "Wait,Loading Demo...");
|
||||
*
|
||||
* // Hide loadingDialog when page loaded instead of when deviceready event
|
||||
* // occurs. (Boolean - default=false)
|
||||
* super.setBooleanProperty("hideLoadingDialogOnPage", true);
|
||||
*
|
||||
* // Cause all links on web page to be loaded into existing web view,
|
||||
* // instead of being loaded into new browser. (Boolean - default=false)
|
||||
* super.setBooleanProperty("loadInWebView", true);
|
||||
@ -116,16 +126,20 @@ public class DroidGap extends PhonegapActivity {
|
||||
protected PluginManager pluginManager;
|
||||
protected boolean cancelLoadUrl = false;
|
||||
protected boolean clearHistory = false;
|
||||
protected ProgressDialog spinnerDialog = null;
|
||||
|
||||
// The initial URL for our app
|
||||
// ie http://server/path/index.html#abc?query
|
||||
private String url;
|
||||
|
||||
// The base of the initial URL for our app
|
||||
private String baseUrl;
|
||||
// The base of the initial URL for our app.
|
||||
// Does not include file name. Ends with /
|
||||
// ie http://server/path/
|
||||
private String baseUrl = null;
|
||||
|
||||
// Plugin to call when activity result is received
|
||||
private Plugin activityResultCallback = null;
|
||||
private boolean activityResultKeepRunning;
|
||||
protected IPlugin activityResultCallback = null;
|
||||
protected boolean activityResultKeepRunning;
|
||||
|
||||
// Flag indicates that a loadUrl timeout occurred
|
||||
private int loadUrlTimeout = 0;
|
||||
@ -133,10 +147,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
/*
|
||||
* The variables below are used to cache some of the activity properties.
|
||||
*/
|
||||
|
||||
// Flag indicates that "app loading" dialog should be hidden once page is loaded.
|
||||
// The default is to hide it once PhoneGap JavaScript code has initialized.
|
||||
protected boolean hideLoadingDialogOnPageLoad = false;
|
||||
|
||||
// Flag indicates that a URL navigated to from PhoneGap app should be loaded into same webview
|
||||
// instead of being loaded into the web browser.
|
||||
@ -167,7 +177,11 @@ public class DroidGap extends PhonegapActivity {
|
||||
WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
|
||||
// This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket!
|
||||
|
||||
root = new LinearLayout(this);
|
||||
Display display = getWindowManager().getDefaultDisplay();
|
||||
int width = display.getWidth();
|
||||
int height = display.getHeight();
|
||||
|
||||
root = new LinearLayoutSoftKeyboardDetect(this, width, height);
|
||||
root.setOrientation(LinearLayout.VERTICAL);
|
||||
root.setBackgroundColor(Color.BLACK);
|
||||
root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||
@ -201,11 +215,11 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
WebViewReflect.checkCompatibility();
|
||||
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("2.")) {
|
||||
this.appView.setWebChromeClient(new EclairClient(DroidGap.this));
|
||||
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
|
||||
this.appView.setWebChromeClient(new GapClient(DroidGap.this));
|
||||
}
|
||||
else {
|
||||
this.appView.setWebChromeClient(new GapClient(DroidGap.this));
|
||||
this.appView.setWebChromeClient(new EclairClient(DroidGap.this));
|
||||
}
|
||||
|
||||
this.setWebViewClient(this.appView, new GapViewClient(this));
|
||||
@ -221,9 +235,9 @@ public class DroidGap extends PhonegapActivity {
|
||||
settings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
|
||||
|
||||
// Enable database
|
||||
Package pack = this.getClass().getPackage();
|
||||
String appPackage = pack.getName();
|
||||
WebViewReflect.setStorage(settings, true, "/data/data/" + appPackage + "/app_database/");
|
||||
settings.setDatabaseEnabled(true);
|
||||
String databasePath = this.getApplicationContext().getDir("database", Context.MODE_PRIVATE).getPath();
|
||||
settings.setDatabasePath(databasePath);
|
||||
|
||||
// Enable DOM storage
|
||||
WebViewReflect.setDomStorage(settings);
|
||||
@ -270,22 +284,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.callbackServer = new CallbackServer();
|
||||
this.pluginManager = new PluginManager(appView, this);
|
||||
|
||||
this.addService("App", "com.phonegap.App");
|
||||
this.addService("Geolocation", "com.phonegap.GeoBroker");
|
||||
this.addService("Device", "com.phonegap.Device");
|
||||
this.addService("Accelerometer", "com.phonegap.AccelListener");
|
||||
this.addService("Compass", "com.phonegap.CompassListener");
|
||||
this.addService("Media", "com.phonegap.AudioHandler");
|
||||
this.addService("Camera", "com.phonegap.CameraLauncher");
|
||||
this.addService("Contacts", "com.phonegap.ContactManager");
|
||||
this.addService("Crypto", "com.phonegap.CryptoHandler");
|
||||
this.addService("File", "com.phonegap.FileUtils");
|
||||
this.addService("Location", "com.phonegap.GeoBroker"); // Always add Location, even though it is built-in on 2.x devices. Let JavaScript decide which one to use.
|
||||
this.addService("Network Status", "com.phonegap.NetworkManager");
|
||||
this.addService("Notification", "com.phonegap.Notification");
|
||||
this.addService("Storage", "com.phonegap.Storage");
|
||||
this.addService("Temperature", "com.phonegap.TempListener");
|
||||
this.addService("FileTransfer", "com.phonegap.FileTransfer");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -305,9 +303,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
root.setBackgroundResource(this.splashscreen);
|
||||
}
|
||||
|
||||
// If hideLoadingDialogOnPageLoad
|
||||
this.hideLoadingDialogOnPageLoad = this.getBooleanProperty("hideLoadingDialogOnPageLoad", false);
|
||||
|
||||
// If loadInWebView
|
||||
this.loadInWebView = this.getBooleanProperty("loadInWebView", false);
|
||||
|
||||
@ -329,12 +324,14 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void loadUrl(final String url) {
|
||||
System.out.println("loadUrl("+url+")");
|
||||
this.url = url;
|
||||
int i = url.lastIndexOf('/');
|
||||
if (i > 0) {
|
||||
this.baseUrl = url.substring(0, i);
|
||||
}
|
||||
else {
|
||||
this.baseUrl = this.url;
|
||||
if (this.baseUrl == null) {
|
||||
int i = url.lastIndexOf('/');
|
||||
if (i > 0) {
|
||||
this.baseUrl = url.substring(0, i+1);
|
||||
}
|
||||
else {
|
||||
this.baseUrl = this.url + "/";
|
||||
}
|
||||
}
|
||||
System.out.println("url="+url+" baseUrl="+baseUrl);
|
||||
|
||||
@ -367,10 +364,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
message = loading;
|
||||
}
|
||||
}
|
||||
JSONArray parm = new JSONArray();
|
||||
parm.put(title);
|
||||
parm.put(message);
|
||||
me.pluginManager.exec("Notification", "activityStart", null, parm.toString(), false);
|
||||
me.spinnerStart(title, message);
|
||||
}
|
||||
|
||||
// Create a timeout timer for loadUrl
|
||||
@ -592,37 +586,58 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void setDoubleProperty(String name, double value) {
|
||||
this.getIntent().putExtra(name, value);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*/
|
||||
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){};");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onPause(this.keepRunning);
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning) {
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onPause();
|
||||
|
||||
// Pause JavaScript timers (including setInterval)
|
||||
this.appView.pauseTimers();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the activity receives a new intent
|
||||
**/
|
||||
protected void onNewIntent(Intent intent) {
|
||||
super.onNewIntent(intent);
|
||||
|
||||
//Forward to plugins
|
||||
this.pluginManager.onNewIntent(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*/
|
||||
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){};");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onResume(this.keepRunning || this.activityResultKeepRunning);
|
||||
|
||||
// If app doesn't want to run in background
|
||||
if (!this.keepRunning || this.activityResultKeepRunning) {
|
||||
|
||||
@ -632,9 +647,6 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.activityResultKeepRunning = false;
|
||||
}
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onResume();
|
||||
|
||||
// Resume JavaScript timers (including setInterval)
|
||||
this.appView.resumeTimers();
|
||||
}
|
||||
@ -647,18 +659,21 @@ public class DroidGap extends PhonegapActivity {
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
if (this.appView != null) {
|
||||
|
||||
// Load blank page so that JavaScript onunload is called
|
||||
this.appView.loadUrl("about:blank");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onDestroy();
|
||||
// Make sure pause event is sent if onPause hasn't been called before onDestroy
|
||||
this.appView.loadUrl("javascript:try{PhoneGap.onPause.fire();}catch(e){};");
|
||||
|
||||
if (this.callbackServer != null) {
|
||||
this.callbackServer.destroy();
|
||||
}
|
||||
// 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");
|
||||
|
||||
// Forward to plugins
|
||||
this.pluginManager.onDestroy();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -681,13 +696,97 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.callbackServer.sendJavascript(statement);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display a new browser with the specified URL.
|
||||
*
|
||||
* NOTE: If usePhoneGap is set, only trusted PhoneGap URLs should be loaded,
|
||||
* since any PhoneGap API can be called by the loaded HTML page.
|
||||
*
|
||||
* @param url The url to load.
|
||||
* @param usePhoneGap Load url in PhoneGap webview.
|
||||
* @param clearPrev Clear the activity stack, so new app becomes top of stack
|
||||
* @param params DroidGap parameters for new app
|
||||
* @throws android.content.ActivityNotFoundException
|
||||
*/
|
||||
public void showWebPage(String url, boolean usePhoneGap, boolean clearPrev, HashMap<String, Object> params) throws android.content.ActivityNotFoundException {
|
||||
Intent intent = null;
|
||||
if (usePhoneGap) {
|
||||
intent = new Intent().setClass(this, com.phonegap.DroidGap.class);
|
||||
intent.putExtra("url", url);
|
||||
|
||||
// Add parameters
|
||||
if (params != null) {
|
||||
java.util.Set<Entry<String,Object>> s = params.entrySet();
|
||||
java.util.Iterator<Entry<String,Object>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Object> entry = it.next();
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value == null) {
|
||||
}
|
||||
else if (value.getClass().equals(String.class)) {
|
||||
intent.putExtra(key, (String)value);
|
||||
}
|
||||
else if (value.getClass().equals(Boolean.class)) {
|
||||
intent.putExtra(key, (Boolean)value);
|
||||
}
|
||||
else if (value.getClass().equals(Integer.class)) {
|
||||
intent.putExtra(key, (Integer)value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else {
|
||||
intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setData(Uri.parse(url));
|
||||
}
|
||||
this.startActivity(intent);
|
||||
|
||||
// Finish current activity
|
||||
if (clearPrev) {
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the spinner. Must be called from the UI thread.
|
||||
*
|
||||
* @param title Title of the dialog
|
||||
* @param message The message of the dialog
|
||||
*/
|
||||
public void spinnerStart(final String title, final String message) {
|
||||
if (this.spinnerDialog != null) {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
final DroidGap me = this;
|
||||
this.spinnerDialog = ProgressDialog.show(DroidGap.this, title , message, true, true,
|
||||
new DialogInterface.OnCancelListener() {
|
||||
public void onCancel(DialogInterface dialog) {
|
||||
me.spinnerDialog = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop spinner.
|
||||
*/
|
||||
public void spinnerStop() {
|
||||
if (this.spinnerDialog != null) {
|
||||
this.spinnerDialog.dismiss();
|
||||
this.spinnerDialog = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a hook for calling "alert" from javascript. Useful for
|
||||
* debugging your javascript.
|
||||
*/
|
||||
public class GapClient extends WebChromeClient {
|
||||
|
||||
private Context ctx;
|
||||
private DroidGap ctx;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -695,7 +794,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param ctx
|
||||
*/
|
||||
public GapClient(Context ctx) {
|
||||
this.ctx = ctx;
|
||||
this.ctx = (DroidGap)ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -767,10 +866,17 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
@Override
|
||||
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
|
||||
|
||||
// Security check to make sure any requests are coming from the page initially
|
||||
// loaded in webview and not another loaded in an iframe.
|
||||
boolean reqOk = false;
|
||||
if (url.indexOf(this.ctx.baseUrl) == 0) {
|
||||
reqOk = true;
|
||||
}
|
||||
|
||||
// Calling PluginManager.exec() to call a native service using
|
||||
// prompt(this.stringify(args), "gap:"+this.stringify([service, action, callbackId, true]));
|
||||
if (defaultValue.substring(0, 4).equals("gap:")) {
|
||||
if (reqOk && defaultValue != null && defaultValue.length() > 3 && defaultValue.substring(0, 4).equals("gap:")) {
|
||||
JSONArray array;
|
||||
try {
|
||||
array = new JSONArray(defaultValue.substring(4));
|
||||
@ -786,13 +892,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
|
||||
// Polling for JavaScript messages
|
||||
else if (defaultValue.equals("gap_poll:")) {
|
||||
else if (reqOk && defaultValue.equals("gap_poll:")) {
|
||||
String r = callbackServer.getJavascript();
|
||||
result.confirm(r);
|
||||
}
|
||||
|
||||
// Calling into CallbackServer
|
||||
else if (defaultValue.equals("gap_callbackServer:")) {
|
||||
else if (reqOk && defaultValue.equals("gap_callbackServer:")) {
|
||||
String r = "";
|
||||
if (message.equals("usePolling")) {
|
||||
r = ""+callbackServer.usePolling();
|
||||
@ -811,18 +917,40 @@ public class DroidGap extends PhonegapActivity {
|
||||
|
||||
// Show dialog
|
||||
else {
|
||||
//@TODO:
|
||||
result.confirm("");
|
||||
}
|
||||
final JsPromptResult res = result;
|
||||
AlertDialog.Builder dlg = new AlertDialog.Builder(this.ctx);
|
||||
dlg.setMessage(message);
|
||||
final EditText input = new EditText(this.ctx);
|
||||
if (defaultValue != null) {
|
||||
input.setText(defaultValue);
|
||||
}
|
||||
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();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* WebChromeClient that extends GapClient with additional support for Android 2.X
|
||||
*/
|
||||
public final class EclairClient extends GapClient {
|
||||
public class EclairClient extends GapClient {
|
||||
|
||||
private String TAG = "PhoneGapLog";
|
||||
private long MAX_QUOTA = 100 * 1024 * 1024;
|
||||
@ -876,6 +1004,12 @@ 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);
|
||||
@ -982,17 +1116,28 @@ public class DroidGap extends PhonegapActivity {
|
||||
// All else
|
||||
else {
|
||||
|
||||
int i = url.lastIndexOf('/');
|
||||
String newBaseUrl = url;
|
||||
if (i > 0) {
|
||||
newBaseUrl = url.substring(0, i);
|
||||
}
|
||||
|
||||
// If our app or file:, then load into our webview
|
||||
// NOTE: This replaces our app with new URL. When BACK is pressed,
|
||||
// our app is reloaded and restarted. All state is lost.
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || this.ctx.baseUrl.equals(newBaseUrl)) {
|
||||
this.ctx.appView.loadUrl(url);
|
||||
if (this.ctx.loadInWebView || url.startsWith("file://") || url.indexOf(this.ctx.baseUrl) == 0) {
|
||||
try {
|
||||
// Init parameters to new DroidGap activity and propagate existing parameters
|
||||
HashMap<String, Object> params = new HashMap<String, Object>();
|
||||
params.put("loadingDialog", "");
|
||||
if (this.ctx.loadInWebView) {
|
||||
params.put("loadInWebView", true);
|
||||
}
|
||||
params.put("keepRunning", this.ctx.keepRunning);
|
||||
params.put("loadUrlTimeoutValue", this.ctx.loadUrlTimeoutValue);
|
||||
String errorUrl = this.ctx.getStringProperty("errorUrl", null);
|
||||
if (errorUrl != null) {
|
||||
params.put("errorUrl", errorUrl);
|
||||
}
|
||||
|
||||
this.ctx.showWebPage(url, true, false, params);
|
||||
} catch (android.content.ActivityNotFoundException e) {
|
||||
System.out.println("Error loading url into DroidGap - "+url+":"+ e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// If not our application, let default viewer handle
|
||||
@ -1025,22 +1170,28 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Try firing the onNativeReady event in JS. If it fails because the JS is
|
||||
// not loaded yet then just set a flag so that the onNativeReady can be fired
|
||||
// from the JS side when the JS gets to that code.
|
||||
appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
if (!url.equals("about:blank")) {
|
||||
appView.loadUrl("javascript:try{ PhoneGap.onNativeReady.fire();}catch(e){_nativeReady = true;}");
|
||||
}
|
||||
|
||||
// Make app view visible
|
||||
appView.setVisibility(View.VISIBLE);
|
||||
|
||||
// Stop "app loading" spinner if showing
|
||||
if (this.ctx.hideLoadingDialogOnPageLoad) {
|
||||
this.ctx.hideLoadingDialogOnPageLoad = false;
|
||||
this.ctx.pluginManager.exec("Notification", "activityStop", null, "[]", false);
|
||||
}
|
||||
this.ctx.spinnerStop();
|
||||
|
||||
// Clear history, so that previous screen isn't there when Back button is pressed
|
||||
if (this.ctx.clearHistory) {
|
||||
this.ctx.clearHistory = false;
|
||||
this.ctx.appView.clearHistory();
|
||||
}
|
||||
|
||||
// Shutdown if blank loaded
|
||||
if (url.equals("about:blank")) {
|
||||
if (this.ctx.callbackServer != null) {
|
||||
this.ctx.callbackServer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1060,11 +1211,32 @@ public class DroidGap extends PhonegapActivity {
|
||||
this.ctx.loadUrlTimeout++;
|
||||
|
||||
// Stop "app loading" spinner if showing
|
||||
this.ctx.pluginManager.exec("Notification", "activityStop", null, "[]", false);
|
||||
this.ctx.spinnerStop();
|
||||
|
||||
// Handle error
|
||||
this.ctx.onReceivedError(errorCode, description, failingUrl);
|
||||
}
|
||||
|
||||
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
|
||||
|
||||
final String packageName = this.ctx.getPackageName();
|
||||
final PackageManager pm = this.ctx.getPackageManager();
|
||||
ApplicationInfo appInfo;
|
||||
try {
|
||||
appInfo = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
|
||||
if ((appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
|
||||
// debug = true
|
||||
handler.proceed();
|
||||
return;
|
||||
} else {
|
||||
// debug = false
|
||||
super.onReceivedSslError(view, handler, error);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// When it doubt, lock it out!
|
||||
super.onReceivedSslError(view, handler, error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1075,6 +1247,9 @@ 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) {
|
||||
@ -1082,6 +1257,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If back key is bound, then send event to JavaScript
|
||||
if (this.bound) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('backbutton');");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not bound
|
||||
@ -1090,6 +1266,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
// Go to previous page in webview if it is possible to go back
|
||||
if (this.appView.canGoBack()) {
|
||||
this.appView.goBack();
|
||||
return true;
|
||||
}
|
||||
|
||||
// If not, then invoke behavior of super class
|
||||
@ -1102,11 +1279,13 @@ public class DroidGap extends PhonegapActivity {
|
||||
// If menu key
|
||||
else if (keyCode == KeyEvent.KEYCODE_MENU) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('menubutton');");
|
||||
return true;
|
||||
}
|
||||
|
||||
// If search key
|
||||
else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
|
||||
this.appView.loadUrl("javascript:PhoneGap.fireEvent('searchbutton');");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -1126,12 +1305,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
@Override
|
||||
public void startActivityForResult(Intent intent, int requestCode) throws RuntimeException {
|
||||
System.out.println("startActivityForResult(intent,"+requestCode+")");
|
||||
if (requestCode == -1) {
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
else {
|
||||
throw new RuntimeException("PhoneGap Exception: Call startActivityForResult(Command, Intent) instead.");
|
||||
}
|
||||
super.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1142,7 +1316,7 @@ public class DroidGap extends PhonegapActivity {
|
||||
* @param intent The intent to start
|
||||
* @param requestCode The request code that is passed to callback to identify the activity
|
||||
*/
|
||||
public void startActivityForResult(Plugin command, Intent intent, int requestCode) {
|
||||
public void startActivityForResult(IPlugin command, Intent intent, int requestCode) {
|
||||
this.activityResultCallback = command;
|
||||
this.activityResultKeepRunning = this.keepRunning;
|
||||
|
||||
@ -1167,12 +1341,17 @@ public class DroidGap extends PhonegapActivity {
|
||||
*/
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||
super.onActivityResult(requestCode, resultCode, intent);
|
||||
Plugin callback = this.activityResultCallback;
|
||||
IPlugin callback = this.activityResultCallback;
|
||||
if (callback != null) {
|
||||
callback.onActivityResult(requestCode, resultCode, intent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setActivityResultCallback(IPlugin plugin) {
|
||||
this.activityResultCallback = plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report an error to the host application. These errors are unrecoverable (i.e. the main resource is unavailable).
|
||||
* The errorCode parameter corresponds to one of the ERROR_* constants.
|
||||
@ -1233,4 +1412,84 @@ public class DroidGap extends PhonegapActivity {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* We are providing this class to detect when the soft keyboard is shown
|
||||
* and hidden in the web view.
|
||||
*/
|
||||
class LinearLayoutSoftKeyboardDetect extends LinearLayout {
|
||||
|
||||
private static final String LOG_TAG = "SoftKeyboardDetect";
|
||||
|
||||
private int oldHeight = 0; // Need to save the old height as not to send redundant events
|
||||
private int oldWidth = 0; // Need to save old width for orientation change
|
||||
private int screenWidth = 0;
|
||||
private int screenHeight = 0;
|
||||
|
||||
public LinearLayoutSoftKeyboardDetect(Context context, int width, int height) {
|
||||
super(context);
|
||||
screenWidth = width;
|
||||
screenHeight = height;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Start listening to new measurement events. Fire events when the height
|
||||
* gets smaller fire a show keyboard event and when height gets bigger fire
|
||||
* a hide keyboard event.
|
||||
*
|
||||
* Note: We are using callbackServer.sendJavascript() instead of
|
||||
* this.appView.loadUrl() as changing the URL of the app would cause the
|
||||
* soft keyboard to go away.
|
||||
*
|
||||
* @param widthMeasureSpec
|
||||
* @param heightMeasureSpec
|
||||
*/
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
Log.d(LOG_TAG, "We are in our onMeasure method");
|
||||
|
||||
// Get the current height of the visible part of the screen.
|
||||
// This height will not included the status bar.
|
||||
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
Log.d(LOG_TAG, "Old Height = " + oldHeight);
|
||||
Log.d(LOG_TAG, "Height = " + height);
|
||||
Log.d(LOG_TAG, "Old Width = " + oldWidth);
|
||||
Log.d(LOG_TAG, "Width = " + width);
|
||||
|
||||
|
||||
// If the oldHeight = 0 then this is the first measure event as the app starts up.
|
||||
// If oldHeight == height then we got a measurement change that doesn't affect us.
|
||||
if (oldHeight == 0 || oldHeight == height) {
|
||||
Log.d(LOG_TAG, "Ignore this event");
|
||||
}
|
||||
// Account for orientation change and ignore this event/Fire orientation change
|
||||
else if(screenHeight == width)
|
||||
{
|
||||
int tmp_var = screenHeight;
|
||||
screenHeight = screenWidth;
|
||||
screenWidth = tmp_var;
|
||||
Log.d(LOG_TAG, "Orientation Change");
|
||||
}
|
||||
// If the height as gotten bigger then we will assume the soft keyboard has
|
||||
// gone away.
|
||||
else if (height > oldHeight) {
|
||||
Log.d(LOG_TAG, "Throw hide keyboard event");
|
||||
callbackServer.sendJavascript("PhoneGap.fireEvent('hidekeyboard');");
|
||||
}
|
||||
// If the height as gotten smaller then we will assume the soft keyboard has
|
||||
// been displayed.
|
||||
else if (height < oldHeight) {
|
||||
Log.d(LOG_TAG, "Throw show keyboard event");
|
||||
callbackServer.sendJavascript("PhoneGap.fireEvent('showkeyboard');");
|
||||
}
|
||||
|
||||
// Update the old height for the next event
|
||||
oldHeight = height;
|
||||
oldWidth = width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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,7 +315,13 @@ public class FileTransfer extends Plugin {
|
||||
|
||||
//------------------ read the SERVER RESPONSE
|
||||
StringBuffer responseString = new StringBuffer("");
|
||||
DataInputStream inStream = new DataInputStream ( conn.getInputStream() );
|
||||
DataInputStream inStream;
|
||||
try {
|
||||
inStream = new DataInputStream ( conn.getInputStream() );
|
||||
} catch(FileNotFoundException e) {
|
||||
throw new IOException("Received error from server");
|
||||
}
|
||||
|
||||
String line;
|
||||
while (( line = inStream.readLine()) != null) {
|
||||
responseString.append(line);
|
||||
|
@ -3,13 +3,14 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
@ -17,8 +18,10 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
@ -97,48 +100,20 @@ public class FileUtils extends Plugin {
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("readAsText")) {
|
||||
try {
|
||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, s);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
String s = this.readAsText(args.getString(0), args.getString(1));
|
||||
return new PluginResult(status, s);
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
try {
|
||||
String s = this.readAsDataURL(args.getString(0));
|
||||
return new PluginResult(status, s);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
}
|
||||
else if (action.equals("writeAsText")) {
|
||||
try {
|
||||
this.writeAsText(args.getString(0), args.getString(1), args.getBoolean(2));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
String s = this.readAsDataURL(args.getString(0));
|
||||
return new PluginResult(status, s);
|
||||
}
|
||||
else if (action.equals("write")) {
|
||||
try {
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getLong(2));
|
||||
return new PluginResult(status, fileSize);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
|
||||
return new PluginResult(status, fileSize);
|
||||
}
|
||||
else if (action.equals("truncate")) {
|
||||
try {
|
||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
||||
return new PluginResult(status, fileSize);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return new PluginResult(PluginResult.Status.ERROR, FileUtils.NOT_READABLE_ERR);
|
||||
}
|
||||
long fileSize = this.truncateFile(args.getString(0), args.getLong(1));
|
||||
return new PluginResult(status, fileSize);
|
||||
}
|
||||
else if (action.equals("requestFileSystem")) {
|
||||
long size = args.optLong(1);
|
||||
@ -254,22 +229,35 @@ public class FileUtils extends Plugin {
|
||||
* @throws JSONException
|
||||
*/
|
||||
private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException {
|
||||
// Test to see if this is a valid URL first
|
||||
@SuppressWarnings("unused")
|
||||
URL testUrl = new URL(url);
|
||||
|
||||
File fp = null;
|
||||
if (url.startsWith("file://")) {
|
||||
fp = new File(url.substring(7, url.length()));
|
||||
} else {
|
||||
fp = new File(url);
|
||||
}
|
||||
if (!fp.exists()) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
if (!fp.canRead()) {
|
||||
throw new IOException();
|
||||
}
|
||||
String decoded = URLDecoder.decode(url, "UTF-8");
|
||||
|
||||
File fp = null;
|
||||
|
||||
// Handle the special case where you get an Android content:// uri.
|
||||
if (decoded.startsWith("content:")) {
|
||||
Cursor cursor = this.ctx.managedQuery(Uri.parse(decoded), new String[] { MediaStore.Images.Media.DATA }, null, null, null);
|
||||
// Note: MediaStore.Images/Audio/Video.Media.DATA is always "_data"
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
cursor.moveToFirst();
|
||||
fp = new File(cursor.getString(column_index));
|
||||
} else {
|
||||
// Test to see if this is a valid URL first
|
||||
@SuppressWarnings("unused")
|
||||
URL testUrl = new URL(decoded);
|
||||
|
||||
if (decoded.startsWith("file://")) {
|
||||
fp = new File(decoded.substring(7, decoded.length()));
|
||||
} else {
|
||||
fp = new File(decoded);
|
||||
}
|
||||
}
|
||||
|
||||
if (!fp.exists()) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
if (!fp.canRead()) {
|
||||
throw new IOException();
|
||||
}
|
||||
return getEntry(fp);
|
||||
}
|
||||
|
||||
@ -436,7 +424,7 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
|
||||
// Check to make sure we are not copying the directory into itself
|
||||
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
|
||||
if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy itself into itself");
|
||||
}
|
||||
|
||||
@ -460,6 +448,26 @@ public class FileUtils extends Plugin {
|
||||
return getEntry(destinationDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if the user attempted to copy an entry into its parent without changing its name,
|
||||
* or attempted to copy a directory into a directory that it contains directly or indirectly.
|
||||
*
|
||||
* @param srcDir
|
||||
* @param destinationDir
|
||||
* @return
|
||||
*/
|
||||
private boolean isCopyOnItself(String src, String dest) {
|
||||
|
||||
// This weird test is to determine if we are copying or moving a directory into itself.
|
||||
// Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
|
||||
// Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
|
||||
if (dest.startsWith(src) && dest.indexOf(File.separator, src.length()-1) != -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a file
|
||||
*
|
||||
@ -505,8 +513,8 @@ public class FileUtils extends Plugin {
|
||||
}
|
||||
|
||||
// Check to make sure we are not copying the directory into itself
|
||||
if (destinationDir.getAbsolutePath().startsWith(srcDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't copy itself into itself");
|
||||
if (isCopyOnItself(srcDir.getAbsolutePath(), destinationDir.getAbsolutePath())) {
|
||||
throw new InvalidModificationException("Can't move itself into itself");
|
||||
}
|
||||
|
||||
// If the destination directory already exists and is empty then delete it. This is according to spec.
|
||||
@ -836,49 +844,19 @@ public class FileUtils extends Plugin {
|
||||
* @return T=returns value
|
||||
*/
|
||||
public boolean isSynch(String action) {
|
||||
if (action.equals("readAsText")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("readAsDataURL")) {
|
||||
return false;
|
||||
}
|
||||
else if (action.equals("writeAsText")) {
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
@ -942,7 +920,7 @@ public class FileUtils extends Plugin {
|
||||
* @param filename
|
||||
* @return a mime type
|
||||
*/
|
||||
private String getMimeType(String filename) {
|
||||
public static String getMimeType(String filename) {
|
||||
MimeTypeMap map = MimeTypeMap.getSingleton();
|
||||
return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
|
||||
}
|
||||
@ -952,39 +930,29 @@ public class FileUtils extends Plugin {
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @param data The contents of the file.
|
||||
* @param append T=append, F=overwrite
|
||||
* @param offset The position to begin writing the file.
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
public void writeAsText(String filename, String data, boolean append) throws FileNotFoundException, IOException {
|
||||
String FilePath= filename;
|
||||
/**/
|
||||
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(FilePath, append);
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write contents of file.
|
||||
*
|
||||
* @param filename The name of the file.
|
||||
* @param data The contents of the file.
|
||||
* @param offset The position to begin writing the file.
|
||||
* @throws FileNotFoundException, IOException
|
||||
*/
|
||||
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,34 +3,65 @@
|
||||
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
|
||||
*
|
||||
* Copyright (c) 2005-2010, Nitobi Software Inc.
|
||||
* Copyright (c) 2010, IBM Corporation
|
||||
* Copyright (c) 2010-2011, IBM Corporation
|
||||
*/
|
||||
package com.phonegap;
|
||||
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import com.phonegap.api.PhonegapActivity;
|
||||
import com.phonegap.api.Plugin;
|
||||
import com.phonegap.api.PluginResult;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.net.*;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.util.Log;
|
||||
|
||||
public class NetworkManager extends Plugin {
|
||||
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int NOT_REACHABLE = 0;
|
||||
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
|
||||
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
|
||||
|
||||
public static final String WIFI = "wifi";
|
||||
public static final String WIMAX = "wimax";
|
||||
// mobile
|
||||
public static final String MOBILE = "mobile";
|
||||
// 2G network types
|
||||
public static final String GSM = "gsm";
|
||||
public static final String GPRS = "gprs";
|
||||
public static final String EDGE = "edge";
|
||||
// 3G network types
|
||||
public static final String CDMA = "cdma";
|
||||
public static final String UMTS = "umts";
|
||||
// 4G network types
|
||||
public static final String LTE = "lte";
|
||||
public static final String UMB = "umb";
|
||||
// return types
|
||||
public static final String TYPE_UNKNOWN = "unknown";
|
||||
public static final String TYPE_ETHERNET = "ethernet";
|
||||
public static final String TYPE_WIFI = "wifi";
|
||||
public static final String TYPE_2G = "2g";
|
||||
public static final String TYPE_3G = "3g";
|
||||
public static final String TYPE_4G = "4g";
|
||||
public static final String TYPE_NONE = "none";
|
||||
|
||||
ConnectivityManager sockMan;
|
||||
private static final String LOG_TAG = "NetworkManager";
|
||||
|
||||
private String connectionCallbackId;
|
||||
|
||||
ConnectivityManager sockMan;
|
||||
BroadcastReceiver receiver;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public NetworkManager() {
|
||||
this.receiver = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,9 +72,24 @@ public class NetworkManager extends Plugin {
|
||||
*/
|
||||
public void setContext(PhonegapActivity ctx) {
|
||||
super.setContext(ctx);
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
}
|
||||
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
this.connectionCallbackId = null;
|
||||
|
||||
// We need to listen to connectivity events to update navigator.connection
|
||||
IntentFilter intentFilter = new IntentFilter() ;
|
||||
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
|
||||
if (this.receiver == null) {
|
||||
this.receiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
|
||||
}
|
||||
};
|
||||
ctx.registerReceiver(this.receiver, intentFilter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the request and returns PluginResult.
|
||||
*
|
||||
@ -53,25 +99,18 @@ public class NetworkManager extends Plugin {
|
||||
* @return A PluginResult object with a status and message.
|
||||
*/
|
||||
public PluginResult execute(String action, JSONArray args, String callbackId) {
|
||||
PluginResult.Status status = PluginResult.Status.OK;
|
||||
String result = "";
|
||||
try {
|
||||
if (action.equals("isAvailable")) {
|
||||
boolean b = this.isAvailable();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("isWifiActive")) {
|
||||
boolean b = this.isWifiActive();
|
||||
return new PluginResult(status, b);
|
||||
}
|
||||
else if (action.equals("isReachable")) {
|
||||
int i = this.isReachable(args.getString(0), args.getBoolean(1));
|
||||
return new PluginResult(status, i);
|
||||
}
|
||||
return new PluginResult(status, result);
|
||||
} catch (JSONException e) {
|
||||
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
|
||||
PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
|
||||
String result = "Unsupported Operation: " + action;
|
||||
|
||||
if (action.equals("getConnectionInfo")) {
|
||||
this.connectionCallbackId = callbackId;
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
|
||||
pluginResult.setKeepCallback(true);
|
||||
return pluginResult;
|
||||
}
|
||||
|
||||
return new PluginResult(status, result);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,70 +123,100 @@ public class NetworkManager extends Plugin {
|
||||
// All methods take a while, so always use async
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop network receiver.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
if (this.receiver != null) {
|
||||
try {
|
||||
this.ctx.unregisterReceiver(this.receiver);
|
||||
} catch (Exception e) {
|
||||
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// LOCAL METHODS
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Determine if a network connection exists.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean isAvailable() {
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
boolean conn = false;
|
||||
if (info != null) {
|
||||
conn = info.isConnected();
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if a WIFI connection exists.
|
||||
* Updates the JavaScript side whenever the connection changes
|
||||
*
|
||||
* @param info the current active network info
|
||||
* @return
|
||||
*/
|
||||
public boolean isWifiActive() {
|
||||
NetworkInfo info = sockMan.getActiveNetworkInfo();
|
||||
if (info != null) {
|
||||
String type = info.getTypeName();
|
||||
return type.equals("WIFI");
|
||||
}
|
||||
return false;
|
||||
private void updateConnectionInfo(NetworkInfo info) {
|
||||
// send update to javascript "navigator.network.connection"
|
||||
sendUpdate(this.getConnectionInfo(info));
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a URI is reachable over the network.
|
||||
|
||||
/**
|
||||
* Get the latest network connection information
|
||||
*
|
||||
* @param uri
|
||||
* @param isIpAddress
|
||||
* @return
|
||||
* @param info the current active network info
|
||||
* @return a JSONObject that represents the network info
|
||||
*/
|
||||
public int isReachable(String uri, boolean isIpAddress) {
|
||||
int reachable = NOT_REACHABLE;
|
||||
|
||||
if (uri.indexOf("http://") == -1) {
|
||||
uri = "http://" + uri;
|
||||
}
|
||||
|
||||
if (this.isAvailable()) {
|
||||
try {
|
||||
DefaultHttpClient httpclient = new DefaultHttpClient();
|
||||
HttpGet httpget = new HttpGet(uri);
|
||||
httpclient.execute(httpget);
|
||||
|
||||
if (this.isWifiActive()) {
|
||||
reachable = REACHABLE_VIA_WIFI_NETWORK;
|
||||
}
|
||||
else {
|
||||
reachable = REACHABLE_VIA_CARRIER_DATA_NETWORK;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
reachable = NOT_REACHABLE;
|
||||
private String getConnectionInfo(NetworkInfo info) {
|
||||
String type = TYPE_NONE;
|
||||
if (info != null) {
|
||||
// If we are not connected to any network set type to none
|
||||
if (!info.isConnected()) {
|
||||
type = TYPE_NONE;
|
||||
}
|
||||
else {
|
||||
type = getType(info);
|
||||
}
|
||||
}
|
||||
|
||||
return reachable;
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new plugin result and send it back to JavaScript
|
||||
*
|
||||
* @param connection the network info to set as navigator.connection
|
||||
*/
|
||||
private void sendUpdate(String type) {
|
||||
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
|
||||
result.setKeepCallback(true);
|
||||
this.success(result, this.connectionCallbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the type of connection
|
||||
*
|
||||
* @param info the network info so we can determine connection type.
|
||||
* @return the type of mobile network we are on
|
||||
*/
|
||||
private String getType(NetworkInfo info) {
|
||||
if (info != null) {
|
||||
String type = info.getTypeName();
|
||||
|
||||
if (type.toLowerCase().equals(WIFI)) {
|
||||
return TYPE_WIFI;
|
||||
}
|
||||
else if (type.toLowerCase().equals(MOBILE)) {
|
||||
type = info.getSubtypeName();
|
||||
if (type.toLowerCase().equals(GSM) ||
|
||||
type.toLowerCase().equals(GPRS) ||
|
||||
type.toLowerCase().equals(EDGE)) {
|
||||
return TYPE_2G;
|
||||
}
|
||||
else if (type.toLowerCase().equals(CDMA) ||
|
||||
type.toLowerCase().equals(UMTS)) {
|
||||
return TYPE_3G;
|
||||
}
|
||||
else if (type.toLowerCase().equals(LTE) ||
|
||||
type.toLowerCase().equals(UMB)) {
|
||||
return TYPE_4G;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
return TYPE_NONE;
|
||||
}
|
||||
return TYPE_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
@ -16,15 +16,21 @@ 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.
|
||||
*/
|
||||
@ -34,29 +40,37 @@ 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")) {
|
||||
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);
|
||||
} else if (action.equals("openDatabase")) {
|
||||
this.openDatabase(args.getString(0), args.getString(1),
|
||||
args.getString(2), args.getLong(3));
|
||||
} else if (action.equals("executeSql")) {
|
||||
String[] s = null;
|
||||
if (args.isNull(1)) {
|
||||
s = new String[0];
|
||||
} else {
|
||||
JSONArray a = args.getJSONArray(1);
|
||||
int len = a.length();
|
||||
s = new String[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
s[i] = a.getString(i);
|
||||
}
|
||||
}
|
||||
this.executeSql(args.getString(0), s, args.getString(2));
|
||||
}
|
||||
@ -67,15 +81,17 @@ 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 false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Clean up and close database.
|
||||
*/
|
||||
@ -87,33 +103,40 @@ 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();
|
||||
@ -121,27 +144,36 @@ 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 {
|
||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||
this.processResults(myCursor, tx_id);
|
||||
myCursor.close();
|
||||
if (isDDL(query)) {
|
||||
this.myDb.execSQL(query);
|
||||
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', '');");
|
||||
}
|
||||
else {
|
||||
Cursor myCursor = this.myDb.rawQuery(query, params);
|
||||
this.processResults(myCursor, tx_id);
|
||||
myCursor.close();
|
||||
}
|
||||
}
|
||||
catch (SQLiteException ex) {
|
||||
ex.printStackTrace();
|
||||
@ -151,24 +183,40 @@ 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();
|
||||
@ -179,19 +227,20 @@ 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
|
||||
+ ");");
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -54,14 +54,23 @@ public interface IPlugin {
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onPause();
|
||||
void onPause(boolean multitasking);
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
void onResume();
|
||||
void onResume(boolean multitasking);
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
void onNewIntent(Intent intent);
|
||||
|
||||
/**
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
|
@ -15,7 +15,7 @@ import android.content.Intent;
|
||||
* It is used to isolate plugin development, and remove dependency on entire Phonegap library.
|
||||
*/
|
||||
public abstract class PhonegapActivity extends Activity {
|
||||
|
||||
|
||||
/**
|
||||
* Add a class that implements a service.
|
||||
*
|
||||
@ -39,5 +39,12 @@ public abstract class PhonegapActivity extends Activity {
|
||||
* @param intent The intent to start
|
||||
* @param requestCode The request code that is passed to callback to identify the activity
|
||||
*/
|
||||
abstract public void startActivityForResult(Plugin command, Intent intent, int requestCode);
|
||||
abstract public void startActivityForResult(IPlugin command, Intent intent, int requestCode);
|
||||
|
||||
/**
|
||||
* Set the plugin to be called when a sub-activity exits.
|
||||
*
|
||||
* @param plugin The plugin on which onActivityResult is to be called
|
||||
*/
|
||||
abstract public void setActivityResultCallback(IPlugin plugin);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
package com.phonegap.api;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.webkit.WebView;
|
||||
@ -19,6 +20,7 @@ import android.webkit.WebView;
|
||||
*/
|
||||
public abstract class Plugin implements IPlugin {
|
||||
|
||||
public String id;
|
||||
public WebView webView; // WebView object
|
||||
public PhonegapActivity ctx; // PhonegapActivity object
|
||||
|
||||
@ -64,14 +66,24 @@ public abstract class Plugin implements IPlugin {
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause() {
|
||||
public void onPause(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume() {
|
||||
public void onResume(boolean multitasking) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
@ -116,6 +128,26 @@ public abstract class Plugin implements IPlugin {
|
||||
this.ctx.sendJavascript(pluginResult.toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(JSONObject message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for success callbacks that just returns the Status.OK by default
|
||||
*
|
||||
* @param message The message to add to the success result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void success(String message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Call the JavaScript error callback for this plugin.
|
||||
*
|
||||
@ -125,4 +157,24 @@ public abstract class Plugin implements IPlugin {
|
||||
public void error(PluginResult pluginResult, String callbackId) {
|
||||
this.ctx.sendJavascript(pluginResult.toErrorCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(JSONObject message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for error callbacks that just returns the Status.ERROR by default
|
||||
*
|
||||
* @param message The message to add to the error result.
|
||||
* @param callbackId The callback id used when calling back into JavaScript.
|
||||
*/
|
||||
public void error(String message, String callbackId) {
|
||||
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
|
||||
}
|
||||
}
|
||||
|
@ -7,12 +7,16 @@
|
||||
*/
|
||||
package com.phonegap.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
@ -23,7 +27,7 @@ import android.webkit.WebView;
|
||||
*/
|
||||
public final class PluginManager {
|
||||
|
||||
private HashMap<String, Plugin> plugins = new HashMap<String,Plugin>();
|
||||
private HashMap<String, IPlugin> plugins = new HashMap<String,IPlugin>();
|
||||
private HashMap<String, String> services = new HashMap<String,String>();
|
||||
|
||||
private final PhonegapActivity ctx;
|
||||
@ -38,6 +42,35 @@ public final class PluginManager {
|
||||
public PluginManager(WebView app, PhonegapActivity ctx) {
|
||||
this.ctx = ctx;
|
||||
this.app = app;
|
||||
this.loadPlugins();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load plugins from res/xml/plugins.xml
|
||||
*/
|
||||
public void loadPlugins() {
|
||||
int id = ctx.getResources().getIdentifier("plugins", "xml", ctx.getPackageName());
|
||||
if (id == 0) { pluginConfigurationMissing(); }
|
||||
XmlResourceParser xml = ctx.getResources().getXml(id);
|
||||
int eventType = -1;
|
||||
while (eventType != XmlResourceParser.END_DOCUMENT) {
|
||||
if (eventType == XmlResourceParser.START_TAG) {
|
||||
String strNode = xml.getName();
|
||||
if (strNode.equals("plugin")) {
|
||||
String name = xml.getAttributeValue(null, "name");
|
||||
String value = xml.getAttributeValue(null, "value");
|
||||
//System.out.println("Plugin: "+name+" => "+value);
|
||||
this.addService(name, value);
|
||||
}
|
||||
}
|
||||
try {
|
||||
eventType = xml.next();
|
||||
} catch (XmlPullParserException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,7 +107,7 @@ public final class PluginManager {
|
||||
c = getClassByName(clazz);
|
||||
}
|
||||
if (isPhoneGapPlugin(c)) {
|
||||
final Plugin plugin = this.addPlugin(clazz, c);
|
||||
final IPlugin plugin = this.addPlugin(clazz, c);
|
||||
final PhonegapActivity ctx = this.ctx;
|
||||
runAsync = async && !plugin.isSynch(action);
|
||||
if (runAsync) {
|
||||
@ -100,7 +133,7 @@ public final class PluginManager {
|
||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.ERROR);
|
||||
PluginResult cr = new PluginResult(PluginResult.Status.ERROR, e.getMessage());
|
||||
ctx.sendJavascript(cr.toErrorCallbackString(callbackId));
|
||||
}
|
||||
}
|
||||
@ -159,24 +192,7 @@ public final class PluginManager {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add plugin to be loaded and cached. This creates an instance of the plugin.
|
||||
* If plugin is already created, then just return it.
|
||||
*
|
||||
* @param className The class to load
|
||||
* @return The plugin
|
||||
*/
|
||||
public Plugin addPlugin(String className) {
|
||||
try {
|
||||
return this.addPlugin(className, this.getClassByName(className));
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
System.out.println("Error adding plugin "+className+".");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add plugin to be loaded and cached. This creates an instance of the plugin.
|
||||
* If plugin is already created, then just return it.
|
||||
@ -187,12 +203,12 @@ public final class PluginManager {
|
||||
* @return The plugin
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private Plugin addPlugin(String className, Class clazz) {
|
||||
private IPlugin addPlugin(String className, Class clazz) {
|
||||
if (this.plugins.containsKey(className)) {
|
||||
return this.getPlugin(className);
|
||||
}
|
||||
try {
|
||||
Plugin plugin = (Plugin)clazz.newInstance();
|
||||
IPlugin plugin = (IPlugin)clazz.newInstance();
|
||||
this.plugins.put(className, plugin);
|
||||
plugin.setContext(this.ctx);
|
||||
plugin.setView(this.app);
|
||||
@ -211,8 +227,8 @@ public final class PluginManager {
|
||||
* @param className The class of the loaded plugin.
|
||||
* @return
|
||||
*/
|
||||
private Plugin getPlugin(String className) {
|
||||
Plugin plugin = this.plugins.get(className);
|
||||
private IPlugin getPlugin(String className) {
|
||||
IPlugin plugin = this.plugins.get(className);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
@ -229,27 +245,31 @@ public final class PluginManager {
|
||||
|
||||
/**
|
||||
* Called when the system is about to start resuming a previous activity.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onPause() {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
public void onPause(boolean multitasking) {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onPause();
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onPause(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity will start interacting with the user.
|
||||
*
|
||||
* @param multitasking Flag indicating if multitasking is turned on for app
|
||||
*/
|
||||
public void onResume() {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
public void onResume(boolean multitasking) {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
plugin.onResume();
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onResume(multitasking);
|
||||
}
|
||||
}
|
||||
|
||||
@ -257,12 +277,32 @@ public final class PluginManager {
|
||||
* The final call you receive before your activity is destroyed.
|
||||
*/
|
||||
public void onDestroy() {
|
||||
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,Plugin> entry = it.next();
|
||||
Plugin plugin = entry.getValue();
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the activity receives a new intent.
|
||||
*/
|
||||
public void onNewIntent(Intent intent) {
|
||||
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
|
||||
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
|
||||
while(it.hasNext()) {
|
||||
Entry<String,IPlugin> entry = it.next();
|
||||
IPlugin plugin = entry.getValue();
|
||||
plugin.onNewIntent(intent);
|
||||
}
|
||||
}
|
||||
|
||||
private void pluginConfigurationMissing() {
|
||||
System.err.println("=====================================================================================");
|
||||
System.err.println("ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
|
||||
System.err.println("https://raw.github.com/phonegap/phonegap-android/master/framework/res/xml/plugins.xml");
|
||||
System.err.println("=====================================================================================");
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ class Classic
|
||||
@android_sdk_path, @name, @pkg, @www, @path = a
|
||||
build
|
||||
end
|
||||
|
||||
|
||||
def build
|
||||
setup
|
||||
clobber
|
||||
@ -16,8 +16,8 @@ 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")
|
||||
@ -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")
|
||||
@ -88,15 +88,18 @@ class Classic
|
||||
|
||||
# copies stuff from src directory into the android project directory (@path)
|
||||
def copy_libs
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION'))
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION')).lstrip.rstrip
|
||||
framework_res_dir = File.join(@framework_dir, "res")
|
||||
app_res_dir = File.join(@path, "res")
|
||||
# copies in the jar
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.#{ version }.jar"), File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap-#{ version }.jar"), File.join(@path, "libs")
|
||||
# copies in the strings.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "values")
|
||||
FileUtils.cp File.join(framework_res_dir, "values","strings.xml"), File.join(app_res_dir, "values", "strings.xml")
|
||||
# copies in plugins.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "xml")
|
||||
FileUtils.cp File.join(framework_res_dir, "xml","plugins.xml"), File.join(app_res_dir, "xml", "plugins.xml")
|
||||
# drops in the layout files: main.xml and preview.xml
|
||||
FileUtils.mkdir_p File.join(app_res_dir, "layout")
|
||||
%w(main.xml).each do |f|
|
||||
@ -125,9 +128,9 @@ class Classic
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
|
||||
|
||||
# puts app name in strings
|
||||
def add_name_to_strings
|
||||
x = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
|
||||
@ -138,8 +141,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 +167,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
|
||||
|
@ -33,12 +33,19 @@ class Update
|
||||
# TODO need to allow for www import inc icon
|
||||
def copy_libs
|
||||
puts "Copying over libraries and assets..."
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "libs")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs")
|
||||
version = IO.read(File.join(@framework_dir, '../VERSION'))
|
||||
|
||||
FileUtils.mkdir_p File.join(@path, "assets", "www")
|
||||
FileUtils.cp File.join(@framework_dir, "assets", "www", "phonegap.js"), File.join(@path, "assets", "www")
|
||||
FileUtils.cp File.join(@framework_dir, "phonegap-#{ version }.jar"), File.join(@path, "libs")
|
||||
|
||||
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
|
||||
js_dir = File.join(@framework_dir, "assets", "js")
|
||||
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
|
||||
Dir.new(js_dir).entries.each do |script|
|
||||
next if script[0].chr == "." or script == "phonegap.js.base"
|
||||
phonegapjs << IO.read(File.join(js_dir, script))
|
||||
phonegapjs << "\n\n"
|
||||
end
|
||||
File.open(File.join(@path, "assets", "www", "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
|
||||
end
|
||||
#
|
||||
end
|
@ -1,31 +0,0 @@
|
||||
YUI is issued by Yahoo! under the BSD License below.
|
||||
|
||||
Copyright (c) 2010, Yahoo! Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms, with or
|
||||
without modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Yahoo! Inc. nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission of Yahoo! Inc.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
http://developer.yahoo.com/yui/license.html
|
||||
|
@ -1,140 +0,0 @@
|
||||
==============================================================================
|
||||
YUI Compressor
|
||||
==============================================================================
|
||||
|
||||
NAME
|
||||
|
||||
YUI Compressor - The Yahoo! JavaScript and CSS Compressor
|
||||
|
||||
SYNOPSIS
|
||||
|
||||
Usage: java -jar yuicompressor-x.y.z.jar [options] [input file]
|
||||
|
||||
Global Options
|
||||
-h, --help Displays this information
|
||||
--type <js|css> Specifies the type of the input file
|
||||
--charset <charset> Read the input file using <charset>
|
||||
--line-break <column> Insert a line break after the specified column number
|
||||
-v, --verbose Display informational messages and warnings
|
||||
-o <file> Place the output into <file>. Defaults to stdout.
|
||||
|
||||
JavaScript Options
|
||||
--nomunge Minify only, do not obfuscate
|
||||
--preserve-semi Preserve all semicolons
|
||||
--disable-optimizations Disable all micro optimizations
|
||||
|
||||
DESCRIPTION
|
||||
|
||||
The YUI Compressor is a JavaScript compressor which, in addition to removing
|
||||
comments and white-spaces, obfuscates local variables using the smallest
|
||||
possible variable name. This obfuscation is safe, even when using constructs
|
||||
such as 'eval' or 'with' (although the compression is not optimal is those
|
||||
cases) Compared to jsmin, the average savings is around 20%.
|
||||
|
||||
The YUI Compressor is also able to safely compress CSS files. The decision
|
||||
on which compressor is being used is made on the file extension (js or css)
|
||||
|
||||
GLOBAL OPTIONS
|
||||
|
||||
-h, --help
|
||||
Prints help on how to use the YUI Compressor
|
||||
|
||||
--line-break
|
||||
Some source control tools don't like files containing lines longer than,
|
||||
say 8000 characters. The linebreak option is used in that case to split
|
||||
long lines after a specific column. It can also be used to make the code
|
||||
more readable, easier to debug (especially with the MS Script Debugger)
|
||||
Specify 0 to get a line break after each semi-colon in JavaScript, and
|
||||
after each rule in CSS.
|
||||
|
||||
--type js|css
|
||||
The type of compressor (JavaScript or CSS) is chosen based on the
|
||||
extension of the input file name (.js or .css) This option is required
|
||||
if no input file has been specified. Otherwise, this option is only
|
||||
required if the input file extension is neither 'js' nor 'css'.
|
||||
|
||||
--charset character-set
|
||||
If a supported character set is specified, the YUI Compressor will use it
|
||||
to read the input file. Otherwise, it will assume that the platform's
|
||||
default character set is being used. The output file is encoded using
|
||||
the same character set.
|
||||
|
||||
-o outfile
|
||||
Place output in file outfile. If not specified, the YUI Compressor will
|
||||
default to the standard output, which you can redirect to a file.
|
||||
|
||||
-v, --verbose
|
||||
Display informational messages and warnings.
|
||||
|
||||
JAVASCRIPT ONLY OPTIONS
|
||||
|
||||
--nomunge
|
||||
Minify only. Do not obfuscate local symbols.
|
||||
|
||||
--preserve-semi
|
||||
Preserve unnecessary semicolons (such as right before a '}') This option
|
||||
is useful when compressed code has to be run through JSLint (which is the
|
||||
case of YUI for example)
|
||||
|
||||
--disable-optimizations
|
||||
Disable all the built-in micro optimizations.
|
||||
|
||||
NOTES
|
||||
|
||||
+ If no input file is specified, it defaults to stdin.
|
||||
|
||||
+ The YUI Compressor requires Java version >= 1.4.
|
||||
|
||||
+ It is possible to prevent a local variable, nested function or function
|
||||
argument from being obfuscated by using "hints". A hint is a string that
|
||||
is located at the very beginning of a function body like so:
|
||||
|
||||
function fn (arg1, arg2, arg3) {
|
||||
"arg2:nomunge, localVar:nomunge, nestedFn:nomunge";
|
||||
|
||||
...
|
||||
var localVar;
|
||||
...
|
||||
|
||||
function nestedFn () {
|
||||
....
|
||||
}
|
||||
|
||||
...
|
||||
}
|
||||
|
||||
The hint itself disappears from the compressed file.
|
||||
|
||||
+ C-style comments starting with /*! are preserved. This is useful with
|
||||
comments containing copyright/license information. For example:
|
||||
|
||||
/*!
|
||||
* TERMS OF USE - EASING EQUATIONS
|
||||
* Open source under the BSD License.
|
||||
* Copyright 2001 Robert Penner All rights reserved.
|
||||
*/
|
||||
|
||||
becomes:
|
||||
|
||||
/*
|
||||
* TERMS OF USE - EASING EQUATIONS
|
||||
* Open source under the BSD License.
|
||||
* Copyright 2001 Robert Penner All rights reserved.
|
||||
*/
|
||||
|
||||
AUTHOR
|
||||
|
||||
The YUI Compressor was written and is maintained by:
|
||||
Julien Lecomte <jlecomte@yahoo-inc.com>
|
||||
The CSS portion is a port of Isaac Schlueter's cssmin utility.
|
||||
|
||||
COPYRIGHT
|
||||
|
||||
Copyright (c) 2007-2009, Yahoo! Inc. All rights reserved.
|
||||
|
||||
LICENSE
|
||||
|
||||
All code specific to YUI Compressor is issued under a BSD license.
|
||||
YUI Compressor extends and implements code from Mozilla's Rhino project.
|
||||
Rhino is issued under the Mozilla Public License (MPL), and MPL applies
|
||||
to the Rhino source and binaries that are distributed with YUI Compressor.
|
Binary file not shown.
Loading…
Reference in New Issue
Block a user