Compare commits

..

98 Commits
0.9.6 ... 1.0.0

Author SHA1 Message Date
macdonst
7bb34dea14 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-29 00:02:07 -04:00
macdonst
004453b03f Upping version to 1.0.0 2011-07-29 12:01:46 +08:00
macdonst
a1edf92fa4 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-28 23:58:09 -04:00
macdonst
e28458869f Issue #185: Fix mis-spelling in file.js 2011-07-29 11:56:29 +08:00
macdonst
229e15f685 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-26 00:31:37 -04:00
macdonst
39d6952494 Upping version to 1.0.0rc3 2011-07-26 12:30:41 +08:00
macdonst
b864a8a6c6 Upping version to 1.0.0rc3 2011-07-26 00:20:35 -04:00
macdonst
f5dac1451d Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-26 00:18:33 -04:00
macdonst
2bf765897b Normalize Android Camera.takePicture with iOS and BB versions.
Both iOS and BlackBerry support the PNG image format so I added support for Android.

Also, iOS and BB use targetWidth/targetHeight to specify the resolution of the image.  I've swiched from using maxResolution to targetWidth/targetHeight in this change list.
2011-07-26 12:18:04 +08:00
macdonst
411288b051 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-25 11:05:38 -04:00
macdonst
1c97467e39 Issue #169: Media.seekTo() does not update Media._position value.
Calling Media.seekTo() now updates the Media._position value. I could not make seekTo() to work when your audio clip is not playing as that is not a supported action of the AndroidMedia player class.
2011-07-23 03:21:45 +08:00
macdonst
7c39edfcd0 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-21 22:31:26 -04:00
macdonst
9f673db79f Issue #176: rc2 for Android does not have updated main.js - demo app code
This commit fixes the issue in main.js but it will still need to be packaged up in the rc3 zip.
2011-07-22 10:06:43 +08:00
Dave Johnson
8c807315e9 there was a "Location" and a "Geolocation" plugin defined. We use "Geolocation" in the JavaScript 2011-07-21 16:42:59 -07:00
Joe Bowser
be48b576d9 Merge branch 'master' of github.com:phonegap/phonegap-android 2011-07-21 13:16:09 -07:00
Joe Bowser
648e56cb67 Merging maxResolution 2011-07-21 13:15:52 -07:00
macdonst
b37defd3cf Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-21 15:18:10 -04:00
macdonst
c093881f54 Issue #174: contact attribs should return null instead of empty array
Currently the implementation will return an empty array for the following Contact attributes: phoneNumbers, emails, addresses, ims, organizations, addresses, websites and photos.  With this fix these attributes will be null unless the lenght of the array is greater than 0.
2011-07-22 03:16:45 +08:00
Brian LeRoux
e09c728fd0 Edited framework/src/com/phonegap/DroidGap.java via GitHub 2011-07-21 11:47:20 -07:00
macdonst
d098b6133e Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-21 11:59:41 -04:00
macdonst
029ddeade0 Fix Issue #170: Sub pages reload on orientation or keyboard changes
Needed to add android:configChanges="orientation|keyboardHidden" to the com.phonegap.DroidGap activity in AndroidManifest.xml.
2011-07-20 23:21:34 +08:00
macdonst
17ccb2db4d Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-20 11:13:33 -04:00
Bryce Curtis
6b84ead393 Issue #167: Remove window.app and use navigator.app instead. Remove App() from namespace. 2011-07-19 11:00:13 -05:00
macdonst
9bf3a82964 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-18 16:11:51 -04:00
macdonst
7afa2d3840 Upping version to 1.0.0rc2 2011-07-19 04:11:17 +08:00
macdonst
e789349c23 Handle content:// uri's in resolveLocalFileSystemURI
Sometimes Android will hand you a content:// uri in the place of a file path. Particularily the Camera.getPicture() code will do this. I've updated the file utils code to handle this type of uri and return a real file path.
2011-07-19 04:10:55 +08:00
macdonst
f7ebc98375 Upping version to 1.0.0rc2 2011-07-18 12:48:26 -04:00
Bryce Curtis
2787a960d8 Issue #153: Display default value in prompt(). 2011-07-16 15:07:34 -05:00
Dave Johnson
4af8952dc3 Merge pull request #161 from don/master
Write error to log when plugins.xml is missing
2011-07-15 22:03:42 -07:00
Don Coleman
701717fd55 remove comment from xml declaration 2011-07-15 23:47:08 -04:00
Don Coleman
3de2084af2 write error to log when plugins.xml is missing 2011-07-15 23:45:32 -04:00
Joe Bowser
6d532c9fe5 Minor change to Android Manifest for Android 3.2 to force it out of Compatibltiy Mode 2011-07-15 16:19:08 -07:00
macdonst
eb0e0d9d11 Issue #156: Camera.DestinationType.FILE_URI on Android not conforming to API Spec
Instead of capturing the orginal image to /sdcard/Pic.jpg or /sdcard/Capture.jpg we detect if the SD card is mounted. If mounted the file is placed in the apps temp directory at:

/sdcard/Android/data/{package name}/cache/

If the SD card is not mounted we default to internal storage at:

/data/data/{package name}/cache/
2011-07-16 05:03:48 +08:00
Bryce Curtis
53410781e4 Issue #154: Propagate existing parameters when starting new DroidGap activity. 2011-07-15 15:00:12 -05:00
Bryce Curtis
2bee9a8442 Remove deprecated addService() method. The new way to register a plugin is to include it in res/xml/plugins.xml. 2011-07-15 14:26:20 -05:00
Bryce Curtis
7c9eca1fab Replace deprecated call to activityStop. 2011-07-15 14:04:46 -05:00
Bryce Curtis
017fa1b917 Use the same database based upon application context for each html page loaded as part of this app. 2011-07-13 16:48:29 -05:00
Bryce Curtis
24bb836221 Shouldn't return error on first call. This prevents successful invocation, which prevents deviceready from firing. 2011-07-13 00:21:34 -05:00
macdonst
6993c7edd1 Merge pull request #152 from macdonst/issue151
Issue151: Remove minification of phonegap.*.js file
2011-07-12 15:03:25 -07:00
macdonst
4cc3772e84 Issue #151: Remove minification of phonegap.*.js file 2011-07-12 15:33:46 -04:00
macdonst
2a3c387ce3 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-12 12:23:34 -04:00
macdonst
c80ddc1b22 Issue #149: Deprecate support for Android 1.X devices
Right now we are just removing the code for Contacts on 1.5/1.6 devices. We still need to keep around our implementation of Geolocation and Storage for older devices since some versions of Android have broken implementations of these features. Android 3.0 I'm looking at you!
2011-07-13 00:18:38 +08:00
macdonst
e1092590d6 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-12 11:05:56 -04:00
macdonst
24f979394f Remove deprecated Network.isReachable (use the Network Information API instead) 2011-07-12 23:01:42 +08:00
macdonst
9971d61601 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-12 10:58:28 -04:00
Bryce Curtis
6c65a6a016 Make sure we load the correct resource id for plugins.xml. 2011-07-11 22:56:20 -05:00
macdonst
37b9cc47bd Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-11 23:30:41 -04:00
macdonst
66f7afbed2 Issue #146: File API - File::writeAsText not in the API, remove 2011-07-12 11:13:24 +08:00
macdonst
b1c0be3dd6 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-11 22:53:46 -04:00
Dave Johnson
60be45b9d9 Fix example to not include the min.js that is non-existent 2011-07-11 18:21:55 -07:00
macdonst
393bd5a1f5 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-11 17:28:56 -04:00
macdonst
1de036a744 Setting version as 1.0.0rc1 2011-07-12 05:25:53 +08:00
macdonst
a235513991 Setting version as 1.0.0rc1 2011-07-11 17:24:33 -04:00
macdonst
22a9cabeb9 Updating Contacts for June 16th W3C Spec 2011-07-11 23:18:24 +08:00
Bryce Curtis
5de4ae7554 Remove dependency on notification.activityStart/Stop so they can be deprecated to an optional plugin. Also remove hideLoadingDialogOnPage option, since it no longer is relevant. 2011-07-08 23:07:22 -05:00
Bryce Curtis
f19d8f9bba Change to PhoneGap naming convention affects droidgap creation & update. Also, create script uses phonegap-x.js, not phonegap-x.min.js. 2011-07-08 16:27:57 -05:00
macdonst
d4ccc702b2 Merge pull request #145 from macdonst/fixJar
Update .gitignore and .jar file name
2011-07-08 13:44:45 -07:00
macdonst
a0c748620a Update .gitignore and .jar file name 2011-07-08 16:40:55 -04:00
macdonst
b85a769372 Merge pull request #144 from macdonst/is129
Fix for #129: PhoneGap JS Naming Convention
2011-07-08 12:03:26 -07:00
macdonst
dd52081deb Fix for #129: PhoneGap JS Naming Convention 2011-07-08 14:35:31 -04:00
macdonst
ebd92a4b12 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-07 16:49:22 -04:00
Bryce Curtis
0a7a77e77b Accept IPlugin for result callback when starting activities for result. 2011-07-07 14:11:03 -05:00
Bryce Curtis
9d1e73656f Merge pull request #138 from davejohnson/iplugin-fix
Update PluginManager to accept plugins implementing IPlugin too
2011-07-07 12:00:36 -07:00
macdonst
6e62a76564 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-07 13:08:25 -04:00
macdonst
3895570edd Fixed Issue 140: Contact.displayName is not being set correctly 2011-07-08 01:06:27 +08:00
Fil Maj
28b01fe494 Fix for #120: zooming in on input elements when filling out forms. Note, you also have to set initial-scale and maximum-scale properties in your meta name=viewport tag. 2011-07-07 09:19:39 -07:00
macdonst
709eacd9dc Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-07-07 11:49:24 -04:00
Dave Johnson
090890b22a Update PluginManager to accept plugins that implement IPlugin rather than extend Plugin 2011-07-07 00:12:44 -07:00
Bryce Curtis
ce9d577415 Remove unused code and variable. 2011-07-06 13:56:49 -05:00
Bryce Curtis
cfc9631873 Copy plugins.xml when creating a new project. 2011-07-06 13:36:29 -05:00
Bryce Curtis
3bf48f82af Merge pull request #136 from brycecurtis/plugins
Loading plugins from res/xml/plugins.xml
2011-07-06 11:12:20 -07:00
Dave Johnson
f9bcf71a7a Merge pull request #134 from davejohnson/plugin-refactor
Plugin refactor
2011-07-06 11:10:00 -07:00
Bryce Curtis
9d5aa9406c Loading plugins from res/xml/plugins.xml 2011-07-05 23:21:32 -05:00
Dave Johnson
0b1e760fc1 Add better support for Activity result callbacks from plugins. Add some sugar for calling success / error callbacks from plugins 2011-07-05 14:13:55 -07:00
Dave Johnson
941b64f6a2 Merge branch 'master' of github.com:phonegap/phonegap-android 2011-06-30 13:43:06 -07:00
macdonst
7cc1cc4a3f Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-30 15:43:50 -04:00
Bryce Curtis
c98b758e94 Update version to 0.9.6.1 2011-06-30 13:15:30 -05:00
macdonst
102d37d48a Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-30 11:58:12 -04:00
Bryce Curtis
4b647fc58d Merge pull request #128 from brycecurtis/pages
Update version number and fix issues with prompt().
2011-06-30 08:56:53 -07:00
Bryce Curtis
b7156c6803 Bump up version to 0.9.6 for example. 2011-06-30 10:53:23 -05:00
Bryce Curtis
b8cc36e805 Don't clear activity stack by default. 2011-06-30 10:42:27 -05:00
Bryce Curtis
76b2df208e Add comment 2011-06-29 18:28:29 -05:00
Bryce Curtis
9643314553 Add more control over how url is loaded. 2011-06-29 18:25:49 -05:00
Bryce Curtis
1e3422ae70 Load new urls in new DroidGap activity - not same webview as initial url. 2011-06-29 18:23:20 -05:00
Bryce Curtis
fc1bea4947 Update version to 0.9.6. 2011-06-29 16:43:52 -05:00
macdonst
afb48e52b6 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-29 09:55:31 -04:00
Fil Maj
b5dc62d9ef fix to build script: gotta strip out new lines from read in version string 2011-06-28 15:20:13 -07:00
macdonst
2a786044de Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-28 14:26:50 -04:00
macdonst
5c8913351c Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-27 12:34:33 -04:00
macdonst
226e4dddcd Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-24 16:43:31 -04:00
macdonst
66dbd9ef8b Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-24 10:56:05 -04:00
macdonst
1a9471a2e0 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-22 14:48:49 -04:00
macdonst
5e733ede9f Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-16 09:57:02 -04:00
macdonst
e5fb0c0e71 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-14 09:59:19 -04:00
macdonst
b46cbfd673 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-08 15:17:36 -04:00
macdonst
fbb6b22de6 Updating Network Connection API to match spec released on June 7th 2011-06-08 15:03:03 -04:00
Dave Johnson
8f7a5decb6 Add DS_Store to gitignore 2011-05-25 17:28:24 -07:00
Ron Reiter
307f9d1871 Add maxResolution flag 2011-04-04 02:57:10 +03:00
37 changed files with 1092 additions and 1742 deletions

5
.gitignore vendored
View File

@@ -1,8 +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
.DS_Store
framework/assets/www/phonegap-*.js
.DS_Store

View File

@@ -1 +1 @@
0.9.6
1.0.0

View File

@@ -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.5.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.0.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>

View File

@@ -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(
navigator.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() {

View File

@@ -5,6 +5,7 @@
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true"
android:resizeable="true"
android:anyDensity="true"
/>
@@ -30,15 +31,19 @@
<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>

View File

@@ -8,6 +8,7 @@
if (!PhoneGap.hasResource("app")) {
PhoneGap.addResource("app");
(function() {
/**
* Constructor
@@ -58,16 +59,6 @@ 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.
@@ -89,6 +80,7 @@ App.prototype.exitApp = function() {
};
PhoneGap.addConstructor(function() {
navigator.app = window.app = new App();
navigator.app = new App();
});
}());
}

View File

@@ -34,6 +34,21 @@ Camera.DestinationType = {
};
Camera.prototype.DestinationType = Camera.DestinationType;
/**
* Encoding of image returned from getPicture.
*
* Example: navigator.camera.getPicture(success, fail,
* { quality: 80,
* destinationType: Camera.DestinationType.DATA_URL,
* sourceType: Camera.PictureSourceType.CAMERA,
* encodingType: Camera.EncodingType.PNG})
*/
Camera.EncodingType = {
JPEG: 0, // Return JPEG encoded image
PNG: 1 // Return PNG encoded image
};
Camera.prototype.EncodingType = Camera.EncodingType;
/**
* Source to getPicture from.
*
@@ -78,6 +93,12 @@ Camera.prototype.getPicture = function(successCallback, errorCallback, options)
if (options.quality) {
quality = this.options.quality;
}
var maxResolution = 0;
if (options.maxResolution) {
maxResolution = this.options.maxResolution;
}
var destinationType = Camera.DestinationType.DATA_URL;
if (this.options.destinationType) {
destinationType = this.options.destinationType;
@@ -86,7 +107,32 @@ Camera.prototype.getPicture = function(successCallback, errorCallback, options)
if (typeof this.options.sourceType === "number") {
sourceType = this.options.sourceType;
}
PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType]);
var encodingType = Camera.EncodingType.JPEG;
if (typeof options.encodingType == "number") {
encodingType = this.options.encodingType;
}
var targetWidth = -1;
if (typeof options.targetWidth == "number") {
targetWidth = options.targetWidth;
} else if (typeof options.targetWidth == "string") {
var width = new Number(options.targetWidth);
if (isNaN(width) === false) {
targetWidth = width.valueOf();
}
}
var targetHeight = -1;
if (typeof options.targetHeight == "number") {
targetHeight = options.targetHeight;
} else if (typeof options.targetHeight == "string") {
var height = new Number(options.targetHeight);
if (isNaN(height) === false) {
targetHeight = height.valueOf();
}
}
PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType]);
};
PhoneGap.addConstructor(function() {

View File

@@ -21,17 +21,14 @@ PhoneGap.addResource("contact");
* @param {Array.<ContactAddress>} addresses array of addresses
* @param {Array.<ContactField>} ims instant messaging user ids
* @param {Array.<ContactOrganization>} organizations
* @param {DOMString} revision date contact was last updated
* @param {DOMString} birthday contact's birthday
* @param {DOMString} gender contact's gender
* @param {DOMString} note user notes about contact
* @param {Array.<ContactField>} photos
* @param {Array.<ContactField>} categories
* @param {Array.<ContactField>} urls contact's web sites
* @param {DOMString} timezone the contacts time zone
*/
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;
@@ -42,14 +39,11 @@ 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;
};
/**
@@ -66,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;
/**
@@ -81,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 {
@@ -197,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;
@@ -219,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;
@@ -243,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]);
}
};
/**
@@ -267,7 +273,7 @@ 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.
* 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
@@ -276,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;
@@ -287,23 +293,18 @@ Contacts.prototype.cast = function(pluginResult) {
* @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();
}
});
}

View File

@@ -92,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]);
};
@@ -176,7 +172,7 @@ FileReader.prototype.abort = function() {
}
// If abort callback
if (typeof this.onabort === "function") {
this.oneabort({"type":"abort", "target":this});
this.onabort({"type":"abort", "target":this});
}
// If load end callback
if (typeof this.onloadend === "function") {
@@ -435,7 +431,7 @@ FileWriter.prototype.abort = function() {
}
// If abort callback
if (typeof this.onabort === "function") {
this.oneabort({"type":"abort", "target":this});
this.onabort({"type":"abort", "target":this});
}
this.readyState = FileWriter.DONE;

View File

@@ -63,6 +63,7 @@ var Media = function(src, successCallback, errorCallback, statusCallback, positi
// Media messages
Media.MEDIA_STATE = 1;
Media.MEDIA_DURATION = 2;
Media.MEDIA_POSITION = 3;
Media.MEDIA_ERROR = 9;
// Media states
@@ -187,7 +188,6 @@ PhoneGap.Media.getMediaObject = function(id) {
*/
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) {
@@ -207,5 +207,8 @@ PhoneGap.Media.onStatus = function(id, msg, value) {
media.errorCallback(value);
}
}
else if (msg == Media.MEDIA_POSITION) {
media._position = value;
}
};
}

View File

@@ -9,56 +9,6 @@
if (!PhoneGap.hasResource("network")) {
PhoneGap.addResource("network");
/**
* This class contains information about any NetworkStatus.
* @constructor
*/
var NetworkStatus = function() {
//this.code = null;
//this.message = "";
};
NetworkStatus.NOT_REACHABLE = 0;
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
/**
* This class provides access to device Network data (reachability).
* @constructor
*/
var Network = function() {
/**
* 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;
};
/**
* Determine if a URI is reachable over the network.
* @param {Object} uri
* @param {Function} callback
* @param {Object} options (isIpAddress:boolean)
*/
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]);
};
/**
* This class contains information about the current network Connection.
* @constructor
@@ -123,7 +73,7 @@ Connection.prototype.getInfo = function(successCallback, errorCallback) {
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();

View File

@@ -365,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
@@ -928,20 +924,4 @@ PhoneGap.includeJavascript = function(jsfile, successCallback) {
id.appendChild(el);
};
/**
* This class is provided to bridge the gap between the way plugins were setup in 0.9.3 and 0.9.4.
* Users should be calling navigator.add.addService() instead of PluginManager.addService().
* @class
* @deprecated
*/
var PluginManager = {
addService: function(serviceType, className) {
try {
navigator.app.addService(serviceType, className);
} catch (e) {
console.log("Error adding service "+serviceType+": "+e);
}
}
};
}

View File

@@ -1,7 +1,7 @@
<html>
<head>
<title></title>
<script src="phonegap.0.9.5.min.js"></script>
<script src="phonegap-1.0.0.js"></script>
</head>
<body>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="PhoneGap" default="help">
<project name="PhoneGap" default="jar">
<!-- LOAD VERSION -->
<loadfile property="version" srcFile="../VERSION">
@@ -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">

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<plugins>
<plugin name="App" value="com.phonegap.App"/>
<plugin name="Geolocation" value="com.phonegap.GeoBroker"/>
<plugin name="Device" value="com.phonegap.Device"/>
<plugin name="Accelerometer" value="com.phonegap.AccelListener"/>
<plugin name="Compass" value="com.phonegap.CompassListener"/>
<plugin name="Media" value="com.phonegap.AudioHandler"/>
<plugin name="Camera" value="com.phonegap.CameraLauncher"/>
<plugin name="Contacts" value="com.phonegap.ContactManager"/>
<plugin name="Crypto" value="com.phonegap.CryptoHandler"/>
<plugin name="File" value="com.phonegap.FileUtils"/>
<plugin name="Network Status" value="com.phonegap.NetworkManager"/>
<plugin name="Notification" value="com.phonegap.Notification"/>
<plugin name="Storage" value="com.phonegap.Storage"/>
<plugin name="Temperature" value="com.phonegap.TempListener"/>
<plugin name="FileTransfer" value="com.phonegap.FileTransfer"/>
<plugin name="Capture" value="com.phonegap.Capture"/>
</plugins>

View File

@@ -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.

View File

@@ -11,11 +11,12 @@ import java.io.File;
import java.io.IOException;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaRecorder;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaRecorder;
import android.os.Environment;
import android.util.Log;
/**
* This class implements the audio playback and recording capabilities used by PhoneGap.
@@ -28,7 +29,9 @@ import android.os.Environment;
*/
public class AudioPlayer implements OnCompletionListener, OnPreparedListener, OnErrorListener {
// AudioPlayer states
private static final String LOG_TAG = "AudioPlayer";
// AudioPlayer states
private static int MEDIA_NONE = 0;
private static int MEDIA_STARTING = 1;
private static int MEDIA_RUNNING = 2;
@@ -38,6 +41,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
// AudioPlayer message ids
private static int MEDIA_STATE = 1;
private static int MEDIA_DURATION = 2;
private static int MEDIA_POSITION = 3;
private static int MEDIA_ERROR = 9;
// AudioPlayer error codes
@@ -99,7 +103,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
*/
public void startRecording(String file) {
if (this.mPlayer != null) {
System.out.println("AudioPlayer Error: Can't record in play mode.");
Log.d(LOG_TAG, "AudioPlayer Error: Can't record in play mode.");
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PLAY_MODE_SET+");");
}
@@ -124,7 +128,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STARTING_RECORDING+");");
}
else {
System.out.println("AudioPlayer Error: Already recording.");
Log.d(LOG_TAG, "AudioPlayer Error: Already recording.");
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_ALREADY_RECORDING+");");
}
}
@@ -166,7 +170,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
*/
public void startPlaying(String file) {
if (this.recorder != null) {
System.out.println("AudioPlayer Error: Can't play in record mode.");
Log.d(LOG_TAG, "AudioPlayer Error: Can't play in record mode.");
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RECORD_MODE_SET+");");
}
@@ -225,7 +229,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.setState(MEDIA_RUNNING);
}
else {
System.out.println("AudioPlayer Error: startPlaying() called during invalid state: "+this.state);
Log.d(LOG_TAG, "AudioPlayer Error: startPlaying() called during invalid state: "+this.state);
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_RESUME_STATE+");");
}
}
@@ -237,6 +241,8 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
public void seekToPlaying(int milliseconds) {
if (this.mPlayer != null) {
this.mPlayer.seekTo(milliseconds);
Log.d(LOG_TAG, "Send a onStatus update for the new seek");
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_POSITION+", "+milliseconds/1000.0f+");");
}
}
@@ -251,7 +257,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.setState(MEDIA_PAUSED);
}
else {
System.out.println("AudioPlayer Error: pausePlaying() called during invalid state: "+this.state);
Log.d(LOG_TAG, "AudioPlayer Error: pausePlaying() called during invalid state: "+this.state);
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_PAUSE_STATE+");");
}
}
@@ -265,7 +271,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.setState(MEDIA_STOPPED);
}
else {
System.out.println("AudioPlayer Error: stopPlaying() called during invalid state: "+this.state);
Log.d(LOG_TAG, "AudioPlayer Error: stopPlaying() called during invalid state: "+this.state);
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_ERROR+", "+MEDIA_ERROR_STOP_STATE+");");
}
}
@@ -286,7 +292,9 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
*/
public long getCurrentPosition() {
if ((this.state == MEDIA_RUNNING) || (this.state == MEDIA_PAUSED)) {
return this.mPlayer.getCurrentPosition();
int curPos = this.mPlayer.getCurrentPosition();
this.handler.sendJavascript("PhoneGap.Media.onStatus('" + this.id + "', "+MEDIA_POSITION+", "+curPos/1000.0f+");");
return curPos;
}
else {
return -1;
@@ -386,7 +394,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* @param arg2 an extra code, specific to the error.
*/
public boolean onError(MediaPlayer mPlayer, int arg1, int arg2) {
System.out.println("AudioPlayer.onError(" + arg1 + ", " + arg2+")");
Log.d(LOG_TAG, "AudioPlayer.onError(" + arg1 + ", " + arg2+")");
// TODO: Not sure if this needs to be sent?
this.mPlayer.stop();
@@ -409,5 +417,4 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.state = state;
}
}

View File

@@ -26,8 +26,7 @@ import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
/**
* This class launches the camera view, allows the user to take a picture, closes the camera view,
@@ -43,7 +42,12 @@ public class CameraLauncher extends Plugin {
private static final int CAMERA = 1; // Take picture from camera
private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android)
private static final int JPEG = 0; // Take a picture of type JPEG
private static final int PNG = 1; // Take a picture of type PNG
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
private int targetWidth; // desired width of the image
private int targetHeight; // desired height of the image
private Uri imageUri; // Uri of captured image
public String callbackId;
@@ -76,8 +80,18 @@ public class CameraLauncher extends Plugin {
if (args.length() > 2) {
srcType = args.getInt(2);
}
if (args.length() > 3) {
this.targetWidth = args.getInt(3);
}
if (args.length() > 4) {
this.targetHeight = args.getInt(4);
}
int encodingType = JPEG;
if (args.length() > 5) {
encodingType = args.getInt(5);
}
if (srcType == CAMERA) {
this.takePicture(args.getInt(0), destType);
this.takePicture(args.getInt(0), destType, encodingType);
}
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
this.getImage(args.getInt(0), srcType, destType);
@@ -111,7 +125,7 @@ public class CameraLauncher extends Plugin {
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
* @param returnType Set the type of image to return.
*/
public void takePicture(int quality, int returnType) {
public void takePicture(int quality, int returnType, int encodingType) {
this.mQuality = quality;
// Display camera
@@ -119,14 +133,30 @@ 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 = createCaptureFile(encodingType);
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
this.imageUri = Uri.fromFile(photo);
this.ctx.startActivityForResult((Plugin) this, intent, (CAMERA+1)*16 + returnType+1);
}
/**
* Create a file in the applications temporary directory based upon the supplied encoding.
*
* @param encodingType of the image to be taken
* @return a File object pointing to the temporary picture
*/
private File createCaptureFile(int encodingType) {
File photo = null;
if (encodingType == JPEG) {
photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.jpg");
} else {
photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Pic.png");
}
return photo;
}
/**
* Get image from photo library.
*
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
@@ -145,6 +175,50 @@ public class CameraLauncher extends Plugin {
new String("Get Picture")), (srcType+1)*16 + returnType + 1);
}
/**
* Scales the bitmap according to the requested size.
*
* @param bitmap The bitmap to scale.
* @return Bitmap A new Bitmap object of the same bitmap after scaling.
*/
public Bitmap scaleBitmap(Bitmap bitmap) {
int newWidth = this.targetWidth;
int newHeight = this.targetHeight;
int origWidth = bitmap.getWidth();
int origHeight = bitmap.getHeight();
// If no new width or height were specified return the original bitmap
if (newWidth <= 0 && newHeight <= 0) {
return bitmap;
}
// Only the width was specified
else if (newWidth > 0 && newHeight <= 0) {
newHeight = (newWidth * origHeight) / origWidth;
}
// only the height was specified
else if (newWidth <= 0 && newHeight > 0) {
newWidth = (newHeight * origWidth) / origHeight;
}
// If the user specified both a positive width and height
// (potentially different aspect ratio) then the width or height is
// scaled so that the image fits while maintaining aspect ratio.
// Alternatively, the specified width and height could have been
// kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
// would result in whitespace in the new image.
else {
double newRatio = newWidth / (double)newHeight;
double origRatio = origWidth / (double)origHeight;
if (origRatio > newRatio) {
newHeight = (newWidth * origHeight) / origWidth;
} else if (origRatio < newRatio) {
newWidth = (newHeight * origWidth) / origHeight;
}
}
return Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
}
/**
* Called when the camera view exits.
*
@@ -161,7 +235,6 @@ public class CameraLauncher extends Plugin {
// If CAMERA
if (srcType == CAMERA) {
// If image available
if (resultCode == Activity.RESULT_OK) {
try {
@@ -175,6 +248,8 @@ public class CameraLauncher extends Plugin {
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
}
bitmap = scaleBitmap(bitmap);
// If sending base64 image back
if (destType == DATA_URL) {
this.processPicture(bitmap);
@@ -237,6 +312,7 @@ public class CameraLauncher extends Plugin {
if (destType == DATA_URL) {
try {
Bitmap bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
bitmap = scaleBitmap(bitmap);
this.processPicture(bitmap);
bitmap.recycle();
bitmap = null;

View File

@@ -95,7 +95,13 @@ public class Capture extends Plugin {
obj.put("duration", 0);
obj.put("codecs", "");
if (mimeType.equals("image/jpeg")) {
// 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")) {
@@ -168,7 +174,7 @@ public class Capture extends Plugin {
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// Specify file so that large image is captured and returned
File photo = new File(Environment.getExternalStorageDirectory(), "Capture.jpg");
File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Capture.jpg");
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
this.imageUri = Uri.fromFile(photo);

View File

@@ -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);
@@ -196,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.
*/

View File

@@ -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;
}
}

View File

@@ -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
@@ -390,13 +425,28 @@ public class ContactAccessorSdk5 extends ContactAccessor {
JSONArray addresses, JSONArray phones, JSONArray emails,
JSONArray ims, JSONArray websites, JSONArray photos) {
try {
contact.put("organizations", organizations);
contact.put("addresses", addresses);
contact.put("phoneNumbers", phones);
contact.put("emails", emails);
contact.put("ims", ims);
contact.put("websites", websites);
contact.put("photos", photos);
// Only return the array if it has at least one entry
if (organizations.length() > 0) {
contact.put("organizations", organizations);
}
if (addresses.length() > 0) {
contact.put("addresses", addresses);
}
if (phones.length() > 0) {
contact.put("phoneNumbers", phones);
}
if (emails.length() > 0) {
contact.put("emails", emails);
}
if (ims.length() > 0) {
contact.put("ims", ims);
}
if (websites.length() > 0) {
contact.put("websites", websites);
}
if (photos.length() > 0) {
contact.put("photos", photos);
}
}
catch (JSONException e) {
Log.e(LOG_TAG,e.getMessage(),e);
@@ -416,15 +466,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 +534,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 +627,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 +655,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 +675,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 +823,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 +860,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 +882,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 +1023,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 +1040,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 +1069,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 +1083,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 +1242,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 +1253,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 +1303,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 +1321,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 +1436,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>();
@@ -1461,18 +1603,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
@@ -1643,58 +1788,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;
}
}

View File

@@ -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,38 +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")) {
if (contactAccessor.save(args.getJSONObject(0))) {
return new PluginResult(status, result);
}
else {
JSONObject r = new JSONObject();
r.put("code", 0);
return new PluginResult(PluginResult.Status.ERROR, r);
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);

2
framework/src/com/phonegap/Device.java Executable file → Normal file
View File

@@ -18,7 +18,7 @@ import android.provider.Settings;
public class Device extends Plugin {
public static String phonegapVersion = "0.9.5"; // PhoneGap version
public static String phonegapVersion = "1.0.0"; // PhoneGap version
public static String platform = "Android"; // Device OS
public static String uuid; // Device UUID

View File

@@ -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();
}
}

225
framework/src/com/phonegap/DroidGap.java Executable file → Normal file
View File

@@ -7,11 +7,15 @@
*/
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;
@@ -46,7 +50,7 @@ import android.widget.EditText;
import android.widget.LinearLayout;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.IPlugin;
import com.phonegap.api.PluginManager;
/**
@@ -91,10 +95,6 @@ import com.phonegap.api.PluginManager;
* // (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);
@@ -126,20 +126,19 @@ 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 initial URL for our app up to and including the file name
// ie http://server/path/index.html
private String urlFile;
// 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
protected Plugin activityResultCallback = null;
protected IPlugin activityResultCallback = null;
protected boolean activityResultKeepRunning;
// Flag indicates that a loadUrl timeout occurred
@@ -148,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.
@@ -240,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);
@@ -289,23 +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");
this.addService("Capture", "com.phonegap.Capture");
}
/**
@@ -325,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);
@@ -348,14 +323,15 @@ public class DroidGap extends PhonegapActivity {
*/
public void loadUrl(final String url) {
System.out.println("loadUrl("+url+")");
this.urlFile = this.getUrlFile(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);
@@ -388,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
@@ -613,7 +586,7 @@ 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.
@@ -723,20 +696,88 @@ public class DroidGap extends PhonegapActivity {
this.callbackServer.sendJavascript(statement);
}
/**
* Return up to file part of url.
* If url = http://server/page.html#abc, then return http://server/page.html
* Display a new browser with the specified URL.
*
* @param url
* @return
* 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
*/
private String getUrlFile(String url) {
int p1 = url.indexOf("#");
int p2 = url.indexOf("?");
if (p1 < 0) p1 = url.length();
if (p2 < 0) p2 = url.length();
int p3 = (p1 < p2) ? p1 : p2;
return url.substring(0, p3);
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;
}
}
/**
@@ -825,8 +866,11 @@ 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 (this.ctx.urlFile.equals(this.ctx.getUrlFile(url))) {
if (url.indexOf(this.ctx.baseUrl) == 0) {
reqOk = true;
}
@@ -877,6 +921,9 @@ public class DroidGap extends PhonegapActivity {
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,
@@ -1069,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", null);
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
@@ -1120,10 +1178,7 @@ public class DroidGap extends PhonegapActivity {
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) {
@@ -1156,7 +1211,7 @@ 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);
@@ -1250,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);
}
/**
@@ -1266,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;
@@ -1291,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.

View File

@@ -18,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;
@@ -104,9 +106,6 @@ public class FileUtils extends Plugin {
else if (action.equals("readAsDataURL")) {
String s = this.readAsDataURL(args.getString(0));
return new PluginResult(status, s);
}
else if (action.equals("writeAsText")) {
this.writeAsText(args.getString(0), args.getString(1), args.getBoolean(2));
}
else if (action.equals("write")) {
long fileSize = this.write(args.getString(0), args.getString(1), args.getInt(2));
@@ -231,22 +230,34 @@ public class FileUtils extends Plugin {
*/
private JSONObject resolveLocalFileSystemURI(String url) throws IOException, JSONException {
String decoded = URLDecoder.decode(url, "UTF-8");
// Test to see if this is a valid URL first
@SuppressWarnings("unused")
URL testUrl = new URL(decoded);
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);
}
}
File fp = null;
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();
}
if (!fp.exists()) {
throw new FileNotFoundException();
}
if (!fp.canRead()) {
throw new IOException();
}
return getEntry(fp);
}
@@ -914,26 +925,6 @@ public class FileUtils extends Plugin {
return map.getMimeTypeFromExtension(map.getFileExtensionFromUrl(filename));
}
/**
* Write contents of file.
*
* @param filename The name of the file.
* @param data The contents of the file.
* @param append T=append, F=overwrite
* @throws FileNotFoundException, IOException
*/
public void writeAsText(String filename, String data, boolean append) throws FileNotFoundException, IOException {
String FilePath= filename;
byte [] rawData = data.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(rawData);
FileOutputStream out= new FileOutputStream(FilePath, append);
byte buff[] = new byte[rawData.length];
in.read(buff, 0, buff.length);
out.write(buff, 0, rawData.length);
out.flush();
out.close();
}
/**
* Write contents of file.
*

View File

@@ -7,10 +7,7 @@
*/
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;
@@ -102,32 +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);
}
else if (action.equals("getConnectionInfo")) {
this.connectionCallbackId = callbackId;
NetworkInfo info = sockMan.getActiveNetworkInfo();
PluginResult pluginResult = new PluginResult(status, this.getConnectionInfo(info));
pluginResult.setKeepCallback(true);
return pluginResult;
}
return new PluginResult(status, result);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
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);
}
/**
@@ -236,66 +219,4 @@ public class NetworkManager extends Plugin {
}
return TYPE_UNKNOWN;
}
/**
* 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.
*
* @return
*/
public boolean isWifiActive() {
NetworkInfo info = sockMan.getActiveNetworkInfo();
if (info != null) {
String type = info.getTypeName();
return type.equals("WIFI");
}
return false;
}
/**
* Determine if a URI is reachable over the network.
*
* @param uri
* @param isIpAddress
* @return
*/
public int isReachable(String uri, boolean isIpAddress) {
int reachable = NOT_REACHABLE;
if (uri.indexOf("http://") == -1 && uri.indexOf("https://") == -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;
}
}
return reachable;
}
}

View File

@@ -66,6 +66,11 @@ public interface IPlugin {
*/
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.
*/

View File

@@ -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);
}

View File

@@ -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
@@ -126,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.
*
@@ -135,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));
}
}

View File

@@ -7,13 +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;
/**
@@ -24,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;
@@ -39,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();
}
}
}
/**
@@ -75,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) {
@@ -101,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));
}
}
@@ -160,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.
@@ -188,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);
@@ -212,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;
}
@@ -234,11 +249,11 @@ public final class PluginManager {
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onPause(boolean multitasking) {
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.onPause(multitasking);
}
}
@@ -249,11 +264,11 @@ public final class PluginManager {
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onResume(boolean multitasking) {
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.onResume(multitasking);
}
}
@@ -262,11 +277,11 @@ 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();
}
}
@@ -275,12 +290,19 @@ public final class PluginManager {
* Called when the activity receives a new intent.
*/
public void onNewIntent(Intent intent) {
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.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("=====================================================================================");
}
}

View File

@@ -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,7 +128,7 @@ class Classic
phonegapjs << IO.read(File.join(js_dir, script))
phonegapjs << "\n\n"
end
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.#{ 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

View File

@@ -35,7 +35,7 @@ class Update
puts "Copying over libraries and assets..."
version = IO.read(File.join(@framework_dir, '../VERSION'))
FileUtils.cp File.join(@framework_dir, "phonegap.#{ version }.jar"), File.join(@path, "libs")
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")
@@ -45,7 +45,7 @@ class Update
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) }
File.open(File.join(@path, "assets", "www", "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
end
#
end

View File

@@ -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

View File

@@ -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.