Compare commits

...

317 Commits

Author SHA1 Message Date
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
Joe Bowser
f3d7ce8fc3 VERSION: 2011-06-27 16:01:43 -07:00
Bryce Curtis
85dab52cf7 Handle errors when adding a service. 2011-06-27 13:49:24 -05:00
Bryce Curtis
c96c9b00b9 Revert to polling if there are any errors with callback server. This addresses various problems with proxies set by carriers. 2011-06-27 13:48:02 -05:00
Joe Bowser
b059a31787 Merge branch 'master' of github.com:phonegap/phonegap-android 2011-06-27 10:23:16 -07:00
Joe Bowser
8cb71673c2 Changing default target to the highest for maximum compatibility 2011-06-27 10:22:57 -07:00
macdonst
5c8913351c Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-27 12:34:33 -04:00
macdonst
8ef93ff0e5 Issue 123: Fixing problem where name object is not specified 2011-06-28 00:34:11 +08:00
Joe Bowser
042c34192b Trying to merge 2011-06-24 14:09:34 -07:00
macdonst
ac2e92321f Issue #121: Problem with resolveLocalFileSystemURI if file name has spaces 2011-06-24 14:08:46 -07:00
Bryce Curtis
05eacf4792 Always call plugin's onPause/onResume with multitasking flag when these lifecycle events occur in activity. It is up to the plugin to handle as necessary. 2011-06-24 14:08:46 -07:00
Kevin Griffin
435c903baf formatting - sigh 2011-06-24 14:08:46 -07:00
Bryce Curtis
53de070a41 Return true when handling key events, indicating that no further processing is necessary. 2011-06-24 14:08:46 -07:00
Bryce Curtis
54fdcbfd46 Urls with same path and file but different # or ? should compare to same url. 2011-06-24 14:08:46 -07:00
Joe Bowser
44aa0aeb0f Adding SSL dev code 2011-06-24 14:08:46 -07:00
Kevin Griffin
7d53eb8e3e formattage 2011-06-24 14:08:46 -07:00
Joe Bowser
7bc0d624ac Fixing conflict 2011-06-24 14:08:26 -07:00
Kevin Griffin
a5039f021d made the mistake of opening anything in finder 2011-06-24 14:06:21 -07:00
Bryce Curtis
80e66d87a9 Issue 112: PhoneGap.Channel: replace instanceof Function with typeof === 'Function' 2011-06-24 14:06:21 -07:00
Bryce Curtis
d35e8cd44b Fix security vulnerability - make sure any requests to run native code only come from url currently loaded into webview. 2011-06-24 14:06:20 -07:00
Benjamin Weingarten
eb3b1f91d4 Fix bug where isreachable doesn't return correct results for https
(http secure) url protocol.
2011-06-24 14:06:20 -07:00
macdonst
8a1ab69235 Updating Network Connection API to match spec released on June 7th 2011-06-24 14:06:20 -07:00
Bryce Curtis
66f3018767 Set PhoneGap.UsePolling flag based upon result from CallbackServer. 2011-06-24 14:06:20 -07:00
macdonst
ff7de25b62 Issue #106: Typo in LocalFileSystem.prototype._castDate 2011-06-24 14:06:20 -07:00
macdonst
85eb6e4997 Issue #104: Bug in FileUtils.copyDirectory & moveDirectory
Adding better test to see if a directory is being moved/copied into itself.

Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
2011-06-24 14:06:20 -07:00
macdonst
0280d5dd82 Updating Connection object to conform with recently released spec
- Replacing currentNW and homeNW with networkName.
- Changing Connection constants to strings instead of ints.
- Firing online/offline events on network change.
2011-06-24 14:06:20 -07:00
Nitobi
3c90a9a23c Fixed droidgap update command 2011-06-24 14:06:20 -07:00
macdonst
088c342198 Improve performance of our encoding
Move from using String.replaceAll() to a modified version or URLEncoder.encode().
2011-06-24 14:06:20 -07:00
macdonst
af18a8e1aa Issue #80: Unable to open large json files on android 2.2 + phonegap 0.9.5
I could not get rid of the url encoding and decoding without hampering some users ability to pass non-ascii characters back to JavaScript.  However, I was able to reduce the amount of data being passed from Java to JavaScript by 40% by decoding common characters that occur in JSON and XML.  These characters will survive the round trip just fine and don't need to be encoded.

This is the best solution I could come up with.  You won't be able to read files as large as you could in 0.9.4 but it will get close and it will support non-ascii characters.
2011-06-24 14:06:20 -07:00
Joe Bowser
11a29e11e1 Merge branch 'master' of github.com:phonegap/phonegap-android 2011-06-24 14:02:02 -07:00
macdonst
226e4dddcd Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-24 16:43:31 -04:00
macdonst
08c44f5c5d Issue #121: Problem with resolveLocalFileSystemURI if file name has spaces 2011-06-25 04:41:19 +08:00
macdonst
66dbd9ef8b Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-24 10:56:05 -04:00
Bryce Curtis
1c3ea54dcb Always call plugin's onPause/onResume with multitasking flag when these lifecycle events occur in activity. It is up to the plugin to handle as necessary. 2011-06-23 23:22:48 -05:00
Bryce Curtis
a65638ab59 Merge pull request #118 from m00sey/master
Always fire onPause, onResume and onNewIntent events for Plugins.
2011-06-23 20:51:35 -07:00
macdonst
1a9471a2e0 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-22 14:48:49 -04:00
Kevin Griffin
c15971253f formatting - sigh 2011-06-22 11:05:33 -04:00
Bryce Curtis
a67dfdb75e Return true when handling key events, indicating that no further processing is necessary. 2011-06-21 22:53:45 -05:00
Bryce Curtis
9b52827744 Urls with same path and file but different # or ? should compare to same url. 2011-06-21 22:50:53 -05:00
Fil Maj
c5b268756b Merge pull request #103 from doggerelverse/droidgap_update
Fixed droidgap update command
2011-06-21 17:22:02 -07:00
Joe Bowser
afa85a74b3 Adding SSL dev code 2011-06-21 10:08:42 -07:00
Kevin Griffin
517b5e0db9 formattage 2011-06-21 11:46:54 -04:00
Kevin Griffin
327bda49a0 Sending pause/resume notifcations to plugins regardless of 'keepRunning' state.
Not sure why you wouldn't want to send them

Added a OnNewIntent override for Plugins to use.
2011-06-21 11:32:22 -04:00
Kevin Griffin
c978341d83 made the mistake of opening anything in finder 2011-06-21 11:32:18 -04:00
macdonst
939754e1c9 Merge pull request #110 from Worklight/isReachble_fix
Fix bug where isReachable doesn't return correct results for https
2011-06-20 09:48:55 -07:00
macdonst
5e733ede9f Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-16 09:57:02 -04:00
Bryce Curtis
d1448e9073 Issue 112: PhoneGap.Channel: replace instanceof Function with typeof === 'Function' 2011-06-15 13:01:03 -05:00
macdonst
e5fb0c0e71 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-14 09:59:19 -04:00
Bryce Curtis
33bfb44f67 Fix security vulnerability - make sure any requests to run native code only come from url currently loaded into webview. 2011-06-13 15:16:08 -05:00
Benjamin Weingarten
a89c8bf482 Fix bug where isreachable doesn't return correct results for https
(http secure) url protocol.
2011-06-12 13:50:49 +03:00
macdonst
b46cbfd673 Merge branch 'master' of git://github.com/phonegap/phonegap-android 2011-06-08 15:17:36 -04:00
macdonst
a67aeed571 Updating Network Connection API to match spec released on June 7th 2011-06-09 03:11:39 +08:00
macdonst
fbb6b22de6 Updating Network Connection API to match spec released on June 7th 2011-06-08 15:03:03 -04:00
Bryce Curtis
315b5a59b3 Set PhoneGap.UsePolling flag based upon result from CallbackServer. 2011-06-07 14:18:18 -05:00
macdonst
8da5ad8eca Issue #106: Typo in LocalFileSystem.prototype._castDate 2011-06-07 22:03:58 +08:00
macdonst
740e50ce61 Issue #104: Bug in FileUtils.copyDirectory & moveDirectory
Adding better test to see if a directory is being moved/copied into itself.

Copy /sdcard/myDir to /sdcard/myDir-backup is okay but
Copy /sdcard/myDir to /sdcard/myDir/backup should thow an INVALID_MODIFICATION_ERR
2011-06-07 03:12:55 +08:00
macdonst
3ce0fc4897 Updating Connection object to conform with recently released spec
- Replacing currentNW and homeNW with networkName.
- Changing Connection constants to strings instead of ints.
- Firing online/offline events on network change.
2011-06-07 02:36:01 +08:00
Nitobi
8d1722ad67 Fixed droidgap update command 2011-06-03 10:59:48 -07:00
macdonst
7d41646a35 Improve performance of our encoding
Move from using String.replaceAll() to a modified version or URLEncoder.encode().
2011-06-02 10:31:56 +08:00
macdonst
992ee0bca4 Issue #80: Unable to open large json files on android 2.2 + phonegap 0.9.5
I could not get rid of the url encoding and decoding without hampering some users ability to pass non-ascii characters back to JavaScript.  However, I was able to reduce the amount of data being passed from Java to JavaScript by 40% by decoding common characters that occur in JSON and XML.  These characters will survive the round trip just fine and don't need to be encoded.

This is the best solution I could come up with.  You won't be able to read files as large as you could in 0.9.4 but it will get close and it will support non-ascii characters.
2011-06-01 21:27:27 +08:00
Joe Bowser
d00a9f33cd Merge branch 'master' of github.com:phonegap/phonegap-android into keyboard 2011-05-31 15:41:03 -07:00
Joe Bowser
431c80782e Changing the layout class so it has the screen dimensions to take into account Device Orientation 2011-05-31 15:38:03 -07:00
Bryce Curtis
39ec9c095d Need to unregister for network intent receiver on shutdown to prevent leaks. 2011-05-31 15:13:54 -05:00
Bryce Curtis
10e1808c56 Clean up CallbackServer when about:blank page has loaded. This fixes errors when shutting down. 2011-05-31 15:11:02 -05:00
macdonst
9036eb8fcc Issue #94: feature request: Event for Keyboard show/hide 2011-05-30 15:12:31 -04:00
Fil Maj
4280fdf252 Fix for ticket #58: Certain 1.5/1.6 devices would throw a FileNotFoundException when taking pictures. Patch submitted by Agustin of AVANTIC (thanks!). 2011-05-26 12:42:44 -07:00
macdonst
ca5e141b5b Changing Media class to return seconds
The media commands getCurrentPosition() and getDuration() will now return seconds (float) instead of milliseconds to line up with iOS and the docs.
2011-05-27 01:54:48 +08:00
Dave Johnson
8f7a5decb6 Add DS_Store to gitignore 2011-05-25 17:28:24 -07:00
macdonst
1d79b6617b JavaScript cleanup to pass jsHint
I did my best to clean up the JavaScript so it would pass through jsHint more cleanly.  There still are issues but there are a lot fewer now.  This helped to make the JS code more consistent.
2011-05-21 02:31:24 +08:00
macdonst
6c3eefe6f9 Issue #85: window.openDatabase throws DOM Exception 18 on Android 3.1
Instead of checking the userAgent catch the exception.  If we do get an exception it will setup our version of Droid Db.
2011-05-21 02:13:03 +08:00
macdonst
2177cd0a39 Moving navigator.connection to navigator.device.connection
Android 2.2 introduces the navigation.connection interface but it does not work properly in WebView.  So in order to get the proper connection information we had to implement our own connection interface which is accessible at navigator.network.connection.
2011-05-18 03:26:31 +08:00
macdonst
6618015151 Fixing a number of issues in File API
Issue #82: The RandomAccessFile class in Android's version of Java does not write non-ASCII characters very well.  I've switched to using a FileOutputStream which seems to work just great.  Tested by myself and folks from Egypt and the Netherlands.

Issue #87: Fixed a problem where the file errors were being returned as evt.target.result.code.code.
2011-05-17 22:11:38 +08:00
macdonst
e99f75d59b Issue #85: window.openDatabase throws DOM Exception 18 on Android 3.1
The way were were detecting we were on an Android 3.0 device was not applicable for Android 3.1.  I've made and update so that any Android 3.X device will use our implementation of web sql databases instead of the built in one which thows a security error.
2011-05-16 23:14:24 +08:00
macdonst
ab8cfe01d0 Removing generated code from project
framework/gen/com/phonegap/R.java
2011-05-16 10:37:25 +08:00
macdonst
e84c59d23c Merge pull request #78 from kernelsandirs/master
Added Media.seekTo(int milliseconds);

Merging this code in now and making some notes to enhance the Media class.
2011-05-15 19:32:02 -07:00
macdonst
e81fc239a7 Setting defaults in Media Capture
Some tests of Media Capture were failing as the CaptureAudio/Image/VideoOptions objects did not have defaults set.
2011-05-14 01:10:18 +08:00
Bryce Curtis
839c577243 Need to use EclairClient for 3.x devices too. This fixes the HTML5 geolocation problem on Android 3.x. 2011-05-10 11:44:09 -05:00
macdonst
116169a4c5 Issue #79: FileWriter.seek() is broken in 0.9.5.
The FileEntry.createWriter() method passes in a FileEntry object instead of a File object. As a result the FileWriter.length was not being set properly so when you do a writer.seek(writer.length) it would go to 0, so your next write would overwrite your file.

In order to fix this issue the FileEntry.createWriter() method now makes a call to FileEntry.file() to get the correct file size.  The File object is now passed to the FileWriter constructor.
2011-05-07 00:41:57 +08:00
macdonst
346ed60f0d Android 2.2+ supports W3C Connection API
For Android version 2.2 or better the navigator.connection object already
exists. If this case we should immediately fire the onPhoneGapConnectionReady
event so we don't tie up the 'deviceready' event.
2011-05-06 04:22:38 +08:00
defrex
bde59adc04 Add docs and fixed to pass through the Google Closure Compiler without warnings 2011-05-04 16:00:03 -07:00
kernelsandirs
ffbc010d7b Added Media.seekTo(int milliseconds); 2011-05-04 11:51:26 -07:00
macdonst
bdadbbc339 Implement W3C Network Information API
Adding a new object to navigator called 'connection'.  Users can query the
connection object to find out what type of network, if any, the device is
connected to.  The connection object will be updated each time there is a
connectivity change on the device.
2011-05-04 22:23:16 +08:00
kernelsandirs
b94eedaf07 Added Media.seekTo(int milliseconds); 2011-05-03 21:27:08 -07:00
macdonst
58ecac335b Capture modifications:
Renaming supportedAudioFormats to supportedAudioModes.
Renaming supportedImageFormats to supportedImageModes.
Renaming supportedVideoFormats to supportedVideoModes.
Adding copywrite header to the Capture.java file.
2011-05-03 00:12:19 +08:00
macdonst
fd8bb2f671 Issue 72: Contact.Save: onSuccess callback is called when contact is not saved. 2011-04-29 02:08:25 +08:00
Bryce Curtis
0aacfbdd50 Update version to 0.9.5 2011-04-27 14:10:13 -05:00
macdonst
2cd116e4e7 Issue 60: Contact search unicode problem
Contact search was not working for unicode letters.  The CallbackServer was changed so that it returned url encode strings.  On the JavaScript side the PhoneGap callback handler decodes the returned string.
2011-04-25 22:22:12 +08:00
Bryce Curtis
673a8871df Ticket 136: window.openDatabase() in Android 3.0 throws SECURITY_ERR (most code written by Simon MacDonald - I just tested and checked in)
When you call window.openDatabase() on an Android 3.0 device you get and error something like this:

E/Web Console( 1791): SECURITY_ERR: DOM Exception 18: An attempt was made to break through the security policy of the user agent.

Simon worked with Pat for a bit and they think this is a WebKit or Android/WebKit interaction bug. In the meantime this fix determines if you are on Android 3.0 and uses Droid_DB if so.
2011-04-19 16:54:16 -05:00
Fil Maj
44945f9d5e Partial resolution for ticket 57: some issues with camera functionality not firing callbacks properly. 2011-04-18 18:12:01 -07:00
Roman
887f754014 Hidden NPE fixed, which appeared when someone pass null as arguments
(for such SQL as e.g. CREATE TABLE).

It is especially important when work with dome 3d party persistemce
libraries, like e.g. http://github.com/zefhemel/persistencejs which
passes these nulls.
2011-04-18 17:02:55 -07:00
macdonst
674015460f Fixing file commands so that they run async 2011-04-13 03:13:10 +08:00
macdonst
40084293c3 Ticket 127: Android FileReader/FileWriter methods should return FileError object on error. 2011-04-07 07:17:41 +08:00
Fil Maj
ed4c57e791 Woops, finger slipped. 2011-04-06 11:01:36 -07:00
Fil Maj
bf164f4161 Fix for ticket 121: Checking for null return on native openDatabase call not enough as only allowed one DB per PhoneGap app. Have to proxy openDatabase and check at runtime. 2011-04-06 10:50:24 -07:00
Bryce Curtis
626119ae3b Bug 126: NullPointerException in onDestroy() 2011-04-05 15:42:25 -05:00
Ron Reiter
307f9d1871 Add maxResolution flag 2011-04-04 02:57:10 +03:00
macdonst
e766188689 W3C Media Capture API
An implementation of the W3C Media Capture spec:
http://dev.w3.org/2009/dap/camera/Overview-API

Capture operations are supported for audio, video, and images.  Each
capture operation launches the native audio recorder, video recorder,
or camera application, respectively.
2011-04-01 15:52:53 -04:00
macdonst
d74569ffa7 Read As Text missing load event call
FileReader.readAsText didn't call the onload callback on success.
2011-04-01 23:04:14 +08:00
macdonst
d424af03e4 Ticket 124: File Transfer multipart badly formed trips mod_security
A standard from has no trailing whitespace after a content-disposition line like so: "Content-Disposition: form-data; name="data";" however when using the extra params of Android FileTransfer a space is added on the end "Content-Disposition: form-data; name="data"; "

This fix simply removes the trailing whitespace.
2011-04-01 22:43:38 +08:00
Bryce Curtis
f6f80537c3 Merge branch 'master' of https://github.com/jos3000/phonegap-android into jos3000-master 2011-03-30 13:48:51 -05:00
Bryce Curtis
908485751b Add check to only init and run JS code once - even if included multiple times. 2011-03-30 13:29:24 -05:00
macdonst
b850d225f4 Support old way of adding service in PhoneGap 0.9.5
PhoneGap 0.9.4 replaced PluginManager.addService() with navigator.app.addService().  This is problematic with the older plugin as they are not being maintained.  I'm adding in a PluginManger JavaScript class which will implement the addService method and call navigator.app.addService() method under the hood.  This way we won't break old code.
2011-03-30 21:04:03 +08:00
Jos Shepherd
010c774988 Added native prompt() dialog support 2011-03-25 16:31:27 +00:00
macdonst
969f0c87d7 PhoneGap Android Ticket 113:
FileTransfer returns FILE_NOT_FOUND_ERR on http 500 error

For some reason on Android if you do a getInputStream() on a HTTP Connection and the server returns a 500 error it will report a FileNotFoundException.  Catching this exception and throwing an IOException so that we can report a more accurate error in JavaScript.
2011-03-24 23:31:12 +08:00
Fil Maj
b3e9794189 Fix for lighthouse ticket 115: certain versions of Android 2.2 return "null" for window.openDatabase. Hook in PhoneGap fallback for storage in this case. 2011-03-23 11:07:45 -07:00
Bryce Curtis
935295c9b8 Bug 110 - When you close an app on Android you see a JS error in logcat. 2011-03-18 17:27:36 -05:00
Fil Maj
04de2052fd As best a fix as can be made for issue 95: on HTC devices, if text input is in bottom half of page, it does not get scrolled up to top half of page when you tap it and virtual keyboard comes up. 2011-03-15 12:46:05 -07:00
Fil Maj
60eb60b4f5 Merge branch 'master' of github.com:phonegap/phonegap-android 2011-03-14 16:15:28 -07:00
Fil Maj
ec307fdda8 Null check in droidgap classic (build script). 2011-03-14 16:15:19 -07:00
Bryce Curtis
7344964c05 Add support for setting sms body using uri "sms:#?body=text". 2011-03-13 22:36:09 -05:00
macdonst
1fc56921aa Ticket #90: Move _createEvent from File to PhoneGap
Got rid of _createEvent from file.js as it is redundant code.
2011-03-10 04:26:11 +08:00
Bryce Curtis
21a34a8980 Ticket 106 - Simplify splash screen logic based upon idea from vadim. 2011-03-08 22:00:33 -06:00
Fil Maj
8d73b364f2 Issue 107: always send resume event to JS. 2011-03-07 16:50:10 -08:00
Fil Maj
fb2c25c6c6 Issue 107: Always send pause event to JS. 2011-03-07 16:48:23 -08:00
Mark Darbyshire
47ca081f36 Implement localStorage.key() and localStorage.length
This brings PhoneGap's implementation in line with the spec at http://dev.w3.org/html5/webstorage/
It makes the following demo work when you include PhoneGap: http://people.w3.org/mike/localstorage.html
I was hopeful it would make my app, which makes use of LawnChair, work, but I've had no such luck as of yet.
2011-03-07 15:55:14 -08:00
Fil Maj
939b70243d Use icon with no width/height if specified. Set default icon to highest-resolution icon when possible. 2011-03-07 13:27:52 -08:00
Fil Maj
d44bb7a9d8 Fix so that if not all icons are specified, doesnt error the build out. 2011-03-07 13:27:52 -08:00
Fil Maj
dccc29daf2 Syntax fixes to my ruby :P 2011-03-07 13:27:52 -08:00
Fil Maj
b402efd1f7 First pass at extracting icon width/height info and assigning to proper resolution dirs (i.e. ldpi, mdpi, hdpi) during build. 2011-03-07 13:27:52 -08:00
macdonst
0c3a8fb9f7 File API: System and Directories
http://www.w3.org/TR/file-system-api/

User can retrieve PERSISTENT and TEMPORARY file systems, list their
contents, and manipulate files and directories within them.

Modify existing FileWriter implementation
-----------------------------------------

  - Change the way user creates a FileWriter.  User must either pass a
    File object to the FileWriter constructor, or use the
    FileEntry.createWriter() method.

  - Drop support for the 'filePath' and 'append' parameters in the
    FileWriter constructor.  The file path is determined from either the
    File object passed to the FileWriter constructor, or the FileEntry
    object used to create the FileWriter.  To append to a file, use the
    FileWriter object's seek method:

    // writer is a FileWriter object
    // seek to length of file to append
    writer.seek(writer.length);

Replace FileMgr JavaScript APIs not specified in any File API spec
------------------------------------------------------------------

  - Remove navigator.fileMgr.createDirectory(dirName) function.  To
    create a directory, use the DirectoryEntry.getDirectory() method,
    which is part of the File API: Directories and System spec.  Set
    the Flags.create to 'true':

    // directory is a DirectoryEntry object
    directory.getDirectory(path, {create:true}, successCB, failCB);

  - Remove navigator.fileMgr.getRootPaths() function.  To retrieve the
    root file systems, use the window.requestFileSystem() function,
    which is part of the File API: Directories and System spec.

  - Remove navigator.fileMgr.getFileProperties(fileName) function.  To
    get the properties of a file, use the FileEntry.file() method, which
    is part of the File API: Directories and System spec.

  - Remove navigator.fileMgr.deleteFile(fileName) function.  To delete a
    file, use the Entry.remove() method, which is part of the File API:
    Directories and System spec.

  - Remove navigator.fileMgr.deleteDirectory(dirName) function.  To
    delete a directory, use the Entry.remove() (if it is empty), or
    DirectoryEntry.removeRecursively() methods, which are part of the
    File API: Directories and System spec.

Clean up existing FileManager native code.  Move some functionality to
file utility class.
2011-03-05 04:26:31 +08:00
paulb777
64d4337d5f Update index.html to Add networking to example 2011-02-28 23:49:14 +08:00
paulb777
a9e1751812 Add networking to example and fix contacts 2011-02-28 23:46:26 +08:00
Bryce Curtis
2bc7bd6768 Worked around JavaScript bridge exception for Android 2.3. Use "prompt" instead of calling objects directly. There were several objects called from JavaScript, including BrowserKey, so key events had to be reworked. 2011-02-27 20:07:24 -06:00
Fil Maj
1711fb07d7 Small patch to build script: ruby needs double quotes to interpolate variables into it properly. 2011-02-16 16:26:01 -08:00
paulb777
6f4673f590 JSLint clean JavaScript sources. No fatal errors remain. Options can turn off rest of warnings 2011-02-15 16:10:09 -08:00
Vadim Voituk
5e858f8bc3 Added CupcakeLocalStorage.clear() method (in according to http://dev.w3.org/html5/webstorage/#the-storage-interface) 2011-02-04 12:08:22 -08:00
Fil Maj
691b093ccd Upped script version in assets. 2011-02-04 11:55:20 -08:00
Fil Maj
99002f9dce Fix for build: version needs to be included in .jar and .js generated files. 2011-02-04 11:35:05 -08:00
Fil Maj
b07072c12b Fix for ticket 86 (build fail if phonegap-android dir is located under a dir with "lib" in it). Also bug fix in build if config.xml didnt contain an <icon> element. 2011-02-04 11:20:22 -08:00
Bryce Curtis
36dd964ba4 Logging status from wrong object. 2011-02-03 21:11:06 -06:00
macdonst
f848527c28 Upping version to 0.9.4 2011-02-03 09:48:31 +08:00
Bryce Curtis
6aa055f46e Change super.setProperty() to use super.set<type>Property() in example comments. 2011-02-02 14:37:09 -06:00
macdonst
7952668cf7 Throwing error on FileWriter.abort() if writer is not in the correct state. Lining up with iPhone and BlackBerry 2011-02-03 02:26:49 +08:00
macdonst
a0c761664d Call onwriteend not onloadend in FileWriter.abort() 2011-02-03 02:08:41 +08:00
macdonst
9fd9cf55cf Adding version number to phonegap jar/js files 2011-02-03 01:51:59 +08:00
macdonst
3c9089b9c7 Enable hardware volume control buttons in DroidGap applications 2011-02-02 23:33:01 +08:00
Bryce Curtis
f220489543 Disable picture listener once event has occurred. 2011-02-01 11:25:01 -06:00
Bryce Curtis
b65f9517db Merge branch 'filmaj-splashscreenfix' 2011-02-01 11:00:02 -06:00
Bryce Curtis
1a0de5f626 Merge branch 'splashscreenfix' of https://github.com/filmaj/phonegap-android into filmaj-splashscreenfix 2011-02-01 10:46:38 -06:00
Fil Maj
3c0bef6cc1 Ticket 81: Tweak to label of local path to index.html in example app, now properly shows actual project-relative path. 2011-01-31 17:51:15 -08:00
Fil Maj
040194157f Ticket 80: running "droidgap gen example" leads to recursive directory creation. README fix included. 2011-01-31 17:48:56 -08:00
macdonst
7ebf8130e4 Set type to url for returned photos 2011-01-29 04:19:06 +08:00
macdonst
64310dc85c Fixing clone issue adding photos, removing relationships 2011-01-27 05:59:22 +08:00
macdonst
cd2e86af2f Removing excess logging in contact.save() 2011-01-27 03:44:27 +08:00
macdonst
b353f3608d Updating to latest W3C spec 2011-01-27 03:41:27 +08:00
macdonst
cda154209d Fixing merge issue 2011-01-26 11:22:08 -05:00
Sveinung Kval Bakken
e3c72fa915 Will now use a "smarter" approach to finding an account for Contact.save, the order of account search will be:
1. Exchange provider
2. Google
3. Any valid email address account
2011-01-25 11:36:08 +01:00
Fil Maj
9354b429f3 Fix for ticket #55: if phonegap source was on a path with "bin" in it would cause major fail. 2011-01-24 15:51:26 -08:00
Fil Maj
b1f0c037bd Getting rid of black screen between native loading screen and actual PhoneGap app. 2011-01-24 12:43:28 -08:00
macdonst
726f1094d9 Fixing bug found by tiny hippos 2011-01-25 03:05:59 +08:00
macdonst
1b8ab156df Adding http: and file: support when saving a contact photo. 2011-01-25 01:58:31 +08:00
macdonst
ee01b5058f Adding support to set a Contact photo 2011-01-22 01:52:20 +08:00
macdonst
03ea8a0b5a Enable the return of photos in a Contact object 2011-01-20 04:27:40 +08:00
macdonst
f090f9a70c Merge branch 'master' of https://github.com/filmaj/phonegap-android into filmaj 2011-01-17 11:54:52 -05:00
Bryce Curtis
b7abc2c344 Skip over beginning / in request when comparing to token. 2011-01-16 15:15:24 -06:00
Fil Maj
53bdf2dd6b Fix for specifying icon in config.xml; the @icon attribute would get overriden by defaults when going from create => classic::build. 2011-01-15 23:12:07 -08:00
Joe Bowser
b9e1b1d280 Adding Blank HTML page 2011-01-13 16:27:54 -08:00
Bryce Curtis
9051b157f8 Ticket 63: Android CallbackServer crashes on external attacks. 2011-01-13 14:45:15 -06:00
macdonst
f16d9b01b7 Fixing geo listner callback fail to send 3 args instead of 4. 2011-01-14 02:07:47 +08:00
macdonst
2a9bc2ddf8 Fixing issue where Android 2.1 and 2.2 don't return the same results on contact.find() 2011-01-14 02:02:21 +08:00
macdonst
6e39c46b07 Middle name for contact being updated incorrectly 2011-01-12 21:58:20 +08:00
macdonst
567ca94245 Adding debug mode so FileTransfer will accept self signed SSL certificates 2011-01-12 10:32:26 +08:00
macdonst
812a4b32b4 Adding file key properly 2011-01-07 23:17:05 +08:00
Bryce Curtis
023df10f31 Allow features/modules to initialize code before deviceready fires. CupcakeLocalStorage uses this capability to delay deviceready until local storage has been read and inited. 2011-01-06 22:50:13 -06:00
macdonst
8d513e2765 Remaining FileUploader to FileTransfer 2011-01-07 01:43:12 +08:00
Bryce Curtis
1eae6786c4 Better memory management when taking pictures. 2011-01-06 11:12:14 -06:00
macdonst
73f278963b Adding File Upload functionality 2011-01-06 07:09:07 +08:00
macdonst
54eff557d9 Guard against null request in Android 1.5/1.6 2011-01-06 04:08:23 +08:00
Bryce Curtis
a7415bcfc9 Support all URIs by passing them to their default activity. This works for market:// and content://. 2011-01-04 13:22:25 -06:00
macdonst
b6bd9ad5b8 Support Market Uri 2011-01-05 03:03:38 +08:00
macdonst
f71d9deb5e Fixing mimetypes for content:// Uri's. 2011-01-05 02:45:04 +08:00
macdonst
115b428a9d Fixing issue where Date's aren't cloned 2010-12-30 00:53:06 +08:00
macdonst
92a1e4a2d9 Remove destroyed AudioPlayer from list of AudioPlayers 2010-12-23 02:51:39 +08:00
macdonst
2504db13d7 Adding release method to Media object 2010-12-23 02:44:52 +08:00
macdonst
43c72e684c Setting content type properly in readAsDataURL 2010-12-23 00:26:38 +08:00
stevengill
46f0bf60c4 Merge branch 'master' of github.com:phonegap/phonegap-android 2010-12-16 13:29:52 -08:00
macdonst
8bad4eb7eb Fixing issue where Camera returned a content URI that File Reader could not read 2010-12-17 05:08:45 +08:00
paulb777
fbe96f891b 1. Split out js to main.js 2. Show a contact 3. Toggle accelerometer 4. More device info 5. Enable scrolling 2010-12-15 11:24:01 -08:00
Bryce Curtis
ab8950a5af Re-enable multitasking in onResume Java callback so that onResume JS handlers are called - it was being re-enabled too soon. 2010-12-09 14:13:23 -06:00
Bryce Curtis
26adfb6346 If multitasking is turned on (keepRunning=true), then temporarily disable it when starting a new activity that returns a result - such as camera. 2010-12-06 16:48:06 -06:00
Bryce Curtis
43b6b6d34e Update version number to 0.9.3 in preparation for next release. 2010-12-03 18:15:00 -06:00
Bryce Curtis
04ddc68dfd Fix bug with saving name in new contact. 2010-12-03 14:14:37 -06:00
Bryce Curtis
42cd10cf56 Need license header since it is removed by minification. 2010-12-01 16:13:22 -06:00
Bryce Curtis
5f3bc33f8e Update comments. 2010-12-01 11:21:49 -06:00
Bryce Curtis
2131070ee9 Add JavaScript minification using YUICompressor. 2010-11-30 19:00:30 -06:00
Bryce Curtis
b2a82975e5 Merge branch 'jos3000-master' 2010-11-29 12:30:49 -06:00
Bryce Curtis
ddeba91faf Merge branch 'master' of https://github.com/jos3000/phonegap-android into jos3000-master 2010-11-29 12:27:00 -06:00
macdonst
6e572f05e4 Put trailing / into getRootPaths() to remain consistent with BBW 2010-11-26 00:45:02 +08:00
macdonst
50b435c4d1 Following File API spec. 2010-11-25 03:11:43 +08:00
Bryce Curtis
af5c5dc021 Update splash screen example and list of properties that can be set in commented code. 2010-11-23 09:53:43 -06:00
Bryce Curtis
87fd9665fe Merge branch 'localStorage' of https://github.com/ascorbic/phonegap-android into ascorbic-localStorage 2010-11-21 17:58:49 -06:00
Bryce Curtis
5e9ca84b40 Expose certain methods from DroidGap to JavaScript so that a PhoneGap web app can better control program configuration and flow. 2010-11-21 17:33:13 -06:00
Bryce Curtis
090ad56d0b Don't need special method to load a splash screen. Instead, the regular loadUrl() and clearHistory() can be used. 2010-11-21 16:47:35 -06:00
Bryce Curtis
e3ebfea064 Improve handling of timeout error when loading URL, and enable WebViewClient to be overridden by app, so developer can intercept webview events. 2010-11-21 16:42:00 -06:00
Bryce Curtis
44761f87d2 Remove comment for unused parameter. 2010-11-21 16:31:49 -06:00
Bryce Curtis
04e3ceac96 Define window.plugins object so plugins can check to see if they have already been created. 2010-11-21 16:30:46 -06:00
Bryce Curtis
afc7e605ff Fix bug when not doing cast - temp isn't defined. 2010-11-20 21:23:15 -06:00
macdonst
1c5aa6cd00 Adding a cast for contacts.find() 2010-11-20 01:42:52 +08:00
macdonst
c1a87ebaaa Adding and optional call to cast Plugin Result 2010-11-20 01:42:43 +08:00
macdonst
46babe7a48 Calling correct events from FileWriter.abort() 2010-11-17 15:30:08 -05:00
Bryce Curtis
0dc64d2aa7 Merge branch 'master' of github.com:phonegap/phonegap-android 2010-11-16 18:15:15 -06:00
Bryce Curtis
1d9e522bd9 Fix quality issue with base64 encoded images. Quality parameter wasn't being set. 2010-11-16 18:14:24 -06:00
macdonst
5dcac6d7fe Fixing issue in File Reader/Writer when newlines in file 2010-11-16 13:30:18 -05:00
macdonst
07418a3606 Small fix to File API 2010-11-17 00:06:49 +08:00
Bryce Curtis
0e08af98ca Better way to handle splash screen when back button pressed. 2010-11-15 16:32:55 -06:00
Bryce Curtis
b8b1ad8421 Add property that lets a PhoneGap app continue to run when another Android app or activity is started. 2010-11-14 17:33:06 -06:00
Bryce Curtis
4fa1f40b44 Add load URL capability, and enable an HTML file to be used as a splash screen. 2010-11-12 22:38:27 -06:00
macdonst
5f55ebf1d9 Adding Contact.save() for Android 1.X and Android 2.X 2010-11-13 05:34:44 +08:00
Bryce Curtis
9798de7efa Remove unused and unneeded getPort() method. 2010-11-12 12:53:34 -06:00
Bryce Curtis
102745875c Allow user to set the loading dialog message. Change default from show to not shown. 2010-11-11 22:24:20 -06:00
Bryce Curtis
dce0d93df8 Replace deprecated debug.log with console.log. 2010-11-11 22:03:12 -06:00
Bryce Curtis
1428ac5ed5 Add error checking for PhoneGap.addPlugin(). 2010-11-11 21:58:07 -06:00
Bryce Curtis
4f1bc1401f Add delay so splash screen can be shown for a specific amount of time. 2010-11-11 21:56:56 -06:00
Bryce Curtis
28ff6e1150 Merged code for bryfox: Re-add support for search & menu key triggers.
See original commit: 799515fa7b
2010-11-11 16:20:32 -06:00
Bryce Curtis
5ffe5fa3c5 Merged code for imhotep: Cleaner way for handling splashscreens.
See original commit: 1761cbb3dc
2010-11-11 15:59:35 -06:00
Bryce Curtis
49341356d7 Add comments to onKeyDown() method. 2010-11-11 14:08:55 -06:00
Bryce Curtis
e8b85f6cf7 Fix formatting and rearrange method order. 2010-11-11 14:00:56 -06:00
Bryce Curtis
4b2398b487 Add properties to DroidGap that can be set when the intent/activity is called. This enables the developer to show app loading dialog, splashscreen, or set other properties. 2010-11-11 11:34:12 -06:00
Bryce Curtis
10f3313ed5 Use polling if PhoneGap app is loaded from server, since XHR doesn't work to localhost due to cross-domain security policy. 2010-11-10 14:19:17 -06:00
Matt Kane
46664c6494 Remove dependency on JSON support 2010-11-10 08:44:33 +00:00
Matt Kane
8ce7e61ed7 Adds localStorage support to older versions 2010-11-10 08:34:59 +00:00
Matt Kane
912458c679 Fix order of args to match w3c spec. 2010-11-09 22:58:13 +08:00
macdonst
e117b95057 Fixing issue with addEventListener and Sencha 2010-11-09 21:48:48 +08:00
Jos Shepherd
3a0101261d Move data transfer for storage to completeQuery for speed up 2010-11-06 20:01:22 +00:00
Bryce Curtis
48d3bc09f3 Add method for dynamic loading of a JavaScript file. 2010-11-05 16:00:58 -05:00
Bryce Curtis
0b3e27b3fa Load url into DroidGap if it was passed in to intent. 2010-11-05 14:50:48 -05:00
Bryce Curtis
b66535a17d Introduce PhonegapActivity class to separate plugin development from base Phonegap. 2010-11-06 03:10:28 +08:00
Jos Shepherd
fdc78e1b08 Fix for troublesome values being returned from DroidDB stores. Stick to one level of JSON serialization. 2010-11-05 17:03:05 +00:00
Bryce Curtis
36064c564e Reduce timeout from 30 sec to 10 sec for CallbackServer. Some devices have shorter timeouts than others for XHR. 2010-11-04 13:07:15 -05:00
Bryce Curtis
7102810283 Fix problem with CallbackServer for certain HTC phones. 2010-11-03 22:45:14 -05:00
Bryce Curtis
ab4d4e22da Include the device's name in Device.name per API docs. 2010-11-03 10:23:12 -05:00
Bryce Curtis
80c15de606 Return error conditions from CallbackServer instead of just closing connection. 2010-11-01 13:59:08 -05:00
Matt Kane
be5cac6d0b This is being called from java code, but callback didn't exist 2010-11-01 05:58:24 +08:00
Matt Kane
2bb67ee4b0 Change use "geo:" instead of WebView.SCHEME_GEO
The constant is "geo:0,0?q=", which means it doesn't match urls that specify the coordinates. The gmap app can handle these though, so we can pass any geo: url to it.
2010-11-01 05:50:55 +08:00
71 changed files with 7997 additions and 2674 deletions

4
.gitignore vendored
View File

@@ -1,7 +1,11 @@
.DS_Store
default.properties
gen
assets/www/phonegap.js
local.properties
framework/phonegap.jar
framework/phonegap-*.jar
framework/bin
framework/assets/www/.DS_Store
framework/assets/www/phonegap-*.js
.DS_Store

View File

@@ -31,7 +31,7 @@ Commands:
<pre>
help ...... See this message. Type help [command name] to see specific help topics.
gen ....... Generate an example PhoneGap application to current directory.
gen ....... Generate the example PhoneGap application to current directory (or optionally provide an output directory as parameter).
create .... Creates an Android compatible project from a WWW folder.
classic ... Backwards support for droidgap script. Run "droidgap help classic" for more info.
update .... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project.
@@ -41,8 +41,8 @@ Commands:
Quickstart:
<pre>
$ droidgap gen example
$ cd example
$ droidgap gen exampleapp
$ cd exampleapp
$ ant debug install && adb logcat
</pre>

1
VERSION Normal file
View File

@@ -0,0 +1 @@
1.0.0rc3

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env ruby
ROOT = File.expand_path(File.dirname(__FILE__).gsub('bin',''))
ROOT = File.expand_path(File.dirname(__FILE__).gsub(/bin$/,''))
require 'fileutils'
require File.join(ROOT, "lib", "generate.rb")
require File.join(ROOT, "lib", "classic.rb")
@@ -60,7 +60,7 @@ if ARGV.first.nil? || ARGV.first == 'help'
Commands:
help ...... See this message. Type help [command name] to see specific help topics.
gen ....... Generate an example PhoneGap application to current directory.
gen ....... Generate the example PhoneGap application to current directory (or optionally provide an output directory as parameter).
create .... Creates an Android compatible project from a WWW folder.
classic ... Backwards support for droidgap script. Run "droidgap help classic" for more info.
update .... Copy a fresh phonegap.jar and phonegap.js into a valid PhoneGap/Android project.
@@ -68,8 +68,8 @@ if ARGV.first.nil? || ARGV.first == 'help'
Quickstart:
$ droidgap gen example
$ cd example
$ droidgap gen exampleapp
$ cd exampleapp
$ ant debug install && adb logcat
EOF
@@ -79,11 +79,13 @@ if ARGV.first.nil? || ARGV.first == 'help'
DroidGap Generate
-----------------
Generate an example PhoneGap application to path supplied or current working directory if none is supplied.
Generate the example PhoneGap application to path supplied or current working directory if none is supplied.
Usage:
droidgap gen [path]
NOTE: Do *not* run "droidgap gen example" - you will end up with a recursive directory problem.
EOF

View File

@@ -5,143 +5,34 @@
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>PhoneGap</title>
<link rel="stylesheet" href="master.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script type="text/javascript" charset="utf-8" src="phonegap.js"></script>
<script type="text/javascript" charset="utf-8">
var deviceInfo = function(){
document.getElementById("platform").innerHTML = device.platform;
document.getElementById("version").innerHTML = device.version;
document.getElementById("uuid").innerHTML = device.uuid;
}
var getLocation = function() {
var suc = function(p){
alert(p.coords.latitude + " " + p.coords.longitude);
};
var fail = function(){};
navigator.geolocation.getCurrentPosition(suc,fail);
}
var beep = function(){
navigator.notification.beep(2);
}
var vibrate = function(){
navigator.notification.vibrate(0);
}
var getContact = function(){
var suc = function(c){ alert("Contact 4: " + c.contacts[3].name); };
var fail = function(){};
navigator.ContactManager.get(suc, fail);
}
var watchAccel = function() {
var suc = function(a){
document.getElementById('x').innerHTML = roundNumber(a.x);
document.getElementById('y').innerHTML = roundNumber(a.y);
document.getElementById('z').innerHTML = roundNumber(a.z);
};
var fail = function(){};
var opt = {};
opt.frequency = 100;
timer = navigator.accelerometer.watchAcceleration(suc,fail,opt);
}
function roundNumber(num) {
var dec = 3;
var result = Math.round(num*Math.pow(10,dec))/Math.pow(10,dec);
return result;
}
var preventBehavior = function(e) {
e.preventDefault();
};
<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0rc3.js"></script>
<script type="text/javascript" charset="utf-8" src="main.js"></script>
function show_pic()
{
var viewport = document.getElementById('viewport');
viewport.style.display = "";
navigator.camera.getPicture(dump_pic, fail, { quality: 50 });
}
function dump_pic(data)
{
var viewport = document.getElementById('viewport');
console.log(data);
viewport.style.display = "";
viewport.style.position = "absolute";
viewport.style.top = "10px";
viewport.style.left = "10px";
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
}
function close()
{
var viewport = document.getElementById('viewport');
viewport.style.position = "relative";
viewport.style.display = "none";
}
function fail(fail)
{
alert(fail);
}
// This is just to do this.
function readFile()
{
navigator.file.read('/sdcard/phonegap.txt', fail , fail);
}
function writeFile()
{
navigator.file.write('foo.txt', "This is a test of writing to a file", fail, fail);
}
function get_contacts()
{
var obj = new ContactFindOptions();
obj.filter="";
obj.multiple=true;
obj.limit=5;
navigator.service.contacts.find(["displayName", "phoneNumbers", "emails"], count_contacts, fail, obj);
}
function count_contacts(contacts)
{
alert(contacts.length);
}
function init(){
document.addEventListener("touchmove", preventBehavior, false);
document.addEventListener("deviceready", deviceInfo, true);
}
</script>
</head>
<body onload="init();" id="stage" class="theme">
<h1>Welcome to PhoneGap!</h1>
<h2>this file is located at assets/index.html</h2>
<h2>this file is located at assets/www/index.html</h2>
<div id="info">
<h4>Platform: <span id="platform">&nbsp;</span></h4>
<h4>Version: <span id="version">&nbsp;</span></h4>
<h4>UUID: <span id="uuid">&nbsp;</span></h4>
</div>
<h4>Platform: <span id="platform"> &nbsp;</span>, Version: <span id="version">&nbsp;</span></h4>
<h4>UUID: <span id="uuid"> &nbsp;</span>, Name: <span id="name">&nbsp;</span></h4>
<h4>Width: <span id="width"> &nbsp;</span>, Height: <span id="height">&nbsp;
</span>, Color Depth: <span id="colorDepth"></span></h4>
</div>
<dl id="accel-data">
<dt>X:</dt><dd id="x">&nbsp;</dd>
<dt>Y:</dt><dd id="y">&nbsp;</dd>
<dt>Z:</dt><dd id="z">&nbsp;</dd>
</dl>
<a href="#" class="btn large" onclick="watchAccel();">Watch Accelerometer</a>
<a href="#" class="btn large" onclick="toggleAccel();">Toggle Accelerometer</a>
<a href="#" class="btn large" onclick="getLocation();">Get Location</a>
<a href="tel://411" class="btn large">Call 411</a>
<a href="#" class="btn large" onclick="beep();">Beep</a>
<a href="#" class="btn large" onclick="vibrate();">Vibrate</a>
<a href="#" class="btn large" onclick="show_pic();">Get a Picture</a>
<a href="#" class="btn large" onclick="get_contacts();">Get phone's contacts</a>
<div id="viewport" class="viewport" style="display: none;">
<a href="#" class="btn large" onclick="get_contacts();">Get Phone's Contacts</a>
<a href="#" class="btn large" onclick="check_network();">Check Network</a>
<div id="viewport" class="viewport" style="display: none;">
<img style="width:60px;height:60px" id="test_img" src="" />
</div>
</div>
</body>
</html>

127
example/main.js Normal file
View File

@@ -0,0 +1,127 @@
var deviceInfo = function() {
document.getElementById("platform").innerHTML = device.platform;
document.getElementById("version").innerHTML = device.version;
document.getElementById("uuid").innerHTML = device.uuid;
document.getElementById("name").innerHTML = device.name;
document.getElementById("width").innerHTML = screen.width;
document.getElementById("height").innerHTML = screen.height;
document.getElementById("colorDepth").innerHTML = screen.colorDepth;
};
var getLocation = function() {
var suc = function(p) {
alert(p.coords.latitude + " " + p.coords.longitude);
};
var locFail = function() {
};
navigator.geolocation.getCurrentPosition(suc, locFail);
};
var beep = function() {
navigator.notification.beep(2);
};
var vibrate = function() {
navigator.notification.vibrate(0);
};
function roundNumber(num) {
var dec = 3;
var result = Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
return result;
}
var accelerationWatch = null;
function updateAcceleration(a) {
document.getElementById('x').innerHTML = roundNumber(a.x);
document.getElementById('y').innerHTML = roundNumber(a.y);
document.getElementById('z').innerHTML = roundNumber(a.z);
}
var toggleAccel = function() {
if (accelerationWatch !== null) {
navigator.accelerometer.clearWatch(accelerationWatch);
updateAcceleration({
x : "",
y : "",
z : ""
});
accelerationWatch = null;
} else {
var options = {};
options.frequency = 1000;
accelerationWatch = navigator.accelerometer.watchAcceleration(
updateAcceleration, function(ex) {
alert("accel fail (" + ex.name + ": " + ex.message + ")");
}, options);
}
};
var preventBehavior = function(e) {
e.preventDefault();
};
function dump_pic(data) {
var viewport = document.getElementById('viewport');
console.log(data);
viewport.style.display = "";
viewport.style.position = "absolute";
viewport.style.top = "10px";
viewport.style.left = "10px";
document.getElementById("test_img").src = "data:image/jpeg;base64," + data;
}
function fail(msg) {
alert(msg);
}
function show_pic() {
navigator.camera.getPicture(dump_pic, fail, {
quality : 50
});
}
function close() {
var viewport = document.getElementById('viewport');
viewport.style.position = "relative";
viewport.style.display = "none";
}
function contacts_success(contacts) {
alert(contacts.length
+ ' contacts returned.'
+ (contacts[2] && contacts[2].name ? (' Third contact is ' + contacts[2].name.formatted)
: ''));
}
function get_contacts() {
var obj = new ContactFindOptions();
obj.filter = "";
obj.multiple = true;
navigator.contacts.find(
[ "displayName", "name" ], contacts_success,
fail, obj);
}
function check_network() {
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() {
// the next line makes it impossible to see Contacts on the HTC Evo since it
// doesn't have a scroll button
// document.addEventListener("touchmove", preventBehavior, false);
document.addEventListener("deviceready", deviceInfo, true);
}

View File

@@ -1,38 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:windowSoftInputMode="adjustPan"
package="com.phonegap" android:versionName="1.1" android:versionCode="5">
<supports-screens
android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:xlargeScreens="true"
android:resizeable="true"
android:anyDensity="true"
/>
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.RECORD_VIDEO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<application android:icon="@drawable/icon" android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".StandAlone"
<activity android:name=".StandAlone" android:windowSoftInputMode="adjustPan"
android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.phonegap.DroidGap" android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<intent-filter>
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="2" />
<uses-sdk android:minSdkVersion="2" />
</manifest>

View File

@@ -3,10 +3,14 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
function Acceleration(x, y, z) {
if (!PhoneGap.hasResource("accelerometer")) {
PhoneGap.addResource("accelerometer");
/** @constructor */
var Acceleration = function(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
@@ -17,7 +21,7 @@ function Acceleration(x, y, z) {
* This class provides access to device accelerometer data.
* @constructor
*/
function Accelerometer() {
var Accelerometer = function() {
/**
* The last known acceleration. type=Acceleration()
@@ -42,13 +46,13 @@ Accelerometer.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
Accelerometer.prototype.getCurrentAcceleration = function(successCallback, errorCallback, options) {
// successCallback required
if (typeof successCallback != "function") {
if (typeof successCallback !== "function") {
console.log("Accelerometer Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Accelerometer Error: errorCallback is not a function");
return;
}
@@ -68,16 +72,16 @@ Accelerometer.prototype.getCurrentAcceleration = function(successCallback, error
Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallback, options) {
// Default interval (10 sec)
var frequency = (options != undefined)? options.frequency : 10000;
var frequency = (options !== undefined)? options.frequency : 10000;
// successCallback required
if (typeof successCallback != "function") {
if (typeof successCallback !== "function") {
console.log("Accelerometer Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Accelerometer Error: errorCallback is not a function");
return;
}
@@ -108,12 +112,15 @@ Accelerometer.prototype.watchAcceleration = function(successCallback, errorCallb
Accelerometer.prototype.clearWatch = function(id) {
// Stop javascript timer & remove from timer list
if (id && navigator.accelerometer.timers[id] != undefined) {
if (id && navigator.accelerometer.timers[id] !== undefined) {
clearInterval(navigator.accelerometer.timers[id]);
delete navigator.accelerometer.timers[id];
}
};
PhoneGap.addConstructor(function() {
if (typeof navigator.accelerometer == "undefined") navigator.accelerometer = new Accelerometer();
if (typeof navigator.accelerometer === "undefined") {
navigator.accelerometer = new Accelerometer();
}
});
}

86
framework/assets/js/app.js Executable file
View File

@@ -0,0 +1,86 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("app")) {
PhoneGap.addResource("app");
(function() {
/**
* Constructor
* @constructor
*/
var App = function() {};
/**
* Clear the resource cache.
*/
App.prototype.clearCache = function() {
PhoneGap.exec(null, null, "App", "clearCache", []);
};
/**
* Load the url into the webview.
*
* @param url The URL to load
* @param props Properties that can be passed in to the activity:
* wait: int => wait msec before loading URL
* loadingDialog: "Title,Message" => display a native loading dialog
* hideLoadingDialogOnPage: boolean => hide loadingDialog when page loaded instead of when deviceready event occurs.
* loadInWebView: boolean => cause all links on web page to be loaded into existing web view, instead of being loaded into new browser.
* loadUrlTimeoutValue: int => time in msec to wait before triggering a timeout error
* errorUrl: URL => URL to load if there's an error loading specified URL with loadUrl(). Should be a local URL such as file:///android_asset/www/error.html");
* keepRunning: boolean => enable app to keep running in background
*
* Example:
* App app = new App();
* app.loadUrl("http://server/myapp/index.html", {wait:2000, loadingDialog:"Wait,Loading App", loadUrlTimeoutValue: 60000});
*/
App.prototype.loadUrl = function(url, props) {
PhoneGap.exec(null, null, "App", "loadUrl", [url, props]);
};
/**
* Cancel loadUrl that is waiting to be loaded.
*/
App.prototype.cancelLoadUrl = function() {
PhoneGap.exec(null, null, "App", "cancelLoadUrl", []);
};
/**
* Clear web history in this web view.
* Instead of BACK button loading the previous web page, it will exit the app.
*/
App.prototype.clearHistory = function() {
PhoneGap.exec(null, null, "App", "clearHistory", []);
};
/**
* Override the default behavior of the Android back button.
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
*
* Note: The user should not have to call this method. Instead, when the user
* registers for the "backbutton" event, this is automatically done.
*
* @param override T=override, F=cancel override
*/
App.prototype.overrideBackbutton = function(override) {
PhoneGap.exec(null, null, "App", "overrideBackbutton", [override]);
};
/**
* Exit and terminate the application.
*/
App.prototype.exitApp = function() {
return PhoneGap.exec(null, null, "App", "exitApp", []);
};
PhoneGap.addConstructor(function() {
navigator.app = new App();
});
}());
}

View File

@@ -3,15 +3,18 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("camera")) {
PhoneGap.addResource("camera");
/**
* This class provides access to the device camera.
*
* @constructor
*/
Camera = function() {
var Camera = function() {
this.successCallback = null;
this.errorCallback = null;
this.options = null;
@@ -31,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.
*
@@ -59,13 +77,13 @@ Camera.prototype.PictureSourceType = Camera.PictureSourceType;
Camera.prototype.getPicture = function(successCallback, errorCallback, options) {
// successCallback required
if (typeof successCallback != "function") {
if (typeof successCallback !== "function") {
console.log("Camera Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Camera Error: errorCallback is not a function");
return;
}
@@ -75,17 +93,51 @@ 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;
}
var sourceType = Camera.PictureSourceType.CAMERA;
if (typeof this.options.sourceType == "number") {
if (typeof this.options.sourceType === "number") {
sourceType = this.options.sourceType;
}
PhoneGap.exec(successCallback, errorCallback, "Camera", "takePicture", [quality, destinationType, sourceType]);
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() {
if (typeof navigator.camera == "undefined") navigator.camera = new Camera();
if (typeof navigator.camera === "undefined") {
navigator.camera = new Camera();
}
});
}

View File

@@ -0,0 +1,191 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("capture")) {
PhoneGap.addResource("capture");
/**
* Represents a single file.
*
* name {DOMString} name of the file, without path information
* fullPath {DOMString} the full path of the file, including the name
* type {DOMString} mime type
* lastModifiedDate {Date} last modified date
* size {Number} size of the file in bytes
*/
var MediaFile = function(name, fullPath, type, lastModifiedDate, size){
this.name = name || null;
this.fullPath = fullPath || null;
this.type = type || null;
this.lastModifiedDate = lastModifiedDate || null;
this.size = size || 0;
};
/**
* Launch device camera application for recording video(s).
*
* @param {Function} successCB
* @param {Function} errorCB
*/
MediaFile.prototype.getFormatData = function(successCallback, errorCallback){
PhoneGap.exec(successCallback, errorCallback, "Capture", "getFormatData", [this.fullPath, this.type]);
};
/**
* MediaFileData encapsulates format information of a media file.
*
* @param {DOMString} codecs
* @param {long} bitrate
* @param {long} height
* @param {long} width
* @param {float} duration
*/
var MediaFileData = function(codecs, bitrate, height, width, duration){
this.codecs = codecs || null;
this.bitrate = bitrate || 0;
this.height = height || 0;
this.width = width || 0;
this.duration = duration || 0;
};
/**
* The CaptureError interface encapsulates all errors in the Capture API.
*/
var CaptureError = function(){
this.code = null;
};
// Capture error codes
CaptureError.CAPTURE_INTERNAL_ERR = 0;
CaptureError.CAPTURE_APPLICATION_BUSY = 1;
CaptureError.CAPTURE_INVALID_ARGUMENT = 2;
CaptureError.CAPTURE_NO_MEDIA_FILES = 3;
CaptureError.CAPTURE_NOT_SUPPORTED = 20;
/**
* The Capture interface exposes an interface to the camera and microphone of the hosting device.
*/
var Capture = function(){
this.supportedAudioModes = [];
this.supportedImageModes = [];
this.supportedVideoModes = [];
};
/**
* Launch audio recorder application for recording audio clip(s).
*
* @param {Function} successCB
* @param {Function} errorCB
* @param {CaptureAudioOptions} options
*/
Capture.prototype.captureAudio = function(successCallback, errorCallback, options){
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureAudio", [options]);
};
/**
* Launch camera application for taking image(s).
*
* @param {Function} successCB
* @param {Function} errorCB
* @param {CaptureImageOptions} options
*/
Capture.prototype.captureImage = function(successCallback, errorCallback, options){
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureImage", [options]);
};
/**
* Launch camera application for taking image(s).
*
* @param {Function} successCB
* @param {Function} errorCB
* @param {CaptureImageOptions} options
*/
Capture.prototype._castMediaFile = function(pluginResult){
var mediaFiles = [];
var i;
for (i = 0; i < pluginResult.message.length; i++) {
var mediaFile = new MediaFile();
mediaFile.name = pluginResult.message[i].name;
mediaFile.fullPath = pluginResult.message[i].fullPath;
mediaFile.type = pluginResult.message[i].type;
mediaFile.lastModifiedDate = pluginResult.message[i].lastModifiedDate;
mediaFile.size = pluginResult.message[i].size;
mediaFiles.push(mediaFile);
}
pluginResult.message = mediaFiles;
return pluginResult;
};
/**
* Launch device camera application for recording video(s).
*
* @param {Function} successCB
* @param {Function} errorCB
* @param {CaptureVideoOptions} options
*/
Capture.prototype.captureVideo = function(successCallback, errorCallback, options){
PhoneGap.exec(successCallback, errorCallback, "Capture", "captureVideo", [options]);
};
/**
* Encapsulates a set of parameters that the capture device supports.
*/
var ConfigurationData = function(){
// The ASCII-encoded string in lower case representing the media type.
this.type = null;
// The height attribute represents height of the image or video in pixels.
// In the case of a sound clip this attribute has value 0.
this.height = 0;
// The width attribute represents width of the image or video in pixels.
// In the case of a sound clip this attribute has value 0
this.width = 0;
};
/**
* Encapsulates all image capture operation configuration options.
*/
var CaptureImageOptions = function(){
// Upper limit of images user can take. Value must be equal or greater than 1.
this.limit = 1;
// The selected image mode. Must match with one of the elements in supportedImageModes array.
this.mode = null;
};
/**
* Encapsulates all video capture operation configuration options.
*/
var CaptureVideoOptions = function(){
// Upper limit of videos user can record. Value must be equal or greater than 1.
this.limit = 1;
// Maximum duration of a single video clip in seconds.
this.duration = 0;
// The selected video mode. Must match with one of the elements in supportedVideoModes array.
this.mode = null;
};
/**
* Encapsulates all audio capture operation configuration options.
*/
var CaptureAudioOptions = function(){
// Upper limit of sound clips user can record. Value must be equal or greater than 1.
this.limit = 1;
// Maximum duration of a single sound clip in seconds.
this.duration = 0;
// The selected audio mode. Must match with one of the elements in supportedAudioModes array.
this.mode = null;
};
PhoneGap.addConstructor(function(){
if (typeof navigator.device === "undefined") {
navigator.device = window.device = new Device();
}
if (typeof navigator.device.capture === "undefined") {
navigator.device.capture = window.device.capture = new Capture();
}
});
}

View File

@@ -3,14 +3,17 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("compass")) {
PhoneGap.addResource("compass");
/**
* This class provides access to device Compass data.
* @constructor
*/
function Compass() {
var Compass = function() {
/**
* The last known Compass position.
*/
@@ -34,13 +37,13 @@ Compass.ERROR_MSG = ["Not running", "Starting", "", "Failed to start"];
Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, options) {
// successCallback required
if (typeof successCallback != "function") {
if (typeof successCallback !== "function") {
console.log("Compass Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Compass Error: errorCallback is not a function");
return;
}
@@ -60,16 +63,16 @@ Compass.prototype.getCurrentHeading = function(successCallback, errorCallback, o
Compass.prototype.watchHeading= function(successCallback, errorCallback, options) {
// Default interval (100 msec)
var frequency = (options != undefined) ? options.frequency : 100;
var frequency = (options !== undefined) ? options.frequency : 100;
// successCallback required
if (typeof successCallback != "function") {
if (typeof successCallback !== "function") {
console.log("Compass Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Compass Error: errorCallback is not a function");
return;
}
@@ -109,5 +112,8 @@ Compass.prototype.clearWatch = function(id) {
};
PhoneGap.addConstructor(function() {
if (typeof navigator.compass == "undefined") navigator.compass = new Compass();
if (typeof navigator.compass === "undefined") {
navigator.compass = new Compass();
}
});
}

View File

@@ -3,39 +3,34 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("contact")) {
PhoneGap.addResource("contact");
/**
* Contains information about a single contact.
* @constructor
* @param {DOMString} id unique identifier
* @param {DOMString} displayName
* @param {ContactName} name
* @param {DOMString} nickname
* @param {ContactField[]} phoneNumbers array of phone numbers
* @param {ContactField[]} emails array of email addresses
* @param {ContactAddress[]} addresses array of addresses
* @param {ContactField[]} ims instant messaging user ids
* @param {ContactOrganization[]} organizations
* @param {DOMString} published date contact was first created
* @param {DOMString} updated date contact was last updated
* @param {Array.<ContactField>} phoneNumbers array of phone numbers
* @param {Array.<ContactField>} emails array of email addresses
* @param {Array.<ContactAddress>} addresses array of addresses
* @param {Array.<ContactField>} ims instant messaging user ids
* @param {Array.<ContactOrganization>} organizations
* @param {DOMString} birthday contact's birthday
* @param (DOMString} anniversary contact's anniversary
* @param {DOMString} gender contact's gender
* @param {DOMString} note user notes about contact
* @param {DOMString} preferredUsername
* @param {ContactField[]} photos
* @param {ContactField[]} tags
* @param {ContactField[]} relationships
* @param {ContactField[]} urls contact's web sites
* @param {ContactAccounts[]} accounts contact's online accounts
* @param {DOMString} utcOffset UTC time zone offset
* @param {DOMString} connected
* @param {Array.<ContactField>} photos
* @param {Array.<ContactField>} categories
* @param {Array.<ContactField>} urls contact's web sites
*/
var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, addresses,
ims, organizations, published, updated, birthday, anniversary, gender, note,
preferredUsername, photos, tags, relationships, urls, accounts, utcOffset, connected) {
var Contact = function (id, displayName, name, nickname, phoneNumbers, emails, addresses,
ims, organizations, birthday, note, photos, categories, urls) {
this.id = id || null;
this.rawId = null;
this.displayName = displayName || null;
this.name = name || null; // ContactName
this.nickname = nickname || null;
@@ -44,35 +39,47 @@ var Contact = function(id, displayName, name, nickname, phoneNumbers, emails, ad
this.addresses = addresses || null; // ContactAddress[]
this.ims = ims || null; // ContactField[]
this.organizations = organizations || null; // ContactOrganization[]
this.published = published || null;
this.updated = updated || null;
this.birthday = birthday || null;
this.anniversary = anniversary || null;
this.gender = gender || null;
this.note = note || null;
this.preferredUsername = preferredUsername || null;
this.photos = photos || null; // ContactField[]
this.tags = tags || null; // ContactField[]
this.relationships = relationships || null; // ContactField[]
this.categories = categories || null; // ContactField[]
this.urls = urls || null; // ContactField[]
this.accounts = accounts || null; // ContactAccount[]
this.utcOffset = utcOffset || null;
this.connected = connected || null;
};
/**
* ContactError.
* An error code assigned by an implementation when an error has occurreds
* @constructor
*/
var ContactError = function() {
this.code=null;
};
/**
* Error codes
*/
ContactError.UNKNOWN_ERROR = 0;
ContactError.INVALID_ARGUMENT_ERROR = 1;
ContactError.TIMEOUT_ERROR = 2;
ContactError.PENDING_OPERATION_ERROR = 3;
ContactError.IO_ERROR = 4;
ContactError.NOT_SUPPORTED_ERROR = 5;
ContactError.PERMISSION_DENIED_ERROR = 20;
/**
* Removes contact from device storage.
* @param successCB success callback
* @param errorCB error callback
*/
Contact.prototype.remove = function(successCB, errorCB) {
if (this.id == null) {
if (this.id === null) {
var errorObj = new ContactError();
errorObj.code = ContactError.NOT_FOUND_ERROR;
errorObj.code = ContactError.UNKNOWN_ERROR;
errorCB(errorObj);
}
PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]);
else {
PhoneGap.exec(successCB, errorCB, "Contacts", "remove", [this.id]);
}
};
/**
@@ -82,7 +89,50 @@ Contact.prototype.remove = function(successCB, errorCB) {
*/
Contact.prototype.clone = function() {
var clonedContact = PhoneGap.clone(this);
var i;
clonedContact.id = null;
clonedContact.rawId = null;
// Loop through and clear out any id's in phones, emails, etc.
if (clonedContact.phoneNumbers) {
for (i = 0; i < clonedContact.phoneNumbers.length; i++) {
clonedContact.phoneNumbers[i].id = null;
}
}
if (clonedContact.emails) {
for (i = 0; i < clonedContact.emails.length; i++) {
clonedContact.emails[i].id = null;
}
}
if (clonedContact.addresses) {
for (i = 0; i < clonedContact.addresses.length; i++) {
clonedContact.addresses[i].id = null;
}
}
if (clonedContact.ims) {
for (i = 0; i < clonedContact.ims.length; i++) {
clonedContact.ims[i].id = null;
}
}
if (clonedContact.organizations) {
for (i = 0; i < clonedContact.organizations.length; i++) {
clonedContact.organizations[i].id = null;
}
}
if (clonedContact.tags) {
for (i = 0; i < clonedContact.tags.length; i++) {
clonedContact.tags[i].id = null;
}
}
if (clonedContact.photos) {
for (i = 0; i < clonedContact.photos.length; i++) {
clonedContact.photos[i].id = null;
}
}
if (clonedContact.urls) {
for (i = 0; i < clonedContact.urls.length; i++) {
clonedContact.urls[i].id = null;
}
}
return clonedContact;
};
@@ -92,10 +142,12 @@ Contact.prototype.clone = function() {
* @param errorCB error callback
*/
Contact.prototype.save = function(successCB, errorCB) {
PhoneGap.exec(successCB, errorCB, "Contacts", "save", [this]);
};
/**
* Contact name.
* @constructor
* @param formatted
* @param familyName
* @param givenName
@@ -114,18 +166,23 @@ var ContactName = function(formatted, familyName, givenName, middle, prefix, suf
/**
* Generic contact field.
* @constructor
* @param {DOMString} id unique identifier, should only be set by native code
* @param type
* @param value
* @param primary
* @param pref
*/
var ContactField = function(type, value, primary) {
var ContactField = function(type, value, pref) {
this.id = null;
this.type = type || null;
this.value = value || null;
this.primary = primary || null;
this.pref = pref || null;
};
/**
* Contact address.
* @constructor
* @param {DOMString} id unique identifier, should only be set by native code
* @param formatted
* @param streetAddress
* @param locality
@@ -133,7 +190,10 @@ var ContactField = function(type, value, primary) {
* @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;
@@ -144,6 +204,8 @@ var ContactAddress = function(formatted, streetAddress, locality, region, postal
/**
* Contact organization.
* @constructor
* @param {DOMString} id unique identifier, should only be set by native code
* @param name
* @param dept
* @param title
@@ -152,35 +214,23 @@ var ContactAddress = function(formatted, streetAddress, locality, region, postal
* @param location
* @param desc
*/
var ContactOrganization = function(name, dept, title, startDate, endDate, location, desc) {
var ContactOrganization = function(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;
this.startDate = startDate || null;
this.endDate = endDate || null;
this.location = location || null;
this.description = desc || null;
};
/**
* Contact account.
* @param domain
* @param username
* @param userid
*/
var ContactAccount = function(domain, username, userid) {
this.domain = domain || null;
this.username = username || null;
this.userid = userid || null;
}
/**
* Represents a group of Contacts.
* @constructor
*/
var Contacts = function() {
this.inProgress = false;
this.records = new Array();
}
this.records = [];
};
/**
* Returns an array of Contacts matching the search criteria.
* @param fields that should be searched
@@ -190,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]);
}
};
/**
@@ -201,53 +260,51 @@ Contacts.prototype.find = function(fields, successCB, errorCB, options) {
* @returns new Contact object
*/
Contacts.prototype.create = function(properties) {
var contact = new Contact();
var i;
var contact = new Contact();
for (i in properties) {
if (contact[i]!='undefined') {
contact[i]=properties[i];
if (contact[i] !== 'undefined') {
contact[i] = properties[i];
}
}
return contact;
};
/**
* This function returns and array of contacts. It is required as we need to convert raw
* JSON objects into concrete Contact objects. Currently this method is called after
* navigator.contacts.find but before the find methods success call back.
*
* @param jsonArray an array of JSON Objects that need to be converted to Contact objects.
* @returns an array of Contact objects
*/
Contacts.prototype.cast = function(pluginResult) {
var contacts = [];
var i;
for (i=0; i<pluginResult.message.length; i++) {
contacts.push(navigator.contacts.create(pluginResult.message[i]));
}
pluginResult.message = contacts;
return pluginResult;
};
/**
* ContactFindOptions.
* @constructor
* @param filter used to match contacts against
* @param multiple boolean used to determine if more than one contact should be returned
* @param limit maximum number of results to return from the contacts search
* @param updatedSince return only contact records that have been updated on or after the given time
*/
var ContactFindOptions = function(filter, multiple, limit, updatedSince) {
var ContactFindOptions = function(filter, multiple) {
this.filter = filter || '';
this.multiple = multiple || false;
this.limit = limit || 1;
this.updatedSince = updatedSince || '';
};
/**
* ContactError.
* An error code assigned by an implementation when an error has occurred
*/
var ContactError = function() {
this.code=null;
};
/**
* Error codes
*/
ContactError.UNKNOWN_ERROR = 0;
ContactError.INVALID_ARGUMENT_ERROR = 1;
ContactError.NOT_FOUND_ERROR = 2;
ContactError.TIMEOUT_ERROR = 3;
ContactError.PENDING_OPERATION_ERROR = 4;
ContactError.IO_ERROR = 5;
ContactError.NOT_SUPPORTED_ERROR = 6;
ContactError.PERMISSION_DENIED_ERROR = 20;
/**
* Add the contact interface into the browser.
*/
PhoneGap.addConstructor(function() {
if(typeof navigator.service == "undefined") navigator.service = new Object();
if(typeof navigator.service.contacts == "undefined") navigator.service.contacts = new Contacts();
if(typeof navigator.contacts === "undefined") {
navigator.contacts = new Contacts();
}
});
}

View File

@@ -3,11 +3,17 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
// TODO: Needs to be commented
if (!PhoneGap.hasResource("crypto")) {
PhoneGap.addResource("crypto");
/**
* @constructor
*/
var Crypto = function() {
};
@@ -30,6 +36,8 @@ Crypto.prototype.getPlainString = function(string) {
};
PhoneGap.addConstructor(function() {
if (typeof navigator.Crypto == "undefined") navigator.Crypto = new Crypto();
if (typeof navigator.Crypto === "undefined") {
navigator.Crypto = new Crypto();
}
});
}

View File

@@ -3,15 +3,18 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("device")) {
PhoneGap.addResource("device");
/**
* This represents the mobile device, and provides properties for inspecting the model, version, UUID of the
* phone, etc.
* @constructor
*/
function Device() {
var Device = function() {
this.available = PhoneGap.available;
this.platform = null;
this.version = null;
@@ -25,6 +28,7 @@ function Device() {
me.available = true;
me.platform = info.platform;
me.version = info.version;
me.name = info.name;
me.uuid = info.uuid;
me.phonegap = info.phonegap;
PhoneGap.onPhoneGapInfoReady.fire();
@@ -34,7 +38,7 @@ function Device() {
console.log("Error initializing PhoneGap: " + e);
alert("Error initializing PhoneGap: "+e);
});
}
};
/**
* Get device info
@@ -45,13 +49,13 @@ function Device() {
Device.prototype.getInfo = function(successCallback, errorCallback) {
// successCallback required
if (typeof successCallback != "function") {
if (typeof successCallback !== "function") {
console.log("Device Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Device Error: errorCallback is not a function");
return;
}
@@ -61,32 +65,41 @@ Device.prototype.getInfo = function(successCallback, errorCallback) {
};
/*
* DEPRECATED
* This is only for Android.
*
* You must explicitly override the back button.
*/
Device.prototype.overrideBackButton = function() {
BackButton.override();
}
console.log("Device.overrideBackButton() is deprecated. Use App.overrideBackbutton(true).");
navigator.app.overrideBackbutton(true);
};
/*
* DEPRECATED
* This is only for Android.
*
* This resets the back button to the default behaviour
*/
Device.prototype.resetBackButton = function() {
BackButton.reset();
}
console.log("Device.resetBackButton() is deprecated. Use App.overrideBackbutton(false).");
navigator.app.overrideBackbutton(false);
};
/*
* DEPRECATED
* This is only for Android.
*
* This terminates the activity!
*/
Device.prototype.exitApp = function() {
BackButton.exitApp();
}
console.log("Device.exitApp() is deprecated. Use App.exitApp().");
navigator.app.exitApp();
};
PhoneGap.addConstructor(function() {
navigator.device = window.device = new Device();
if (typeof navigator.device === "undefined") {
navigator.device = window.device = new Device();
}
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("filetransfer")) {
PhoneGap.addResource("filetransfer");
/**
* FileTransfer uploads a file to a remote server.
* @constructor
*/
var FileTransfer = function() {};
/**
* FileUploadResult
* @constructor
*/
var FileUploadResult = function() {
this.bytesSent = 0;
this.responseCode = null;
this.response = null;
};
/**
* FileTransferError
* @constructor
*/
var FileTransferError = function() {
this.code = null;
};
FileTransferError.FILE_NOT_FOUND_ERR = 1;
FileTransferError.INVALID_URL_ERR = 2;
FileTransferError.CONNECTION_ERR = 3;
/**
* Given an absolute file path, uploads a file on the device to a remote server
* using a multipart HTTP request.
* @param filePath {String} Full path of the file on the device
* @param server {String} URL of the server to receive the file
* @param successCallback (Function} Callback to be invoked when upload has completed
* @param errorCallback {Function} Callback to be invoked upon error
* @param options {FileUploadOptions} Optional parameters such as file name and mimetype
*/
FileTransfer.prototype.upload = function(filePath, server, successCallback, errorCallback, options, debug) {
// check for options
var fileKey = null;
var fileName = null;
var mimeType = null;
var params = null;
if (options) {
fileKey = options.fileKey;
fileName = options.fileName;
mimeType = options.mimeType;
if (options.params) {
params = options.params;
}
else {
params = {};
}
}
PhoneGap.exec(successCallback, errorCallback, 'FileTransfer', 'upload', [filePath, server, fileKey, fileName, mimeType, params, debug]);
};
/**
* Options to customize the HTTP request used to upload files.
* @constructor
* @param fileKey {String} Name of file request parameter.
* @param fileName {String} Filename to be used by the server. Defaults to image.jpg.
* @param mimeType {String} Mimetype of the uploaded file. Defaults to image/jpeg.
* @param params {Object} Object with key: value params to send to the server.
*/
var FileUploadOptions = function(fileKey, fileName, mimeType, params) {
this.fileKey = fileKey || null;
this.fileName = fileName || null;
this.mimeType = mimeType || null;
this.params = params || null;
};
}

View File

@@ -3,14 +3,17 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("geolocation")) {
PhoneGap.addResource("geolocation");
/**
* This class provides access to device GPS data.
* @constructor
*/
function Geolocation() {
var Geolocation = function() {
// The last known GPS position.
this.lastPosition = null;
@@ -22,10 +25,11 @@ function Geolocation() {
/**
* Position error object
*
* @constructor
* @param code
* @param message
*/
function PositionError(code, message) {
var PositionError = function(code, message) {
this.code = code;
this.message = message;
};
@@ -42,7 +46,7 @@ PositionError.TIMEOUT = 3;
* @param {PositionOptions} options The options for getting the position data. (OPTIONAL)
*/
Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallback, options) {
if (navigator._geo.listeners["global"]) {
if (navigator._geo.listeners.global) {
console.log("Geolocation Error: Still waiting for previous getCurrentPosition() request.");
try {
errorCallback(new PositionError(PositionError.TIMEOUT, "Geolocation Error: Still waiting for previous getCurrentPosition() request."));
@@ -53,20 +57,20 @@ Geolocation.prototype.getCurrentPosition = function(successCallback, errorCallba
var maximumAge = 10000;
var enableHighAccuracy = false;
var timeout = 10000;
if (typeof options != "undefined") {
if (typeof options.maximumAge != "undefined") {
if (typeof options !== "undefined") {
if (typeof options.maximumAge !== "undefined") {
maximumAge = options.maximumAge;
}
if (typeof options.enableHighAccuracy != "undefined") {
if (typeof options.enableHighAccuracy !== "undefined") {
enableHighAccuracy = options.enableHighAccuracy;
}
if (typeof options.timeout != "undefined") {
if (typeof options.timeout !== "undefined") {
timeout = options.timeout;
}
}
navigator._geo.listeners["global"] = {"success" : successCallback, "fail" : errorCallback };
navigator._geo.listeners.global = {"success" : successCallback, "fail" : errorCallback };
PhoneGap.exec(null, null, "Geolocation", "getCurrentLocation", [enableHighAccuracy, timeout, maximumAge]);
}
};
/**
* Asynchronously watches the geolocation for changes to geolocation. When a change occurs,
@@ -81,17 +85,17 @@ Geolocation.prototype.watchPosition = function(successCallback, errorCallback, o
var maximumAge = 10000;
var enableHighAccuracy = false;
var timeout = 10000;
if (typeof options != "undefined") {
if (typeof options.frequency != "undefined") {
if (typeof options !== "undefined") {
if (typeof options.frequency !== "undefined") {
maximumAge = options.frequency;
}
if (typeof options.maximumAge != "undefined") {
if (typeof options.maximumAge !== "undefined") {
maximumAge = options.maximumAge;
}
if (typeof options.enableHighAccuracy != "undefined") {
if (typeof options.enableHighAccuracy !== "undefined") {
enableHighAccuracy = options.enableHighAccuracy;
}
if (typeof options.timeout != "undefined") {
if (typeof options.timeout !== "undefined") {
timeout = options.timeout;
}
}
@@ -118,7 +122,7 @@ Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, s
var coords = new Coordinates(lat, lng, alt, altacc, head, vel);
var loc = new Position(coords, stamp);
try {
if (lat == "undefined" || lng == "undefined") {
if (lat === "undefined" || lng === "undefined") {
navigator._geo.listeners[id].fail(new PositionError(PositionError.POSITION_UNAVAILABLE, "Lat/Lng are undefined."));
}
else {
@@ -130,8 +134,8 @@ Geolocation.prototype.success = function(id, lat, lng, alt, altacc, head, vel, s
console.log("Geolocation Error: Error calling success callback function.");
}
if (id == "global") {
delete navigator._geo.listeners["global"];
if (id === "global") {
delete navigator._geo.listeners.global;
}
};
@@ -186,9 +190,9 @@ PhoneGap.addConstructor(function() {
navigator._geo = new Geolocation();
// No native geolocation object for Android 1.x, so use PhoneGap geolocation
if (typeof navigator.geolocation == 'undefined') {
if (typeof navigator.geolocation === 'undefined') {
navigator.geolocation = navigator._geo;
Geolocation.usingPhoneGap = true;
}
});
}

View File

@@ -5,9 +5,3 @@
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
PhoneGap.addConstructor(function() {
if (typeof navigator.splashScreen == "undefined") {
navigator.splashScreen = SplashScreen; // SplashScreen object come from native side through addJavaScriptInterface
}
});

View File

@@ -1,23 +0,0 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
function KeyEvent()
{
}
KeyEvent.prototype.backTrigger = function()
{
var e = document.createEvent('Events');
e.initEvent('backKeyDown');
document.dispatchEvent(e);
}
if (document.keyEvent == null || typeof document.keyEvent == 'undefined')
{
window.keyEvent = document.keyEvent = new KeyEvent();
}

View File

@@ -3,66 +3,16 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
/**
* List of media objects.
* PRIVATE
*/
PhoneGap.mediaObjects = {};
/**
* Object that receives native callbacks.
* PRIVATE
*/
PhoneGap.Media = function() {};
/**
* Get the media object.
* PRIVATE
*
* @param id The media object id (string)
*/
PhoneGap.Media.getMediaObject = function(id) {
return PhoneGap.mediaObjects[id];
};
/**
* Audio has status update.
* PRIVATE
*
* @param id The media object id (string)
* @param status The status code (int)
* @param msg The status message (string)
*/
PhoneGap.Media.onStatus = function(id, msg, value) {
var media = PhoneGap.mediaObjects[id];
// If state update
if (msg == Media.MEDIA_STATE) {
if (value == Media.MEDIA_STOPPED) {
if (media.successCallback) {
media.successCallback();
}
}
if (media.statusCallback) {
media.statusCallback(value);
}
}
else if (msg == Media.MEDIA_DURATION) {
media._duration = value;
}
else if (msg == Media.MEDIA_ERROR) {
if (media.errorCallback) {
media.errorCallback(value);
}
}
};
if (!PhoneGap.hasResource("media")) {
PhoneGap.addResource("media");
/**
* This class provides access to the device media, interfaces to both sound and video
*
* @constructor
* @param src The file name or url to play
* @param successCallback The callback to be called when the file is done playing or recording.
* successCallback() - OPTIONAL
@@ -73,28 +23,28 @@ PhoneGap.Media.onStatus = function(id, msg, value) {
* @param positionCallback The callback to be called when media position has changed.
* positionCallback(long position) - OPTIONAL
*/
Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
var Media = function(src, successCallback, errorCallback, statusCallback, positionCallback) {
// successCallback optional
if (successCallback && (typeof successCallback != "function")) {
if (successCallback && (typeof successCallback !== "function")) {
console.log("Media Error: successCallback is not a function");
return;
}
// errorCallback optional
if (errorCallback && (typeof errorCallback != "function")) {
if (errorCallback && (typeof errorCallback !== "function")) {
console.log("Media Error: errorCallback is not a function");
return;
}
// statusCallback optional
if (statusCallback && (typeof statusCallback != "function")) {
if (statusCallback && (typeof statusCallback !== "function")) {
console.log("Media Error: statusCallback is not a function");
return;
}
// statusCallback optional
if (positionCallback && (typeof positionCallback != "function")) {
if (positionCallback && (typeof positionCallback !== "function")) {
console.log("Media Error: positionCallback is not a function");
return;
}
@@ -113,6 +63,7 @@ Media = function(src, successCallback, errorCallback, statusCallback, positionCa
// Media messages
Media.MEDIA_STATE = 1;
Media.MEDIA_DURATION = 2;
Media.MEDIA_POSITION = 3;
Media.MEDIA_ERROR = 9;
// Media states
@@ -128,8 +79,8 @@ Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
* This class contains information about any Media errors.
* @constructor
*/
function MediaError() {
this.code = null,
var MediaError = function() {
this.code = null;
this.message = "";
};
@@ -152,6 +103,13 @@ Media.prototype.stop = function() {
return PhoneGap.exec(null, null, "Media", "stopPlayingAudio", [this.id]);
};
/**
* Seek or jump to a new time in the track..
*/
Media.prototype.seekTo = function(milliseconds) {
PhoneGap.exec(null, null, "Media", "seekToAudio", [this.id, milliseconds]);
};
/**
* Pause playing audio file.
*/
@@ -171,8 +129,6 @@ Media.prototype.getDuration = function() {
/**
* Get position of audio.
*
* @return
*/
Media.prototype.getCurrentPosition = function(success, fail) {
PhoneGap.exec(success, fail, "Media", "getCurrentPositionAudio", [this.id]);
@@ -192,3 +148,67 @@ Media.prototype.stopRecord = function() {
PhoneGap.exec(null, null, "Media", "stopRecordingAudio", [this.id]);
};
/**
* Release the resources.
*/
Media.prototype.release = function() {
PhoneGap.exec(null, null, "Media", "release", [this.id]);
};
/**
* List of media objects.
* PRIVATE
*/
PhoneGap.mediaObjects = {};
/**
* Object that receives native callbacks.
* PRIVATE
* @constructor
*/
PhoneGap.Media = function() {};
/**
* Get the media object.
* PRIVATE
*
* @param id The media object id (string)
*/
PhoneGap.Media.getMediaObject = function(id) {
return PhoneGap.mediaObjects[id];
};
/**
* Audio has status update.
* PRIVATE
*
* @param id The media object id (string)
* @param status The status code (int)
* @param msg The status message (string)
*/
PhoneGap.Media.onStatus = function(id, msg, value) {
var media = PhoneGap.mediaObjects[id];
// If state update
if (msg === Media.MEDIA_STATE) {
if (value === Media.MEDIA_STOPPED) {
if (media.successCallback) {
media.successCallback();
}
}
if (media.statusCallback) {
media.statusCallback(value);
}
}
else if (msg === Media.MEDIA_DURATION) {
media._duration = value;
}
else if (msg === Media.MEDIA_ERROR) {
if (media.errorCallback) {
media.errorCallback(value);
}
}
else if (msg == Media.MEDIA_POSITION) {
media._position = value;
}
};
}

View File

@@ -3,60 +3,80 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("network")) {
PhoneGap.addResource("network");
/**
* This class contains information about any NetworkStatus.
* This class contains information about the current network Connection.
* @constructor
*/
function NetworkStatus() {
//this.code = null;
//this.message = "";
var Connection = function() {
this.type = null;
this._firstRun = true;
this._timer = null;
this.timeout = 500;
var me = this;
this.getInfo(
function(type) {
// Need to send events if we are on or offline
if (type == "none") {
// set a timer if still offline at the end of timer send the offline event
me._timer = setTimeout(function(){
me.type = type;
PhoneGap.fireEvent('offline');
me._timer = null;
}, me.timeout);
} else {
// If there is a current offline event pending clear it
if (me._timer != null) {
clearTimeout(me._timer);
me._timer = null;
}
me.type = type;
PhoneGap.fireEvent('online');
}
// should only fire this once
if (me._firstRun) {
me._firstRun = false;
PhoneGap.onPhoneGapConnectionReady.fire();
}
},
function(e) {
console.log("Error initializing Network Connection: " + e);
});
};
NetworkStatus.NOT_REACHABLE = 0;
NetworkStatus.REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
NetworkStatus.REACHABLE_VIA_WIFI_NETWORK = 2;
Connection.UNKNOWN = "unknown";
Connection.ETHERNET = "ethernet";
Connection.WIFI = "wifi";
Connection.CELL_2G = "2g";
Connection.CELL_3G = "3g";
Connection.CELL_4G = "4g";
Connection.NONE = "none";
/**
* This class provides access to device Network data (reachability).
* @constructor
* Get connection info
*
* @param {Function} successCallback The function to call when the Connection data is available
* @param {Function} errorCallback The function to call when there is an error getting the Connection data. (OPTIONAL)
*/
function Network() {
/**
* The last known Network status.
* { hostName: string, ipAddress: string,
remoteHostStatus: int(0/1/2), internetConnectionStatus: int(0/1/2), localWiFiConnectionStatus: int (0/2) }
*/
this.lastReachability = null;
Connection.prototype.getInfo = function(successCallback, errorCallback) {
// Get info
PhoneGap.exec(successCallback, errorCallback, "Network Status", "getConnectionInfo", []);
};
/**
* 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]);
};
PhoneGap.addConstructor(function() {
if (typeof navigator.network == "undefined") navigator.network = new Network();
if (typeof navigator.network === "undefined") {
navigator.network = new Object();
}
if (typeof navigator.network.connection === "undefined") {
navigator.network.connection = new Connection();
}
});
}

View File

@@ -3,14 +3,18 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("notification")) {
PhoneGap.addResource("notification");
/**
* This class provides access to notifications on the device.
* @constructor
*/
function Notification() {
}
var Notification = function() {
};
/**
* Open a native alert dialog, with a customizable title and button text.
@@ -111,6 +115,8 @@ Notification.prototype.beep = function(count) {
};
PhoneGap.addConstructor(function() {
if (typeof navigator.notification == "undefined") navigator.notification = new Notification();
if (typeof navigator.notification === "undefined") {
navigator.notification = new Notification();
}
});
}

View File

@@ -1,11 +1,12 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (typeof PhoneGap === "undefined") {
/**
* The order of events during page load and PhoneGap startup is as follows:
@@ -18,6 +19,8 @@
* onPhoneGapInfoReady Internal event fired when device properties are available
* onDeviceReady User event fired to indicate that PhoneGap is ready
* onResume User event fired to indicate a start/resume lifecycle event
* onPause User event fired to indicate a pause lifecycle event
* onDestroy Internal event fired when app is being destroyed (User should use window.onunload event, not this one).
*
* The only PhoneGap events that user code should register for are:
* onDeviceReady
@@ -26,10 +29,12 @@
* Listeners can be registered as:
* document.addEventListener("deviceready", myDeviceReadyListener, false);
* document.addEventListener("resume", myResumeListener, false);
* document.addEventListener("pause", myPauseListener, false);
*/
if (typeof(DeviceInfo) != 'object')
DeviceInfo = {};
if (typeof(DeviceInfo) !== 'object') {
var DeviceInfo = {};
}
/**
* This represents the PhoneGap API itself, and provides a global namespace for accessing
@@ -44,11 +49,36 @@ var PhoneGap = {
}
};
/**
* List of resource files loaded by PhoneGap.
* This is used to ensure JS and other files are loaded only once.
*/
PhoneGap.resources = {base: true};
/**
* Determine if resource has been loaded by PhoneGap
*
* @param name
* @return
*/
PhoneGap.hasResource = function(name) {
return PhoneGap.resources[name];
};
/**
* Add a resource to list of loaded resources by PhoneGap
*
* @param name
*/
PhoneGap.addResource = function(name) {
PhoneGap.resources[name] = true;
};
/**
* Custom pub-sub channel that can have functions subscribed to it
* @constructor
*/
PhoneGap.Channel = function(type)
PhoneGap.Channel = function (type)
{
this.type = type;
this.handlers = {};
@@ -58,7 +88,7 @@ PhoneGap.Channel = function(type)
};
/**
* Subscribes the given function to the channel. Any time that
* Subscribes the given function to the channel. Any time that
* Channel.fire is called so too will the function.
* Optionally specify an execution context for the function
* and a guid that can be used to stop subscribing to the channel.
@@ -66,10 +96,10 @@ PhoneGap.Channel = function(type)
*/
PhoneGap.Channel.prototype.subscribe = function(f, c, g) {
// need a function to call
if (f == null) { return; }
if (f === null) { return; }
var func = f;
if (typeof c == "object" && f instanceof Function) { func = PhoneGap.close(c, f); }
if (typeof c === "object" && typeof f === "function") { func = PhoneGap.close(c, f); }
g = g || func.observer_guid || f.observer_guid || this.guid++;
func.observer_guid = g;
@@ -88,9 +118,9 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
var m = function() {
f.apply(c || null, arguments);
_this.unsubscribe(g);
}
};
if (this.fired) {
if (typeof c == "object" && f instanceof Function) { f = PhoneGap.close(c, f); }
if (typeof c === "object" && typeof f === "function") { f = PhoneGap.close(c, f); }
f.apply(this, this.fireArgs);
} else {
g = this.subscribe(m);
@@ -98,26 +128,29 @@ PhoneGap.Channel.prototype.subscribeOnce = function(f, c) {
return g;
};
/**
/**
* Unsubscribes the function with the given guid from the channel.
*/
PhoneGap.Channel.prototype.unsubscribe = function(g) {
if (g instanceof Function) { g = g.observer_guid; }
if (typeof g === "function") { g = g.observer_guid; }
this.handlers[g] = null;
delete this.handlers[g];
};
/**
/**
* Calls all functions subscribed to this channel.
*/
PhoneGap.Channel.prototype.fire = function(e) {
if (this.enabled) {
var fail = false;
for (var item in this.handlers) {
var handler = this.handlers[item];
if (handler instanceof Function) {
var rv = (handler.apply(this, arguments)==false);
fail = fail || rv;
var item, handler, rv;
for (item in this.handlers) {
if (this.handlers.hasOwnProperty(item)) {
handler = this.handlers[item];
if (typeof handler === "function") {
rv = (handler.apply(this, arguments) === false);
fail = fail || rv;
}
}
}
this.fired = true;
@@ -134,18 +167,29 @@ PhoneGap.Channel.prototype.fire = function(e) {
PhoneGap.Channel.join = function(h, c) {
var i = c.length;
var f = function() {
if (!(--i)) h();
if (!(--i)) {
h();
}
};
var len = i;
var j;
for (j=0; j<len; j++) {
if (!c[j].fired) {
c[j].subscribeOnce(f);
}
else {
i--;
}
}
for (var j=0; j<i; j++) {
(!c[j].fired?c[j].subscribeOnce(f):i--);
if (!i) {
h();
}
if (!i) h();
};
/**
* Boolean flag indicating if the PhoneGap API is available and initialized.
*/ // TODO: Remove this, it is unused here ... -jm
PhoneGap.available = DeviceInfo.uuid != undefined;
PhoneGap.available = DeviceInfo.uuid !== undefined;
/**
* Add an initialization function to a queue that ensures it will run and initialize
@@ -157,30 +201,36 @@ PhoneGap.addConstructor = function(func) {
try {
func();
} catch(e) {
if (typeof(debug['log']) == 'function') {
debug.log("Failed to run constructor: " + debug.processMessage(e));
} else {
alert("Failed to run constructor: " + e.message);
}
console.log("Failed to run constructor: " + e);
}
});
};
/**
* Adds a plugin object to window.plugins
* Plugins object
*/
PhoneGap.addPlugin = function(name, obj) {
if ( !window.plugins ) {
window.plugins = {};
}
if ( !window.plugins[name] ) {
window.plugins[name] = obj;
}
if (!window.plugins) {
window.plugins = {};
}
/**
* onDOMContentLoaded channel is fired when the DOM content
* Adds a plugin object to window.plugins.
* The plugin is accessed using window.plugins.<name>
*
* @param name The plugin name
* @param obj The plugin object
*/
PhoneGap.addPlugin = function(name, obj) {
if (!window.plugins[name]) {
window.plugins[name] = obj;
}
else {
console.log("Error: Plugin "+name+" already exists.");
}
};
/**
* onDOMContentLoaded channel is fired when the DOM content
* of the page has been parsed.
*/
PhoneGap.onDOMContentLoaded = new PhoneGap.Channel('onDOMContentLoaded');
@@ -208,6 +258,12 @@ PhoneGap.onPhoneGapReady = new PhoneGap.Channel('onPhoneGapReady');
*/
PhoneGap.onPhoneGapInfoReady = new PhoneGap.Channel('onPhoneGapInfoReady');
/**
* onPhoneGapConnectionReady channel is fired when the PhoneGap connection properties
* has been set.
*/
PhoneGap.onPhoneGapConnectionReady = new PhoneGap.Channel('onPhoneGapConnectionReady');
/**
* onResume channel is fired when the PhoneGap native code
* resumes.
@@ -220,8 +276,19 @@ PhoneGap.onResume = new PhoneGap.Channel('onResume');
*/
PhoneGap.onPause = new PhoneGap.Channel('onPause');
/**
* onDestroy channel is fired when the PhoneGap native code
* is destroyed. It is used internally.
* Window.onunload should be used by the user.
*/
PhoneGap.onDestroy = new PhoneGap.Channel('onDestroy');
PhoneGap.onDestroy.subscribeOnce(function() {
PhoneGap.shuttingDown = true;
});
PhoneGap.shuttingDown = false;
// _nativeReady is global variable that the native side can set
// to signify that the native code is ready. It is a global since
// to signify that the native code is ready. It is a global since
// it may be called before any PhoneGap JS is ready.
if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); }
@@ -232,6 +299,39 @@ if (typeof _nativeReady !== 'undefined') { PhoneGap.onNativeReady.fire(); }
PhoneGap.onDeviceReady = new PhoneGap.Channel('onDeviceReady');
// Array of channels that must fire before "deviceready" is fired
PhoneGap.deviceReadyChannelsArray = [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady, PhoneGap.onPhoneGapConnectionReady];
// Hashtable of user defined channels that must also fire before "deviceready" is fired
PhoneGap.deviceReadyChannelsMap = {};
/**
* Indicate that a feature needs to be initialized before it is ready to be used.
* This holds up PhoneGap's "deviceready" event until the feature has been initialized
* and PhoneGap.initComplete(feature) is called.
*
* @param feature {String} The unique feature name
*/
PhoneGap.waitForInitialization = function(feature) {
if (feature) {
var channel = new PhoneGap.Channel(feature);
PhoneGap.deviceReadyChannelsMap[feature] = channel;
PhoneGap.deviceReadyChannelsArray.push(channel);
}
};
/**
* Indicate that initialization code has completed and the feature is ready to be used.
*
* @param feature {String} The unique feature name
*/
PhoneGap.initializationComplete = function(feature) {
var channel = PhoneGap.deviceReadyChannelsMap[feature];
if (channel) {
channel.fire();
}
};
/**
* Create all PhoneGap objects once page has fully loaded and native side is ready.
*/
@@ -239,11 +339,20 @@ PhoneGap.Channel.join(function() {
// Start listening for XHR callbacks
setTimeout(function() {
if (CallbackServer.usePolling()) {
if (PhoneGap.UsePolling) {
PhoneGap.JSCallbackPolling();
}
else {
PhoneGap.JSCallback();
var polling = prompt("usePolling", "gap_callbackServer:");
PhoneGap.UsePolling = polling;
if (polling == "true") {
PhoneGap.UsePolling = true;
PhoneGap.JSCallbackPolling();
}
else {
PhoneGap.UsePolling = false;
PhoneGap.JSCallback();
}
}
}, 1);
@@ -253,22 +362,17 @@ PhoneGap.Channel.join(function() {
// Fire event to notify that all objects are created
PhoneGap.onPhoneGapReady.fire();
// Fire onDeviceReady event once all constructors have run and PhoneGap info has been
// received from native side, and any user defined initialization channels.
PhoneGap.Channel.join(function() {
PhoneGap.onDeviceReady.fire();
// Fire the onresume event, since first one happens before JavaScript is loaded
PhoneGap.onResume.fire();
}, PhoneGap.deviceReadyChannelsArray);
}, [ PhoneGap.onDOMContentLoaded, PhoneGap.onNativeReady ]);
/**
* Fire onDeviceReady event once all constructors have run and PhoneGap info has been
* received from native side.
*/
PhoneGap.Channel.join(function() {
// Turn off app loading dialog
navigator.notification.activityStop();
PhoneGap.onDeviceReady.fire();
// Fire the onresume event, since first one happens before JavaScript is loaded
PhoneGap.onResume.fire();
}, [ PhoneGap.onPhoneGapReady, PhoneGap.onPhoneGapInfoReady]);
// Listen for DOMContentLoaded and notify our channel subscribers
document.addEventListener('DOMContentLoaded', function() {
PhoneGap.onDOMContentLoaded.fire();
@@ -279,15 +383,48 @@ PhoneGap.m_document_addEventListener = document.addEventListener;
document.addEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
if (e == 'deviceready') {
if (e === 'deviceready') {
PhoneGap.onDeviceReady.subscribeOnce(handler);
} else if (e == 'resume') {
} else if (e === 'resume') {
PhoneGap.onResume.subscribe(handler);
} else if (e == 'pause') {
if (PhoneGap.onDeviceReady.fired) {
PhoneGap.onResume.fire();
}
} else if (e === 'pause') {
PhoneGap.onPause.subscribe(handler);
} else {
PhoneGap.m_document_addEventListener.call(document, evt, handler);
}
else {
// If subscribing to Android backbutton
if (e === 'backbutton') {
PhoneGap.exec(null, null, "App", "overrideBackbutton", [true]);
}
PhoneGap.m_document_addEventListener.call(document, evt, handler, capture);
}
};
// Intercept calls to document.removeEventListener and watch for events that
// are generated by PhoneGap native code
PhoneGap.m_document_removeEventListener = document.removeEventListener;
document.removeEventListener = function(evt, handler, capture) {
var e = evt.toLowerCase();
// If unsubscribing to Android backbutton
if (e === 'backbutton') {
PhoneGap.exec(null, null, "App", "overrideBackbutton", [false]);
}
PhoneGap.m_document_removeEventListener.call(document, evt, handler, capture);
};
/**
* Method to fire event from native code
*/
PhoneGap.fireEvent = function(type) {
var e = document.createEvent('Events');
e.initEvent(type);
document.dispatchEvent(e);
};
/**
@@ -295,51 +432,56 @@ document.addEventListener = function(evt, handler, capture) {
* The restriction on ours is that it must be an array of simple types.
*
* @param args
* @return
* @return {String}
*/
PhoneGap.stringify = function(args) {
if (typeof JSON == "undefined") {
if (typeof JSON === "undefined") {
var s = "[";
for (var i=0; i<args.length; i++) {
if (i > 0) {
s = s + ",";
}
var type = typeof args[i];
if ((type == "number") || (type == "boolean")) {
s = s + args[i];
}
else if (args[i] instanceof Array) {
s = s + "[" + args[i] + "]";
}
else if (args[i] instanceof Object) {
var start = true;
s = s + '{';
for (var name in args[i]) {
if (!start) {
s = s + ',';
}
s = s + '"' + name + '":';
var nameType = typeof args[i][name];
if ((nameType == "number") || (nameType == "boolean")) {
s = s + args[i][name];
}
else {
s = s + '"' + args[i][name] + '"';
}
start=false;
}
s = s + '}';
}
else {
var a = args[i].replace(/\\/g, '\\\\');
a = a.replace(/"/g, '\\"');
s = s + '"' + a + '"';
var i, type, start, name, nameType, a;
for (i = 0; i < args.length; i++) {
if (args[i] !== null) {
if (i > 0) {
s = s + ",";
}
type = typeof args[i];
if ((type === "number") || (type === "boolean")) {
s = s + args[i];
} else if (args[i] instanceof Array) {
s = s + "[" + args[i] + "]";
} else if (args[i] instanceof Object) {
start = true;
s = s + '{';
for (name in args[i]) {
if (args[i][name] !== null) {
if (!start) {
s = s + ',';
}
s = s + '"' + name + '":';
nameType = typeof args[i][name];
if ((nameType === "number") || (nameType === "boolean")) {
s = s + args[i][name];
} else if ((typeof args[i][name]) === 'function') {
// don't copy the functions
s = s + '""';
} else if (args[i][name] instanceof Object) {
s = s + PhoneGap.stringify(args[i][name]);
} else {
s = s + '"' + args[i][name] + '"';
}
start = false;
}
}
s = s + '}';
} else {
a = args[i].replace(/\\/g, '\\\\');
a = a.replace(/"/g, '\\"');
s = s + '"' + a + '"';
}
}
}
s = s + "]";
return s;
}
else {
} else {
return JSON.stringify(args);
}
};
@@ -348,36 +490,41 @@ PhoneGap.stringify = function(args) {
* Does a deep clone of the object.
*
* @param obj
* @return
* @return {Object}
*/
PhoneGap.clone = function(obj) {
if(!obj) {
return obj;
}
if(obj instanceof Array){
var retVal = new Array();
for(var i = 0; i < obj.length; ++i){
retVal.push(PhoneGap.clone(obj[i]));
}
return retVal;
}
if (obj instanceof Function) {
return obj;
}
if(!(obj instanceof Object)){
return obj;
}
retVal = new Object();
for(i in obj){
if(!(i in retVal) || retVal[i] != obj[i]) {
retVal[i] = PhoneGap.clone(obj[i]);
}
}
return retVal;
var i, retVal;
if(!obj) {
return obj;
}
if(obj instanceof Array){
retVal = [];
for(i = 0; i < obj.length; ++i){
retVal.push(PhoneGap.clone(obj[i]));
}
return retVal;
}
if (typeof obj === "function") {
return obj;
}
if(!(obj instanceof Object)){
return obj;
}
if (obj instanceof Date) {
return obj;
}
retVal = {};
for(i in obj){
if(!(i in retVal) || retVal[i] !== obj[i]) {
retVal[i] = PhoneGap.clone(obj[i]);
}
}
return retVal;
};
PhoneGap.callbackId = 0;
@@ -397,7 +544,7 @@ PhoneGap.callbackStatus = {
/**
* Execute a PhoneGap command. It is up to the native side whether this action is synch or async.
* Execute a PhoneGap command. It is up to the native side whether this action is synch or async.
* The native side can return:
* Synchronous: PluginResult object as a JSON string
* Asynchrounous: Empty string ""
@@ -408,7 +555,7 @@ PhoneGap.callbackStatus = {
* @param {Function} fail The fail callback
* @param {String} service The name of the service to use
* @param {String} action Action to be run in PhoneGap
* @param {String[]} [args] Zero or more arguments to pass to the method
* @param {Array.<String>} [args] Zero or more arguments to pass to the method
*/
PhoneGap.exec = function(success, fail, service, action, args) {
try {
@@ -416,24 +563,23 @@ PhoneGap.exec = function(success, fail, service, action, args) {
if (success || fail) {
PhoneGap.callbacks[callbackId] = {success:success, fail:fail};
}
// Note: Device returns string, but for some reason emulator returns object - so convert to string.
var r = ""+PluginManager.exec(service, action, callbackId, this.stringify(args), true);
var r = prompt(PhoneGap.stringify(args), "gap:"+PhoneGap.stringify([service, action, callbackId, true]));
// If a result was returned
if (r.length > 0) {
eval("var v="+r+";");
// If status is OK, then return value back to caller
if (v.status == PhoneGap.callbackStatus.OK) {
// If there is a success callback, then call it now with returned value
// If status is OK, then return value back to caller
if (v.status === PhoneGap.callbackStatus.OK) {
// If there is a success callback, then call it now with
// returned value
if (success) {
try {
success(v.message);
}
catch (e) {
console.log("Error in success callback: "+callbackId+" = "+e);
} catch (e) {
console.log("Error in success callback: " + callbackId + " = " + e);
}
// Clear callback if not expecting any more results
@@ -445,8 +591,8 @@ PhoneGap.exec = function(success, fail, service, action, args) {
}
// If no result
else if (v.status == PhoneGap.callbackStatus.NO_RESULT) {
else if (v.status === PhoneGap.callbackStatus.NO_RESULT) {
// Clear callback if not expecting any more results
if (!v.keepCallback) {
delete PhoneGap.callbacks[callbackId];
@@ -455,15 +601,15 @@ PhoneGap.exec = function(success, fail, service, action, args) {
// If error, then display error
else {
console.log("Error: Status="+r.status+" Message="+v.message);
console.log("Error: Status="+v.status+" Message="+v.message);
// If there is a fail callback, then call it now with returned value
if (fail) {
try {
fail(v.message);
}
catch (e) {
console.log("Error in error callback: "+callbackId+" = "+e);
catch (e1) {
console.log("Error in error callback: "+callbackId+" = "+e1);
}
// Clear callback if not expecting any more results
@@ -474,8 +620,8 @@ PhoneGap.exec = function(success, fail, service, action, args) {
return null;
}
}
} catch (e) {
console.log("Error: "+e);
} catch (e2) {
console.log("Error: "+e2);
}
};
@@ -489,7 +635,7 @@ PhoneGap.callbackSuccess = function(callbackId, args) {
if (PhoneGap.callbacks[callbackId]) {
// If result is to be sent to callback
if (args.status == PhoneGap.callbackStatus.OK) {
if (args.status === PhoneGap.callbackStatus.OK) {
try {
if (PhoneGap.callbacks[callbackId].success) {
PhoneGap.callbacks[callbackId].success(args.message);
@@ -499,7 +645,7 @@ PhoneGap.callbackSuccess = function(callbackId, args) {
console.log("Error in success callback: "+callbackId+" = "+e);
}
}
// Clear callback if not expecting any more results
if (!args.keepCallback) {
delete PhoneGap.callbacks[callbackId];
@@ -523,7 +669,7 @@ PhoneGap.callbackError = function(callbackId, args) {
catch (e) {
console.log("Error in error callback: "+callbackId+" = "+e);
}
// Clear callback if not expecting any more results
if (!args.keepCallback) {
delete PhoneGap.callbacks[callbackId];
@@ -541,71 +687,94 @@ PhoneGap.callbackError = function(callbackId, args) {
*/
// TODO: Is this used?
PhoneGap.run_command = function() {
if (!PhoneGap.available || !PhoneGap.queue.ready)
if (!PhoneGap.available || !PhoneGap.queue.ready) {
return;
}
PhoneGap.queue.ready = false;
var args = PhoneGap.queue.commands.shift();
if (PhoneGap.queue.commands.length == 0) {
if (PhoneGap.queue.commands.length === 0) {
clearInterval(PhoneGap.queue.timer);
PhoneGap.queue.timer = null;
}
var uri = [];
var dict = null;
for (var i = 1; i < args.length; i++) {
var i;
for (i = 1; i < args.length; i++) {
var arg = args[i];
if (arg == undefined || arg == null)
if (arg === undefined || arg === null) {
arg = '';
if (typeof(arg) == 'object')
}
if (typeof(arg) === 'object') {
dict = arg;
else
} else {
uri.push(encodeURIComponent(arg));
}
}
var url = "gap://" + args[0] + "/" + uri.join("/");
if (dict != null) {
if (dict !== null) {
var name;
var query_args = [];
for (var name in dict) {
if (typeof(name) != 'string')
continue;
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
for (name in dict) {
if (dict.hasOwnProperty(name) && (typeof (name) === 'string')) {
query_args.push(encodeURIComponent(name) + "=" + encodeURIComponent(dict[name]));
}
}
if (query_args.length > 0)
if (query_args.length > 0) {
url += "?" + query_args.join("&");
}
}
document.location = url;
};
PhoneGap.JSCallbackPort = CallbackServer.getPort();
PhoneGap.JSCallbackToken = CallbackServer.getToken();
PhoneGap.JSCallbackPort = null;
PhoneGap.JSCallbackToken = null;
/**
* This is only for Android.
*
* Internal function that uses XHR to call into PhoneGap Java code and retrieve
* Internal function that uses XHR to call into PhoneGap Java code and retrieve
* any JavaScript code that needs to be run. This is used for callbacks from
* Java to JavaScript.
*/
PhoneGap.JSCallback = function() {
// Exit if shutting down app
if (PhoneGap.shuttingDown) {
return;
}
// If polling flag was changed, start using polling from now on
if (PhoneGap.UsePolling) {
PhoneGap.JSCallbackPolling();
return;
}
var xmlhttp = new XMLHttpRequest();
// Callback function when XMLHttpRequest is ready
xmlhttp.onreadystatechange=function(){
if(xmlhttp.readyState == 4){
if(xmlhttp.readyState === 4){
// Exit if shutting down app
if (PhoneGap.shuttingDown) {
return;
}
// If callback has JavaScript statement to execute
if (xmlhttp.status == 200) {
if (xmlhttp.status === 200) {
var msg = xmlhttp.responseText;
// Need to url decode the response
var msg = decodeURIComponent(xmlhttp.responseText);
setTimeout(function() {
try {
var t = eval(msg);
}
catch (e) {
// If we're getting an error here, seeing the message will help in debugging
console.log("Message from Server: " + msg);
console.log("JSCallback: Message from Server: " + msg);
console.log("JSCallback Error: "+e);
}
}, 1);
@@ -613,19 +782,40 @@ PhoneGap.JSCallback = function() {
}
// If callback ping (used to keep XHR request from timing out)
else if (xmlhttp.status == 404) {
else if (xmlhttp.status === 404) {
setTimeout(PhoneGap.JSCallback, 10);
}
// If error, restart callback server
// If security error
else if (xmlhttp.status === 403) {
console.log("JSCallback Error: Invalid token. Stopping callbacks.");
}
// If server is stopping
else if (xmlhttp.status === 503) {
console.log("JSCallback Error: Service unavailable. Stopping callbacks.");
}
// If request wasn't GET
else if (xmlhttp.status === 400) {
console.log("JSCallback Error: Bad request. Stopping callbacks.");
}
// If error, revert to polling
else {
console.log("JSCallback Error: Request failed.");
CallbackServer.restartServer();
setTimeout(PhoneGap.JSCallback, 100);
PhoneGap.UsePolling = true;
PhoneGap.JSCallbackPolling();
}
}
}
};
if (PhoneGap.JSCallbackPort === null) {
PhoneGap.JSCallbackPort = prompt("getPort", "gap_callbackServer:");
}
if (PhoneGap.JSCallbackToken === null) {
PhoneGap.JSCallbackToken = prompt("getToken", "gap_callbackServer:");
}
xmlhttp.open("GET", "http://127.0.0.1:"+PhoneGap.JSCallbackPort+"/"+PhoneGap.JSCallbackToken , true);
xmlhttp.send();
};
@@ -636,21 +826,39 @@ PhoneGap.JSCallback = function() {
*/
PhoneGap.JSCallbackPollingPeriod = 50;
/**
* Flag that can be set by the user to force polling to be used or force XHR to be used.
*/
PhoneGap.UsePolling = false; // T=use polling, F=use XHR
/**
* This is only for Android.
*
* Internal function that uses polling to call into PhoneGap Java code and retrieve
* Internal function that uses polling to call into PhoneGap Java code and retrieve
* any JavaScript code that needs to be run. This is used for callbacks from
* Java to JavaScript.
*/
PhoneGap.JSCallbackPolling = function() {
var msg = CallbackServer.getJavascript();
// Exit if shutting down app
if (PhoneGap.shuttingDown) {
return;
}
// If polling flag was changed, stop using polling from now on
if (!PhoneGap.UsePolling) {
PhoneGap.JSCallback();
return;
}
var msg = prompt("", "gap_poll:");
if (msg) {
setTimeout(function() {
try {
var t = eval(""+msg);
}
catch (e) {
console.log("JSCallbackPolling: Message from Server: " + msg);
console.log("JSCallbackPolling Error: "+e);
}
}, 1);
@@ -664,7 +872,7 @@ PhoneGap.JSCallbackPolling = function() {
/**
* Create a UUID
*
* @return
* @return {String}
*/
PhoneGap.createUUID = function() {
return PhoneGap.UUIDcreatePart(4) + '-' +
@@ -676,9 +884,10 @@ PhoneGap.createUUID = function() {
PhoneGap.UUIDcreatePart = function(length) {
var uuidpart = "";
for (var i=0; i<length; i++) {
var uuidchar = parseInt((Math.random() * 256)).toString(16);
if (uuidchar.length == 1) {
var i, uuidchar;
for (i=0; i<length; i++) {
uuidchar = parseInt((Math.random() * 256),0).toString(16);
if (uuidchar.length === 1) {
uuidchar = "0" + uuidchar;
}
uuidpart += uuidchar;
@@ -690,11 +899,29 @@ PhoneGap.close = function(context, func, params) {
if (typeof params === 'undefined') {
return function() {
return func.apply(context, arguments);
}
};
} else {
return function() {
return func.apply(context, params);
}
};
}
};
/**
* Load a JavaScript file after page has loaded.
*
* @param {String} jsfile The url of the JavaScript file to load.
* @param {Function} successCallback The callback to call when the file has been loaded.
*/
PhoneGap.includeJavascript = function(jsfile, successCallback) {
var id = document.getElementsByTagName("head")[0];
var el = document.createElement('script');
el.type = 'text/javascript';
if (typeof successCallback === 'function') {
el.onload = successCallback;
}
el.src = jsfile;
id.appendChild(el);
};
}

View File

@@ -3,9 +3,12 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
if (!PhoneGap.hasResource("position")) {
PhoneGap.addResource("position");
/**
* This class contains position information.
* @param {Object} lat
@@ -17,12 +20,13 @@
* @param {Object} vel
* @constructor
*/
function Position(coords, timestamp) {
var Position = function(coords, timestamp) {
this.coords = coords;
this.timestamp = (timestamp != 'undefined') ? timestamp : new Date().getTime();
}
this.timestamp = (timestamp !== 'undefined') ? timestamp : new Date().getTime();
};
function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
/** @constructor */
var Coordinates = function(lat, lng, alt, acc, head, vel, altacc) {
/**
* The latitude of the position.
*/
@@ -50,14 +54,14 @@ function Coordinates(lat, lng, alt, acc, head, vel, altacc) {
/**
* The altitude accuracy of the position.
*/
this.altitudeAccuracy = (altacc != 'undefined') ? altacc : null;
}
this.altitudeAccuracy = (altacc !== 'undefined') ? altacc : null;
};
/**
* This class specifies the options for requesting position data.
* @constructor
*/
function PositionOptions() {
var PositionOptions = function() {
/**
* Specifies the desired position accuracy.
*/
@@ -67,18 +71,19 @@ function PositionOptions() {
* is called.
*/
this.timeout = 10000;
}
};
/**
* This class contains information about any GSP errors.
* @constructor
*/
function PositionError() {
var PositionError = function() {
this.code = null;
this.message = "";
}
};
PositionError.UNKNOWN_ERROR = 0;
PositionError.PERMISSION_DENIED = 1;
PositionError.POSITION_UNAVAILABLE = 2;
PositionError.TIMEOUT = 3;
}

View File

@@ -3,7 +3,7 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
/*
@@ -12,29 +12,45 @@
* most manufacturers ship with Android 1.5 and do not do OTA Updates, this is required
*/
if (!PhoneGap.hasResource("storage")) {
PhoneGap.addResource("storage");
/**
* Storage object that is called by native code when performing queries.
* SQL result set object
* PRIVATE METHOD
* @constructor
*/
var DroidDB = function() {
this.queryQueue = {};
var DroidDB_Rows = function() {
this.resultSet = []; // results array
this.length = 0; // number of rows
};
/**
* Callback from native code when result from a query is available.
* PRIVATE METHOD
* Get item from SQL result set
*
* @param rawdata JSON string of the row data
* @param id Query id
* @param row The row number to return
* @return The row object
*/
DroidDB.prototype.addResult = function(rawdata, id) {
try {
eval("var data = " + rawdata + ";");
var query = this.queryQueue[id];
query.resultSet.push(data);
} catch (e) {
console.log("DroidDB.addResult(): Error="+e);
}
DroidDB_Rows.prototype.item = function(row) {
return this.resultSet[row];
};
/**
* SQL result set that is returned to user.
* PRIVATE METHOD
* @constructor
*/
var DroidDB_Result = function() {
this.rows = new DroidDB_Rows();
};
/**
* Storage object that is called by native code when performing queries.
* PRIVATE METHOD
* @constructor
*/
var DroidDB = function() {
this.queryQueue = {};
};
/**
@@ -43,7 +59,7 @@ DroidDB.prototype.addResult = function(rawdata, id) {
*
* @param id Query id
*/
DroidDB.prototype.completeQuery = function(id) {
DroidDB.prototype.completeQuery = function(id, data) {
var query = this.queryQueue[id];
if (query) {
try {
@@ -59,10 +75,10 @@ DroidDB.prototype.completeQuery = function(id) {
// Save query results
var r = new DroidDB_Result();
r.rows.resultSet = query.resultSet;
r.rows.length = query.resultSet.length;
r.rows.resultSet = data;
r.rows.length = data.length;
try {
if (typeof query.successCallback == 'function') {
if (typeof query.successCallback === 'function') {
query.successCallback(query.tx, r);
}
} catch (ex) {
@@ -100,7 +116,7 @@ DroidDB.prototype.fail = function(reason, id) {
tx.queryList = {};
try {
if (typeof query.errorCallback == 'function') {
if (typeof query.errorCallback === 'function') {
query.errorCallback(query.tx, reason);
}
} catch (ex) {
@@ -116,38 +132,40 @@ DroidDB.prototype.fail = function(reason, id) {
}
};
var DatabaseShell = function() {
};
/**
* Start a transaction.
* Does not support rollback in event of failure.
* SQL query object
* PRIVATE METHOD
*
* @param process {Function} The transaction function
* @param successCallback {Function}
* @param errorCallback {Function}
* @constructor
* @param tx The transaction object that this query belongs to
*/
DatabaseShell.prototype.transaction = function(process, successCallback, errorCallback) {
var tx = new DroidDB_Tx();
tx.successCallback = successCallback;
tx.errorCallback = errorCallback;
try {
process(tx);
} catch (e) {
console.log("Transaction error: "+e);
if (tx.errorCallback) {
try {
tx.errorCallback(e);
} catch (ex) {
console.log("Transaction error calling user error callback: "+e);
}
}
}
var DroidDB_Query = function(tx) {
// Set the id of the query
this.id = PhoneGap.createUUID();
// Add this query to the queue
droiddb.queryQueue[this.id] = this;
// Init result
this.resultSet = [];
// Set transaction that this query belongs to
this.tx = tx;
// Add this query to transaction list
this.tx.queryList[this.id] = this;
// Callbacks
this.successCallback = null;
this.errorCallback = null;
};
/**
* Transaction object
* PRIVATE METHOD
* @constructor
*/
var DroidDB_Tx = function() {
@@ -174,10 +192,13 @@ DroidDB_Tx.prototype.queryComplete = function(id) {
// If no more outstanding queries, then fire transaction success
if (this.successCallback) {
var count = 0;
for (var i in this.queryList) {
count++;
var i;
for (i in this.queryList) {
if (this.queryList.hasOwnProperty(i)) {
count++;
}
}
if (count == 0) {
if (count === 0) {
try {
this.successCallback();
} catch(e) {
@@ -210,35 +231,6 @@ DroidDB_Tx.prototype.queryFailed = function(id, reason) {
}
};
/**
* SQL query object
* PRIVATE METHOD
*
* @param tx The transaction object that this query belongs to
*/
var DroidDB_Query = function(tx) {
// Set the id of the query
this.id = PhoneGap.createUUID();
// Add this query to the queue
droiddb.queryQueue[this.id] = this;
// Init result
this.resultSet = [];
// Set transaction that this query belongs to
this.tx = tx;
// Add this query to transaction list
this.tx.queryList[this.id] = this;
// Callbacks
this.successCallback = null;
this.errorCallback = null;
}
/**
* Execute SQL statement
*
@@ -250,7 +242,7 @@ var DroidDB_Query = function(tx) {
DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCallback) {
// Init params array
if (typeof params == 'undefined') {
if (typeof params === 'undefined') {
params = [];
}
@@ -266,31 +258,33 @@ DroidDB_Tx.prototype.executeSql = function(sql, params, successCallback, errorCa
PhoneGap.exec(null, null, "Storage", "executeSql", [sql, params, query.id]);
};
/**
* SQL result set that is returned to user.
* PRIVATE METHOD
*/
DroidDB_Result = function() {
this.rows = new DroidDB_Rows();
var DatabaseShell = function() {
};
/**
* SQL result set object
* PRIVATE METHOD
*/
DroidDB_Rows = function() {
this.resultSet = []; // results array
this.length = 0; // number of rows
};
/**
* Get item from SQL result set
* Start a transaction.
* Does not support rollback in event of failure.
*
* @param row The row number to return
* @return The row object
* @param process {Function} The transaction function
* @param successCallback {Function}
* @param errorCallback {Function}
*/
DroidDB_Rows.prototype.item = function(row) {
return this.resultSet[row];
DatabaseShell.prototype.transaction = function(process, errorCallback, successCallback) {
var tx = new DroidDB_Tx();
tx.successCallback = successCallback;
tx.errorCallback = errorCallback;
try {
process(tx);
} catch (e) {
console.log("Transaction error: "+e);
if (tx.errorCallback) {
try {
tx.errorCallback(e);
} catch (ex) {
console.log("Transaction error calling user error callback: "+e);
}
}
}
};
/**
@@ -302,15 +296,133 @@ DroidDB_Rows.prototype.item = function(row) {
* @param size Database size in bytes
* @return Database object
*/
DroidDB_openDatabase = function(name, version, display_name, size) {
var DroidDB_openDatabase = function(name, version, display_name, size) {
PhoneGap.exec(null, null, "Storage", "openDatabase", [name, version, display_name, size]);
var db = new DatabaseShell();
return db;
};
/**
* For browsers with no localStorage we emulate it with SQLite. Follows the w3c api.
* TODO: Do similar for sessionStorage.
*/
/**
* @constructor
*/
var CupcakeLocalStorage = function() {
try {
this.db = openDatabase('localStorage', '1.0', 'localStorage', 2621440);
var storage = {};
this.length = 0;
function setLength (length) {
this.length = length;
localStorage.length = length;
}
this.db.transaction(
function (transaction) {
var i;
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
transaction.executeSql('SELECT * FROM storage', [], function(tx, result) {
for(var i = 0; i < result.rows.length; i++) {
storage[result.rows.item(i)['id']] = result.rows.item(i)['body'];
}
setLength(result.rows.length);
PhoneGap.initializationComplete("cupcakeStorage");
});
},
function (err) {
alert(err.message);
}
);
this.setItem = function(key, val) {
if (typeof(storage[key])=='undefined') {
this.length++;
}
storage[key] = val;
this.db.transaction(
function (transaction) {
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
transaction.executeSql('REPLACE INTO storage (id, body) values(?,?)', [key,val]);
}
);
};
this.getItem = function(key) {
return storage[key];
};
this.removeItem = function(key) {
delete storage[key];
this.length--;
this.db.transaction(
function (transaction) {
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
transaction.executeSql('DELETE FROM storage where id=?', [key]);
}
);
};
this.clear = function() {
storage = {};
this.length = 0;
this.db.transaction(
function (transaction) {
transaction.executeSql('CREATE TABLE IF NOT EXISTS storage (id NVARCHAR(40) PRIMARY KEY, body NVARCHAR(255))');
transaction.executeSql('DELETE FROM storage', []);
}
);
};
this.key = function(index) {
var i = 0;
for (var j in storage) {
if (i==index) {
return j;
} else {
i++;
}
}
return null;
};
} catch(e) {
alert("Database error "+e+".");
return;
}
};
PhoneGap.addConstructor(function() {
if (typeof window.openDatabase == "undefined") {
var setupDroidDB = function() {
navigator.openDatabase = window.openDatabase = DroidDB_openDatabase;
window.droiddb = new DroidDB();
}
if (typeof window.openDatabase === "undefined") {
setupDroidDB();
} else {
window.openDatabase_orig = window.openDatabase;
window.openDatabase = function(name, version, desc, size){
// Some versions of Android will throw a SECURITY_ERR so we need
// to catch the exception and seutp our own DB handling.
var db = null;
try {
db = window.openDatabase_orig(name, version, desc, size);
}
catch (ex) {
db = null;
}
if (db == null) {
setupDroidDB();
return DroidDB_openDatabase(name, version, desc, size);
}
else {
return db;
}
}
}
if (typeof window.localStorage === "undefined") {
navigator.localStorage = window.localStorage = new CupcakeLocalStorage();
PhoneGap.waitForInitialization("cupcakeStorage");
}
});
}

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title></title>
<script src="phonegap-1.0.0rc3.js"></script>
</head>
<body>
</body>
</html>

96
framework/build.xml Normal file → Executable file
View File

@@ -1,5 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project name="PhoneGap" default="help">
<project name="PhoneGap" default="jar">
<!-- LOAD VERSION -->
<loadfile property="version" srcFile="../VERSION">
<filterchain>
<striplinebreaks/>
</filterchain>
</loadfile>
<!-- The local.properties file is created and updated by the 'android' tool.
It contains the path to the SDK. It should *NOT* be checked in in Version
@@ -33,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. -->
@@ -64,47 +74,67 @@
-->
<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.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>
<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. -->
<target name="build-javascript">
<delete file="assets/www/phonegap.js"/>
<concat destfile="assets/www/phonegap.js">
<!-- Clean up existing files -->
<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.min.js -->
<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"/>
</target>
<!-- Build PhoneGap jar file that includes all native code, and PhoneGap JS file
that includes all JavaScript code.
-->
<target name="jar" depends="build-javascript, compile">
<jar jarfile="phonegap.jar" basedir="bin/classes" excludes="**/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

@@ -10,5 +10,5 @@
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-8
target=android-12
apk-configurations=

View File

@@ -1,27 +0,0 @@
/* AUTO-GENERATED FILE. DO NOT MODIFY.
*
* This class was automatically generated by the
* aapt tool from the resource data it found. It
* should not be modified by hand.
*/
package com.phonegap;
public final class R {
public static final class attr {
}
public static final class drawable {
public static final int icon=0x7f020000;
public static final int splash=0x7f020001;
}
public static final class id {
public static final int appView=0x7f050000;
}
public static final class layout {
public static final int main=0x7f030000;
}
public static final class string {
public static final int app_name=0x7f040000;
public static final int go=0x7f040001;
}
}

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

@@ -13,6 +13,7 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
@@ -60,7 +61,7 @@ public class AccelListener extends Plugin implements SensorEventListener {
*
* @param ctx The context of the main Activity.
*/
public void setContext(DroidGap ctx) {
public void setContext(PhonegapActivity ctx) {
super.setContext(ctx);
this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE);
}

View File

@@ -0,0 +1,175 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap;
import org.json.JSONArray;
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.
*/
public class App extends Plugin {
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
try {
if (action.equals("clearCache")) {
this.clearCache();
}
else if (action.equals("loadUrl")) {
this.loadUrl(args.getString(0), args.optJSONObject(1));
}
else if (action.equals("cancelLoadUrl")) {
this.cancelLoadUrl();
}
else if (action.equals("clearHistory")) {
this.clearHistory();
}
else if (action.equals("overrideBackbutton")) {
this.overrideBackbutton(args.getBoolean(0));
}
else if (action.equals("isBackbuttonOverridden")) {
boolean b = this.isBackbuttonOverridden();
return new PluginResult(status, b);
}
else if (action.equals("exitApp")) {
this.exitApp();
}
return new PluginResult(status, result);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Clear the resource cache.
*/
public void clearCache() {
((DroidGap)this.ctx).clearCache();
}
/**
* Load the url into the webview.
*
* @param url
* @param props Properties that can be passed in to the DroidGap activity (i.e. loadingDialog, wait, ...)
* @throws JSONException
*/
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++) {
String key = keys.getString(i);
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)) {
params.put(key, (String)value);
}
else if (value.getClass().equals(Boolean.class)) {
params.put(key, (Boolean)value);
}
else if (value.getClass().equals(Integer.class)) {
params.put(key, (Integer)value);
}
}
}
}
// If wait property, then delay loading
if (wait > 0) {
try {
synchronized(this) {
this.wait(wait);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
((DroidGap)this.ctx).showWebPage(url, usePhoneGap, clearPrev, params);
}
/**
* Cancel loadUrl before it has been loaded.
*/
public void cancelLoadUrl() {
((DroidGap)this.ctx).cancelLoadUrl();
}
/**
* Clear web history in this web view.
*/
public void clearHistory() {
((DroidGap)this.ctx).clearHistory();
}
/**
* Override the default behavior of the Android back button.
* If overridden, when the back button is pressed, the "backKeyDown" JavaScript event will be fired.
*
* @param override T=override, F=cancel override
*/
public void overrideBackbutton(boolean override) {
System.out.println("WARNING: Back Button Default Behaviour will be overridden. The backbutton event will be fired!");
((DroidGap)this.ctx).bound = override;
}
/**
* Return whether the Android back button is overridden by the user.
*
* @return boolean
*/
public boolean isBackbuttonOverridden() {
return ((DroidGap)this.ctx).bound;
}
/**
* Exit the Android application.
*/
public void exitApp() {
((DroidGap)this.ctx).finish();
}
}

View File

@@ -20,7 +20,7 @@ import android.content.Context;
import android.media.AudioManager;
/**
* This class called by DroidGap to play and record audio.
* This class called by PhonegapActivity to play and record audio.
* The file can be local or over a network using http.
*
* Audio formats supported (tested):
@@ -63,6 +63,9 @@ public class AudioHandler extends Plugin {
else if (action.equals("startPlayingAudio")) {
this.startPlayingAudio(args.getString(0), args.getString(1));
}
else if (action.equals("seekToAudio")) {
this.seekToAudio(args.getString(0), args.getInt(1));
}
else if (action.equals("pausePlayingAudio")) {
this.pausePlayingAudio(args.getString(0));
}
@@ -70,12 +73,16 @@ public class AudioHandler extends Plugin {
this.stopPlayingAudio(args.getString(0));
}
else if (action.equals("getCurrentPositionAudio")) {
long l = this.getCurrentPositionAudio(args.getString(0));
return new PluginResult(status, l);
float f = this.getCurrentPositionAudio(args.getString(0));
return new PluginResult(status, f);
}
else if (action.equals("getDurationAudio")) {
long l = this.getDurationAudio(args.getString(0), args.getString(1));
return new PluginResult(status, l);
float f = this.getDurationAudio(args.getString(0), args.getString(1));
return new PluginResult(status, f);
}
else if (action.equals("release")) {
boolean b = this.release(args.getString(0));
return new PluginResult(status, b);
}
return new PluginResult(status, result);
} catch (JSONException e) {
@@ -83,7 +90,7 @@ public class AudioHandler extends Plugin {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
/**
* Identifies if action to be executed returns a value and should be run synchronously.
*
@@ -117,6 +124,21 @@ public class AudioHandler extends Plugin {
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Release the audio player instance to save memory.
*
* @param id The id of the audio player
*/
private boolean release(String id) {
if (!this.players.containsKey(id)) {
return false;
}
AudioPlayer audio = this.players.get(id);
this.players.remove(id);
audio.destroy();
return true;
}
/**
* Start recording and save the specified file.
@@ -162,6 +184,20 @@ public class AudioHandler extends Plugin {
audio.startPlaying(file);
}
/**
* Seek to a location.
*
*
* @param id The id of the audio player
* @param miliseconds int: number of milliseconds to skip 1000 = 1 second
*/
public void seekToAudio(String id, int milliseconds) {
AudioPlayer audio = this.players.get(id);
if (audio != null) {
audio.seekToPlaying(milliseconds);
}
}
/**
* Pause playing.
*
@@ -194,10 +230,10 @@ public class AudioHandler extends Plugin {
* @param id The id of the audio player
* @return position in msec
*/
public long getCurrentPositionAudio(String id) {
public float getCurrentPositionAudio(String id) {
AudioPlayer audio = this.players.get(id);
if (audio != null) {
return(audio.getCurrentPosition());
return(audio.getCurrentPosition()/1000.0f);
}
return -1;
}
@@ -209,7 +245,7 @@ public class AudioHandler extends Plugin {
* @param file The name of the audio file.
* @return The duration in msec.
*/
public long getDurationAudio(String id, String file) {
public float getDurationAudio(String id, String file) {
// Get audio file
AudioPlayer audio = this.players.get(id);

View File

@@ -11,10 +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.
@@ -27,7 +29,9 @@ import android.media.MediaPlayer.OnPreparedListener;
*/
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;
@@ -37,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
@@ -53,7 +58,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
private String id; // The id of this player (used to identify Media object in JavaScript)
private int state = MEDIA_NONE; // State of recording or playback
private String audioFile = null; // File name to play or record to
private long duration = -1; // Duration of audio
private float duration = -1; // Duration of audio
private MediaRecorder recorder = null; // Audio recording object
private String tempFile = null; // Temporary recording file name
@@ -70,10 +75,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
public AudioPlayer(AudioHandler handler, String id) {
this.handler = handler;
this.id = id;
// YES, I know this is bad, but I can't do it the right way because Google didn't have the
// foresight to add android.os.environment.getExternalDataDirectory until Android 2.2
this.tempFile = "/sdcard/tmprecording.mp3";
this.tempFile = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording.mp3";
}
/**
@@ -101,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+");");
}
@@ -126,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+");");
}
}
@@ -168,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+");");
}
@@ -209,7 +211,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.mPlayer.prepare();
// Get duration
this.duration = this.mPlayer.getDuration();
this.duration = getDurationInSeconds();
}
}
catch (Exception e) {
@@ -227,12 +229,23 @@ 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+");");
}
}
}
/**
* Seek or jump to a new time in the track.
*/
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+");");
}
}
/**
* Pause playing.
*/
@@ -244,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+");");
}
}
@@ -258,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+");");
}
}
@@ -279,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;
@@ -310,7 +325,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
* -1=can't be determined
* -2=not allowed
*/
public long getDuration(String file) {
public float getDuration(String file) {
// Can't get duration of recording
if (this.recorder != null) {
@@ -353,7 +368,7 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
}
// Save off duration
this.duration = this.mPlayer.getDuration();
this.duration = getDurationInSeconds();
this.prepareOnly = false;
// Send status notification to JavaScript
@@ -361,6 +376,15 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
}
/**
* By default Android returns the length of audio in mills but we want seconds
*
* @return length of clip in seconds
*/
private float getDurationInSeconds() {
return (this.mPlayer.getDuration() / 1000.0f);
}
/**
* Callback to be invoked when there has been an error during an asynchronous operation
* (other errors will throw exceptions at method call time).
@@ -370,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();
@@ -393,5 +417,4 @@ public class AudioPlayer implements OnCompletionListener, OnPreparedListener, On
this.state = state;
}
}

View File

@@ -1,51 +0,0 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
*/
package com.phonegap;
import android.app.Activity;
import android.util.Log;
import android.webkit.WebView;
/*
* This class literally exists to protect DroidGap from Javascript directly.
*
*
*/
public class BrowserKey {
DroidGap mAction;
boolean bound;
WebView mView;
BrowserKey(WebView view, DroidGap action)
{
bound = false;
mAction = action;
}
public void override()
{
Log.d("PhoneGap", "WARNING: Back Button Default Behaviour will be overridden. The backKeyDown event will be fired!");
bound = true;
}
public boolean isBound()
{
return bound;
}
public void reset()
{
bound = false;
}
public void exitApp()
{
mAction.finish();
}
}

View File

@@ -11,10 +11,14 @@ import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URLEncoder;
import java.util.LinkedList;
import android.util.Log;
/**
* This class provides a way for Java to run JavaScript in the web page that has loaded PhoneGap.
* The CallbackServer class implements an XHR server and a polling server with a list of JavaScript
@@ -41,6 +45,8 @@ import java.util.LinkedList;
*/
public class CallbackServer implements Runnable {
private static final String LOG_TAG = "CallbackServer";
/**
* The list of JavaScript statements to be sent to JavaScript.
*/
@@ -69,7 +75,7 @@ public class CallbackServer implements Runnable {
/**
* Indicates that polling should be used instead of XHR.
*/
private boolean usePolling;
private boolean usePolling = true;
/**
* Security token to prevent other apps from accessing this callback server via XHR
@@ -84,10 +90,28 @@ public class CallbackServer implements Runnable {
this.active = false;
this.empty = true;
this.port = 0;
this.javascript = new LinkedList<String>();
if (android.net.Proxy.getDefaultHost() != null) {
this.javascript = new LinkedList<String>();
}
/**
* Init callback server and start XHR if running local app.
*
* If PhoneGap app is loaded from file://, then we can use XHR
* otherwise we have to use polling due to cross-domain security restrictions.
*
* @param url The URL of the PhoneGap app being loaded
*/
public void init(String url) {
//System.out.println("CallbackServer.start("+url+")");
// Determine if XHR or polling is to be used
if ((url != null) && !url.startsWith("file://")) {
this.usePolling = true;
this.stopServer();
}
else if (android.net.Proxy.getDefaultHost() != null) {
this.usePolling = true;
this.stopServer();
}
else {
this.usePolling = false;
@@ -96,7 +120,7 @@ public class CallbackServer implements Runnable {
}
/**
* Determine if polling should be used instead of XHR.
* Return if polling is being used instead of XHR.
*
* @return
*/
@@ -158,9 +182,9 @@ public class CallbackServer implements Runnable {
String request;
ServerSocket waitSocket = new ServerSocket(0);
this.port = waitSocket.getLocalPort();
//System.out.println(" -- using port " +this.port);
//System.out.println("CallbackServer -- using port " +this.port);
this.token = java.util.UUID.randomUUID().toString();
//System.out.println(" -- using token "+this.token);
//System.out.println("CallbackServer -- using token "+this.token);
while (this.active) {
//System.out.println("CallbackServer: Waiting for data on socket");
@@ -168,43 +192,66 @@ public class CallbackServer implements Runnable {
BufferedReader xhrReader = new BufferedReader(new InputStreamReader(connection.getInputStream()),40);
DataOutputStream output = new DataOutputStream(connection.getOutputStream());
request = xhrReader.readLine();
//System.out.println("Request="+request);
if (request.contains("GET")) {
String response = "";
//System.out.println("CallbackServerRequest="+request);
if (this.active && (request != null)) {
if (request.contains("GET")) {
// Get requested file
String[] requestParts = request.split(" ");
// Must have security token
if ((requestParts.length == 3) && (requestParts[1].substring(1).equals(this.token))) {
//System.out.println("CallbackServer -- Processing GET request");
// Must have security token
if (request.substring(5,41).equals(this.token)) {
//System.out.println(" -- Processing GET request");
// Wait until there is some data to send, or send empty data every 30 sec
// to prevent XHR timeout on the client
synchronized (this) {
while (this.empty) {
try {
this.wait(30000); // prevent timeout from happening
//System.out.println(">>> break <<<");
break;
// Wait until there is some data to send, or send empty data every 10 sec
// to prevent XHR timeout on the client
synchronized (this) {
while (this.empty) {
try {
this.wait(10000); // prevent timeout from happening
//System.out.println("CallbackServer>>> break <<<");
break;
}
catch (Exception e) { }
}
catch (Exception e) { }
}
}
}
// If server is still running
if (this.active) {
// If server is still running
if (this.active) {
// If no data, then send 404 back to client before it times out
if (this.empty) {
//System.out.println(" -- sending data 0");
output.writeBytes("HTTP/1.1 404 NO DATA\r\n\r\n");
// If no data, then send 404 back to client before it times out
if (this.empty) {
//System.out.println("CallbackServer -- sending data 0");
response = "HTTP/1.1 404 NO DATA\r\n\r\n "; // need to send content otherwise some Android devices fail, so send space
}
else {
//System.out.println("CallbackServer -- sending item");
response = "HTTP/1.1 200 OK\r\n\r\n";
String js = this.getJavascript();
if (js != null) {
response += encode(js, "UTF-8");
}
}
}
else {
//System.out.println(" -- sending item");
output.writeBytes("HTTP/1.1 200 OK\r\n\r\n"+this.getJavascript());
response = "HTTP/1.1 503 Service Unavailable\r\n\r\n ";
}
}
else {
response = "HTTP/1.1 403 Forbidden\r\n\r\n ";
}
}
else {
response = "HTTP/1.1 400 Bad Request\r\n\r\n ";
}
//System.out.println("CallbackServer: response="+response);
//System.out.println("CallbackServer: closing output");
output.writeBytes(response);
output.flush();
}
//System.out.println("CallbackServer: closing output");
output.close();
output.close();
xhrReader.close();
}
} catch (IOException e) {
e.printStackTrace();
@@ -219,11 +266,13 @@ public class CallbackServer implements Runnable {
*/
public void stopServer() {
//System.out.println("CallbackServer.stopServer()");
this.active = false;
if (this.active) {
this.active = false;
// Break out of server wait
synchronized (this) {
this.notify();
// Break out of server wait
synchronized (this) {
this.notify();
}
}
}
@@ -278,4 +327,81 @@ public class CallbackServer implements Runnable {
}
}
/* The Following code has been modified from original implementation of URLEncoder */
/* start */
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
static final String digits = "0123456789ABCDEF";
/**
* This will encode the return value to JavaScript. We revert the encoding for
* common characters that don't require encoding to reduce the size of the string
* being passed to JavaScript.
*
* @param s to be encoded
* @param enc encoding type
* @return encoded string
*/
public static String encode(String s, String enc) throws UnsupportedEncodingException {
if (s == null || enc == null) {
throw new NullPointerException();
}
// check for UnsupportedEncodingException
"".getBytes(enc);
// Guess a bit bigger for encoded form
StringBuilder buf = new StringBuilder(s.length() + 16);
int start = -1;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
|| (ch >= '0' && ch <= '9')
|| " .-*_'(),<>=?@[]{}:~\"\\/;!".indexOf(ch) > -1) {
if (start >= 0) {
convert(s.substring(start, i), buf, enc);
start = -1;
}
if (ch != ' ') {
buf.append(ch);
} else {
buf.append(' ');
}
} else {
if (start < 0) {
start = i;
}
}
}
if (start >= 0) {
convert(s.substring(start, s.length()), buf, enc);
}
return buf.toString();
}
private static void convert(String s, StringBuilder buf, String enc) throws UnsupportedEncodingException {
byte[] bytes = s.getBytes(enc);
for (int j = 0; j < bytes.length; j++) {
buf.append('%');
buf.append(digits.charAt((bytes[j] & 0xf0) >> 4));
buf.append(digits.charAt(bytes[j] & 0xf));
}
}
/* end */
}

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,11 +80,21 @@ 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(srcType, destType);
this.getImage(args.getInt(0), srcType, destType);
}
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
r.setKeepCallback(true);
@@ -100,7 +114,7 @@ public class CameraLauncher extends Plugin {
/**
* Take a picture with the camera.
* When an image is captured or the camera view is cancelled, the result is returned
* in DroidGap.onActivityResult, which forwards the result to this.onActivityResult.
* in PhonegapActivity.onActivityResult, which forwards the result to this.onActivityResult.
*
* The image can either be returned as a base64 string or a URI that points to the file.
* To display base64 string in an img tag, set the source to:
@@ -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,20 +133,40 @@ 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 returnType
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
* @param srcType The album to get image from.
* @param returnType Set the type of image to return.
*/
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
public void getImage(int srcType, int returnType) {
public void getImage(int quality, int srcType, int returnType) {
this.mQuality = quality;
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
@@ -141,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.
*
@@ -157,13 +235,21 @@ public class CameraLauncher extends Plugin {
// If CAMERA
if (srcType == CAMERA) {
// If image available
if (resultCode == Activity.RESULT_OK) {
try {
// Read in bitmap of captured image
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
Bitmap bitmap;
try {
bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
} catch (FileNotFoundException e) {
Uri uri = intent.getData();
android.content.ContentResolver resolver = this.ctx.getContentResolver();
bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
}
bitmap = scaleBitmap(bitmap);
// If sending base64 image back
if (destType == DATA_URL) {
this.processPicture(bitmap);
@@ -197,6 +283,9 @@ public class CameraLauncher extends Plugin {
// Send Uri back to JavaScript for viewing image
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
}
bitmap.recycle();
bitmap = null;
System.gc();
} catch (IOException e) {
e.printStackTrace();
this.failPicture("Error capturing image.");
@@ -223,7 +312,11 @@ 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;
System.gc();
} catch (FileNotFoundException e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
@@ -257,11 +350,15 @@ public class CameraLauncher extends Plugin {
byte[] output = Base64.encodeBase64(code);
String js_out = new String(output);
this.success(new PluginResult(PluginResult.Status.OK, js_out), this.callbackId);
js_out = null;
output = null;
code = null;
}
}
catch(Exception e) {
this.failPicture("Error compressing image.");
}
jpeg_data = null;
}
/**

View File

@@ -0,0 +1,359 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2011, IBM Corporation
*/
package com.phonegap;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Environment;
import android.util.Log;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
public class Capture extends Plugin {
private static final String _DATA = "_data"; // The column name where the file path is stored
private static final int CAPTURE_AUDIO = 0; // Constant for capture audio
private static final int CAPTURE_IMAGE = 1; // Constant for capture image
private static final int CAPTURE_VIDEO = 2; // Constant for capture video
private static final String LOG_TAG = "Capture";
private String callbackId; // The ID of the callback to be invoked with our result
private long limit; // the number of pics/vids/clips to take
private double duration; // optional duration parameter for video recording
private JSONArray results; // The array of results to be returned to the user
private Uri imageUri; // Uri of captured image
@Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
this.callbackId = callbackId;
this.limit = 1;
this.duration = 0.0f;
this.results = new JSONArray();
JSONObject options = args.optJSONObject(0);
if (options != null) {
limit = options.optLong("limit", 1);
duration = options.optDouble("duration", 0.0f);
}
if (action.equals("getFormatData")) {
try {
JSONObject obj = getFormatData(args.getString(0), args.getString(1));
return new PluginResult(PluginResult.Status.OK, obj);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.ERROR);
}
}
else if (action.equals("captureAudio")) {
this.captureAudio();
}
else if (action.equals("captureImage")) {
this.captureImage();
}
else if (action.equals("captureVideo")) {
this.captureVideo(duration);
}
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
r.setKeepCallback(true);
return r;
}
/**
* Provides the media data file data depending on it's mime type
*
* @param filePath path to the file
* @param mimeType of the file
* @return a MediaFileData object
*/
private JSONObject getFormatData(String filePath, String mimeType) {
JSONObject obj = new JSONObject();
try {
// setup defaults
obj.put("height", 0);
obj.put("width", 0);
obj.put("bitrate", 0);
obj.put("duration", 0);
obj.put("codecs", "");
// If the mimeType isn't set the rest will fail
// so let's see if we can determine it.
if (mimeType == null || mimeType.equals("")) {
mimeType = FileUtils.getMimeType(filePath);
}
if (mimeType.equals("image/jpeg") || filePath.endsWith(".jpg")) {
obj = getImageData(filePath, obj);
}
else if (filePath.endsWith("audio/3gpp")) {
obj = getAudioVideoData(filePath, obj, false);
}
else if (mimeType.equals("video/3gpp")) {
obj = getAudioVideoData(filePath, obj, true);
}
}
catch (JSONException e) {
Log.d(LOG_TAG, "Error: setting media file data object");
}
return obj;
}
/**
* Get the Image specific attributes
*
* @param filePath path to the file
* @param obj represents the Media File Data
* @return a JSONObject that represents the Media File Data
* @throws JSONException
*/
private JSONObject getImageData(String filePath, JSONObject obj) throws JSONException {
Bitmap bitmap = BitmapFactory.decodeFile(filePath);
obj.put("height", bitmap.getHeight());
obj.put("width", bitmap.getWidth());
return obj;
}
/**
* Get the Image specific attributes
*
* @param filePath path to the file
* @param obj represents the Media File Data
* @param video if true get video attributes as well
* @return a JSONObject that represents the Media File Data
* @throws JSONException
*/
private JSONObject getAudioVideoData(String filePath, JSONObject obj, boolean video) throws JSONException {
MediaPlayer player = new MediaPlayer();
try {
player.setDataSource(filePath);
player.prepare();
obj.put("duration", player.getDuration());
if (video) {
obj.put("height", player.getVideoHeight());
obj.put("width", player.getVideoWidth());
}
}
catch (IOException e) {
Log.d(LOG_TAG, "Error: loading video file");
}
return obj;
}
/**
* Sets up an intent to capture audio. Result handled by onActivityResult()
*/
private void captureAudio() {
Intent intent = new Intent(android.provider.MediaStore.Audio.Media.RECORD_SOUND_ACTION);
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_AUDIO);
}
/**
* Sets up an intent to capture images. Result handled by onActivityResult()
*/
private void captureImage() {
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// Specify file so that large image is captured and returned
File photo = new File(DirectoryManager.getTempDirectoryPath(ctx), "Capture.jpg");
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
this.imageUri = Uri.fromFile(photo);
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_IMAGE);
}
/**
* Sets up an intent to capture video. Result handled by onActivityResult()
*/
private void captureVideo(double duration) {
Intent intent = new Intent(android.provider.MediaStore.ACTION_VIDEO_CAPTURE);
// Introduced in API 8
//intent.putExtra(android.provider.MediaStore.EXTRA_DURATION_LIMIT, duration);
this.ctx.startActivityForResult((Plugin) this, intent, CAPTURE_VIDEO);
}
/**
* Called when the video view exits.
*
* @param requestCode The request code originally supplied to startActivityForResult(),
* allowing you to identify who this result came from.
* @param resultCode The integer result code returned by the child activity through its setResult().
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
* @throws JSONException
*/
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
// Result received okay
if (resultCode == Activity.RESULT_OK) {
// An audio clip was requested
if (requestCode == CAPTURE_AUDIO) {
// Get the uri of the audio clip
Uri data = intent.getData();
// create a file object from the uri
results.put(createMediaFile(data));
if (results.length() >= limit) {
// Send Uri back to JavaScript for listening to audio
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
} else {
// still need to capture more audio clips
captureAudio();
}
} else if (requestCode == CAPTURE_IMAGE) {
// For some reason if I try to do:
// Uri data = intent.getData();
// It crashes in the emulator and on my phone with a null pointer exception
// To work around it I had to grab the code from CameraLauncher.java
try {
// Read in bitmap of captured image
Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getContentResolver(), imageUri);
// Create entry in media store for image
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
ContentValues values = new ContentValues();
values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
Uri uri = null;
try {
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} catch (UnsupportedOperationException e) {
System.out.println("Can't write to external media storage.");
try {
uri = this.ctx.getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
} catch (UnsupportedOperationException ex) {
System.out.println("Can't write to internal media storage.");
this.fail("Error capturing image - no media storage found.");
return;
}
}
// Add compressed version of captured image to returned media store Uri
OutputStream os = this.ctx.getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
bitmap.recycle();
bitmap = null;
System.gc();
// Add image to results
results.put(createMediaFile(uri));
if (results.length() >= limit) {
// Send Uri back to JavaScript for viewing image
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
} else {
// still need to capture more images
captureImage();
}
} catch (IOException e) {
e.printStackTrace();
this.fail("Error capturing image.");
}
} else if (requestCode == CAPTURE_VIDEO) {
// Get the uri of the video clip
Uri data = intent.getData();
// create a file object from the uri
results.put(createMediaFile(data));
if (results.length() >= limit) {
// Send Uri back to JavaScript for viewing video
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
} else {
// still need to capture more video clips
captureVideo(duration);
}
}
}
// If canceled
else if (resultCode == Activity.RESULT_CANCELED) {
// If we have partial results send them back to the user
if (results.length() > 0) {
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
}
// user canceled the action
else {
this.fail("Canceled.");
}
}
// If something else
else {
// If we have partial results send them back to the user
if (results.length() > 0) {
this.success(new PluginResult(PluginResult.Status.OK, results, "navigator.device.capture._castMediaFile"), this.callbackId);
}
// something bad happened
else {
this.fail("Did not complete!");
}
}
}
/**
* Creates a JSONObject that represents a File from the Uri
*
* @param data the Uri of the audio/image/video
* @return a JSONObject that represents a File
*/
private JSONObject createMediaFile(Uri data) {
File fp = new File(getRealPathFromURI(data));
JSONObject obj = new JSONObject();
try {
// File properties
obj.put("name", fp.getName());
obj.put("fullPath", fp.getAbsolutePath());
obj.put("type", FileUtils.getMimeType(fp.getAbsolutePath()));
obj.put("lastModifiedDate", fp.lastModified());
obj.put("size", fp.length());
} catch (JSONException e) {
// this will never happen
e.printStackTrace();
}
return obj;
}
/**
* Queries the media store to find out what the file path is for the Uri we supply
*
* @param contentUri the Uri of the audio/image/video
* @return the full path to the file
*/
private String getRealPathFromURI(Uri contentUri) {
String[] proj = { _DATA };
Cursor cursor = this.ctx.managedQuery(contentUri, proj, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(_DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
}
/**
* Send error message to JavaScript.
*
* @param err
*/
public void fail(String err) {
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
}
}

View File

@@ -12,6 +12,7 @@ import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
@@ -55,7 +56,7 @@ public class CompassListener extends Plugin implements SensorEventListener {
*
* @param ctx The context of the main Activity.
*/
public void setContext(DroidGap ctx) {
public void setContext(PhonegapActivity ctx) {
super.setContext(ctx);
this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE);
}

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,65 +69,115 @@ 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);
}
}
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);
}
return map;
}
/**
* Convenience method to get a string from a JSON object. Saves a
* lot of try/catch writing.
* If the property is not found in the object null will be returned.
*
* @param obj contact object to search
* @param property to be looked up
* @return The value of the property
*/
protected String getJsonString(JSONObject obj, String property) {
String value = null;
try {
if (obj != null) {
value = obj.getString(property);
if (value.equals("null")) {
Log.d(LOG_TAG, property + " is string called 'null'");
value = null;
}
}
}
catch (JSONException e) {
Log.d(LOG_TAG, "Could not get = " + e.getMessage());
}
return value;
}
/**
* Handles adding a JSON Contact object into the database.
* @return TODO
*/
public abstract void 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,455 +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.database.Cursor;
import android.net.Uri;
import android.provider.Contacts;
import android.provider.Contacts.ContactMethods;
import android.provider.Contacts.ContactMethodsColumns;
import android.provider.Contacts.Organizations;
import android.provider.Contacts.People;
import android.provider.Contacts.Phones;
import android.util.Log;
import android.webkit.WebView;
/**
* An implementation of {@link ContactAccessor} that uses legacy Contacts API.
* These APIs are deprecated and should not be used unless we are running on a
* pre-Eclair SDK.
* <p>
* There are several reasons why we wouldn't want to use this class on an Eclair device:
* <ul>
* <li>It would see at most one account, namely the first Google account created on the device.
* <li>It would work through a compatibility layer, which would make it inherently less efficient.
* <li>Not relevant to this particular example, but it would not have access to new kinds
* of data available through current APIs.
* </ul>
*/
@SuppressWarnings("deprecation")
public class ContactAccessorSdk3_4 extends ContactAccessor {
/**
* A static map that converts the JavaScript property name to Android database column name.
*/
private static final Map<String, String> dbMap = new HashMap<String, String>();
static {
dbMap.put("id", People._ID);
dbMap.put("displayName", People.DISPLAY_NAME);
dbMap.put("phoneNumbers", Phones.NUMBER);
dbMap.put("phoneNumbers.value", Phones.NUMBER);
dbMap.put("emails", ContactMethods.DATA);
dbMap.put("emails.value", ContactMethods.DATA);
dbMap.put("addresses", ContactMethodsColumns.DATA);
dbMap.put("addresses.formatted", ContactMethodsColumns.DATA);
dbMap.put("ims", ContactMethodsColumns.DATA);
dbMap.put("ims.value", ContactMethodsColumns.DATA);
dbMap.put("organizations", Organizations.COMPANY);
dbMap.put("organizations.name", Organizations.COMPANY);
dbMap.put("organizations.title", Organizations.TITLE);
dbMap.put("note", People.NOTES);
}
/**
* Create an contact accessor.
*/
public ContactAccessorSdk3_4(WebView view, Activity app)
{
mApp = app;
mView = view;
}
@Override
/**
* This method takes the fields required and search options in order to produce an
* array of contacts that matches the criteria provided.
* @param fields an array of items to be used as search criteria
* @param options that can be applied to contact searching
* @return an array of contacts
*/
public JSONArray search(JSONArray fields, JSONObject options) {
String searchTerm = "";
int limit = 1;
boolean multiple = false;
try {
searchTerm = options.getString("filter");
if (searchTerm.length()==0) {
searchTerm = "%";
}
else {
searchTerm = "%" + searchTerm + "%";
}
multiple = options.getBoolean("multiple");
if (multiple) {
limit = options.getInt("limit");
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
ContentResolver cr = mApp.getContentResolver();
Set<String> contactIds = buildSetOfContactIds(fields, searchTerm);
HashMap<String,Boolean> populate = buildPopulationSet(fields);
Iterator<String> it = contactIds.iterator();
JSONArray contacts = new JSONArray();
JSONObject contact;
String contactId;
int pos = 0;
while (it.hasNext() && (pos < limit)) {
contact = new JSONObject();
try {
contactId = it.next();
contact.put("id", contactId);
// Do query for name and note
Cursor cur = cr.query(People.CONTENT_URI,
new String[] {People.DISPLAY_NAME, People.NOTES},
"people._id = ?",
new String[] {contactId},
null);
cur.moveToFirst();
if (isRequired("displayName",populate)) {
contact.put("displayName", cur.getString(cur.getColumnIndex(People.DISPLAY_NAME)));
}
if (isRequired("phoneNumbers",populate)) {
contact.put("phoneNumbers", phoneQuery(cr, contactId));
}
if (isRequired("emails",populate)) {
contact.put("emails", emailQuery(cr, contactId));
}
if (isRequired("addresses",populate)) {
contact.put("addresses", addressQuery(cr, contactId));
}
if (isRequired("organizations",populate)) {
contact.put("organizations", organizationQuery(cr, contactId));
}
if (isRequired("ims",populate)) {
contact.put("ims", imQuery(cr, contactId));
}
if (isRequired("note",populate)) {
contact.put("note", cur.getString(cur.getColumnIndex(People.NOTES)));
}
// nickname
// urls
// relationship
// birthdays
// anniversary
pos++;
cur.close();
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
contacts.put(contact);
}
return contacts;
}
/**
* Query the database using the search term to build up a list of contact ID's
* matching the search term
* @param fields
* @param searchTerm
* @return a set of contact ID's
*/
private Set<String> buildSetOfContactIds(JSONArray fields, String searchTerm) {
Set<String> contactIds = new HashSet<String>();
String key;
try {
for (int i=0; i<fields.length(); i++) {
key = fields.getString(i);
if (key.startsWith("displayName")) {
doQuery(searchTerm, contactIds,
People.CONTENT_URI,
People._ID,
dbMap.get(key) + " LIKE ?",
new String[] {searchTerm});
}
// else if (key.startsWith("name")) {
// Log.d(LOG_TAG, "Doing " + key + " query");
// doQuery(searchTerm, contactIds,
// ContactsContract.Data.CONTENT_URI,
// ContactsContract.Data.CONTACT_ID,
// dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?",
// new String[] {searchTerm, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE});
// }
else if (key.startsWith("phoneNumbers")) {
doQuery(searchTerm, contactIds,
Phones.CONTENT_URI,
Phones.PERSON_ID,
dbMap.get(key) + " LIKE ?",
new String[] {searchTerm});
}
else if (key.startsWith("emails")) {
doQuery(searchTerm, contactIds,
ContactMethods.CONTENT_EMAIL_URI,
ContactMethods.PERSON_ID,
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
new String[] {searchTerm, ContactMethods.CONTENT_EMAIL_ITEM_TYPE});
}
else if (key.startsWith("addresses")) {
doQuery(searchTerm, contactIds,
ContactMethods.CONTENT_URI,
ContactMethods.PERSON_ID,
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
new String[] {searchTerm, ContactMethods.CONTENT_POSTAL_ITEM_TYPE});
}
else if (key.startsWith("ims")) {
doQuery(searchTerm, contactIds,
ContactMethods.CONTENT_URI,
ContactMethods.PERSON_ID,
dbMap.get(key) + " LIKE ? AND " + ContactMethods.KIND + " = ?",
new String[] {searchTerm, ContactMethods.CONTENT_IM_ITEM_TYPE});
}
else if (key.startsWith("organizations")) {
doQuery(searchTerm, contactIds,
Organizations.CONTENT_URI,
ContactMethods.PERSON_ID,
dbMap.get(key) + " LIKE ?",
new String[] {searchTerm});
}
else if (key.startsWith("note")) {
doQuery(searchTerm, contactIds,
People.CONTENT_URI,
People._ID,
dbMap.get(key) + " LIKE ?",
new String[] {searchTerm});
}
}
}
catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return contactIds;
}
/**
* A convenience method so we don't duplicate code in doQuery
* @param searchTerm
* @param contactIds
* @param uri
* @param projection
* @param selection
* @param selectionArgs
*/
private void doQuery(String searchTerm, Set<String> contactIds,
Uri uri, String projection, String selection, String[] selectionArgs) {
ContentResolver cr = mApp.getContentResolver();
Cursor cursor = cr.query(
uri,
null,
selection,
selectionArgs,
null);
while (cursor.moveToNext()) {
contactIds.add(cursor.getString(cursor.getColumnIndex(projection)));
}
cursor.close();
}
/**
* Create a ContactField JSONArray
* @param cr database access object
* @param contactId the ID to search the database for
* @return a JSONArray representing a set of ContactFields
*/
private JSONArray imQuery(ContentResolver cr, String contactId) {
String imWhere = ContactMethods.PERSON_ID
+ " = ? AND " + ContactMethods.KIND + " = ?";
String[] imWhereParams = new String[]{contactId, ContactMethods.CONTENT_IM_ITEM_TYPE};
Cursor cursor = cr.query(ContactMethods.CONTENT_URI,
null, imWhere, imWhereParams, null);
JSONArray ims = new JSONArray();
JSONObject im;
while (cursor.moveToNext()) {
im = new JSONObject();
try{
im.put("primary", false);
im.put("value", cursor.getString(
cursor.getColumnIndex(ContactMethodsColumns.DATA)));
im.put("type", cursor.getString(
cursor.getColumnIndex(ContactMethodsColumns.TYPE)));
ims.put(im);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
}
cursor.close();
return null;
}
/**
* Create a ContactOrganization JSONArray
* @param cr database access object
* @param contactId the ID to search the database for
* @return a JSONArray representing a set of ContactOrganization
*/
private JSONArray organizationQuery(ContentResolver cr, String contactId) {
String orgWhere = ContactMethods.PERSON_ID + " = ?";
String[] orgWhereParams = new String[]{contactId};
Cursor cursor = cr.query(Organizations.CONTENT_URI,
null, orgWhere, orgWhereParams, null);
JSONArray organizations = new JSONArray();
JSONObject organization;
while (cursor.moveToNext()) {
organization = new JSONObject();
try{
organization.put("name", cursor.getString(cursor.getColumnIndex(Organizations.COMPANY)));
organization.put("title", cursor.getString(cursor.getColumnIndex(Organizations.TITLE)));
// organization.put("department", cursor.getString(cursor.getColumnIndex(Organizations)));
// organization.put("description", cursor.getString(cursor.getColumnIndex(Organizations)));
// organization.put("endDate", cursor.getString(cursor.getColumnIndex(Organizations)));
// organization.put("location", cursor.getString(cursor.getColumnIndex(Organizations)));
// organization.put("startDate", cursor.getString(cursor.getColumnIndex(Organizations)));
organizations.put(organization);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
}
return organizations;
}
/**
* Create a ContactAddress JSONArray
* @param cr database access object
* @param contactId the ID to search the database for
* @return a JSONArray representing a set of ContactAddress
*/
private JSONArray addressQuery(ContentResolver cr, String contactId) {
String addrWhere = ContactMethods.PERSON_ID
+ " = ? AND " + ContactMethods.KIND + " = ?";
String[] addrWhereParams = new String[]{contactId,
ContactMethods.CONTENT_POSTAL_ITEM_TYPE};
Cursor cursor = cr.query(ContactMethods.CONTENT_URI,
null, addrWhere, addrWhereParams, null);
JSONArray addresses = new JSONArray();
JSONObject address;
while (cursor.moveToNext()) {
address = new JSONObject();
try{
address.put("formatted", cursor.getString(cursor.getColumnIndex(ContactMethodsColumns.DATA)));
addresses.put(address);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
}
return addresses;
}
/**
* Create a ContactField JSONArray
* @param cr database access object
* @param contactId the ID to search the database for
* @return a JSONArray representing a set of ContactFields
*/
private JSONArray phoneQuery(ContentResolver cr, String contactId) {
Cursor cursor = cr.query(
Phones.CONTENT_URI,
null,
Phones.PERSON_ID +" = ?",
new String[]{contactId}, null);
JSONArray phones = new JSONArray();
JSONObject phone;
while (cursor.moveToNext()) {
phone = new JSONObject();
try{
phone.put("primary", false);
phone.put("value", cursor.getString(cursor.getColumnIndex(Phones.NUMBER)));
phone.put("type", cursor.getString(cursor.getColumnIndex(Phones.TYPE)));
phones.put(phone);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
}
return phones;
}
/**
* Create a ContactField JSONArray
* @param cr database access object
* @param contactId the ID to search the database for
* @return a JSONArray representing a set of ContactFields
*/
private JSONArray emailQuery(ContentResolver cr, String contactId) {
Cursor cursor = cr.query(
ContactMethods.CONTENT_EMAIL_URI,
null,
ContactMethods.PERSON_ID +" = ?",
new String[]{contactId}, null);
JSONArray emails = new JSONArray();
JSONObject email;
while (cursor.moveToNext()) {
email = new JSONObject();
try{
email.put("primary", false);
email.put("value", cursor.getString(cursor.getColumnIndex(ContactMethods.DATA)));
// TODO Find out why adding an email type throws and exception
//email.put("type", cursor.getString(cursor.getColumnIndex(ContactMethods.TYPE)));
emails.put(email);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
}
return emails;
}
@Override
public void save(JSONObject contact) {
// TODO Auto-generated method stub
}
@Override
/**
* This method will remove a Contact from the database based on ID.
* @param id the unique ID of the contact to remove
*/
public boolean remove(String id) {
int result = mApp.getContentResolver().delete(People.CONTENT_URI,
"people._id = ?",
new String[] {id});
return (result > 0) ? true : false;
}
}

File diff suppressed because it is too large Load Diff

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,31 +43,57 @@ public class ContactManager extends Plugin {
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
if (contactAccessor == null) {
contactAccessor = ContactAccessor.getInstance(webView, ctx);
}
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
/**
* Check to see if we are on an Android 1.X device. If we are return an error as we
* do not support this as of PhoneGap 1.0.
*/
if (android.os.Build.VERSION.RELEASE.startsWith("1.")) {
JSONObject res = null;
try {
res = new JSONObject();
res.put("code", NOT_SUPPORTED_ERROR);
res.put("message", "Contacts are not supported in Android 1.X devices");
} catch (JSONException e) {
// This should never happen
Log.e(LOG_TAG, e.getMessage(), e);
}
return new PluginResult(PluginResult.Status.ERROR, res);
}
/**
* Only create the contactAccessor after we check the Android version or the program will crash
* older phones.
*/
if (this.contactAccessor == null) {
this.contactAccessor = new ContactAccessorSdk5(this.webView, this.ctx);
}
try {
if (action.equals("search")) {
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.getJSONObject(1));
return new PluginResult(status, res);
JSONArray res = contactAccessor.search(args.getJSONArray(0), args.optJSONObject(1));
return new PluginResult(status, res, "navigator.contacts.cast");
}
else if (action.equals("save")) {
// TODO Coming soon!
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);

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

@@ -11,15 +11,14 @@ import java.util.TimeZone;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import android.content.Context;
import android.provider.Settings;
import android.telephony.TelephonyManager;
public class Device extends Plugin {
public static String phonegapVersion = "0.9.2"; // PhoneGap version
public static String phonegapVersion = "1.0.0rc3"; // PhoneGap version
public static String platform = "Android"; // Device OS
public static String uuid; // Device UUID
@@ -35,7 +34,7 @@ public class Device extends Plugin {
*
* @param ctx The context of the main Activity.
*/
public void setContext(DroidGap ctx) {
public void setContext(PhonegapActivity ctx) {
super.setContext(ctx);
Device.uuid = getUuid();
}
@@ -115,34 +114,13 @@ public class Device extends Plugin {
public String getPhonegapVersion() {
return Device.phonegapVersion;
}
public String getLine1Number(){
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getLine1Number();
}
public String getDeviceId(){
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getDeviceId();
}
public String getSimSerialNumber(){
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getSimSerialNumber();
}
public String getSubscriberId(){
TelephonyManager operator = (TelephonyManager)this.ctx.getSystemService(Context.TELEPHONY_SERVICE);
return operator.getSubscriberId();
}
public String getModel()
{
public String getModel() {
String model = android.os.Build.MODEL;
return model;
}
public String getProductName()
{
public String getProductName() {
String productname = android.os.Build.PRODUCT;
return productname;
}
@@ -157,8 +135,7 @@ public class Device extends Plugin {
return osversion;
}
public String getSDKVersion()
{
public String getSDKVersion() {
String sdkversion = android.os.Build.VERSION.SDK;
return sdkversion;
}

View File

@@ -9,9 +9,9 @@ package com.phonegap;
import java.io.File;
import android.content.Context;
import android.os.Environment;
import android.os.StatFs;
import android.util.Log;
/**
* This class provides file directory utilities.
@@ -21,6 +21,8 @@ import android.util.Log;
*/
public class DirectoryManager {
private static final String LOG_TAG = "DirectoryManager";
/**
* Determine if a file or directory exists.
*
@@ -36,7 +38,6 @@ public class DirectoryManager {
File newPath = constructFilePaths(path.toString(), name);
status = newPath.exists();
}
// If no SD card
else{
status = false;
@@ -72,29 +73,6 @@ public class DirectoryManager {
return (freeSpace);
}
/**
* Create directory on SD card.
*
* @param directoryName The name of the directory to create.
* @return T=successful, F=failed
*/
protected static boolean createDirectory(String directoryName) {
boolean status;
// Make sure SD card exists
if ((testSaveLocationExists()) && (!directoryName.equals(""))) {
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), directoryName);
status = newPath.mkdir();
status = true;
}
// If no SD card or invalid dir name
else {
status = false;
}
return status;
}
/**
* Determine if SD card exists.
@@ -117,95 +95,6 @@ public class DirectoryManager {
return status;
}
/**
* Delete directory.
*
* @param fileName The name of the directory to delete
* @return T=deleted, F=could not delete
*/
protected static boolean deleteDirectory(String fileName) {
boolean status;
SecurityManager checker = new SecurityManager();
// Make sure SD card exists
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), fileName);
checker.checkDelete(newPath.toString());
// If dir to delete is really a directory
if (newPath.isDirectory()) {
String[] listfile = newPath.list();
// Delete all files within the specified directory and then delete the directory
try{
for (int i=0; i < listfile.length; i++){
File deletedFile = new File (newPath.toString()+"/"+listfile[i].toString());
deletedFile.delete();
}
newPath.delete();
Log.i("DirectoryManager deleteDirectory", fileName);
status = true;
}
catch (Exception e){
e.printStackTrace();
status = false;
}
}
// If dir not a directory, then error
else {
status = false;
}
}
// If no SD card
else {
status = false;
}
return status;
}
/**
* Delete file.
*
* @param fileName The name of the file to delete
* @return T=deleted, F=not deleted
*/
protected static boolean deleteFile(String fileName) {
boolean status;
SecurityManager checker = new SecurityManager();
// Make sure SD card exists
if ((testSaveLocationExists()) && (!fileName.equals(""))) {
File path = Environment.getExternalStorageDirectory();
File newPath = constructFilePaths(path.toString(), fileName);
checker.checkDelete(newPath.toString());
// If file to delete is really a file
if (newPath.isFile()){
try {
Log.i("DirectoryManager deleteFile", fileName);
newPath.delete();
status = true;
}catch (SecurityException se){
se.printStackTrace();
status = false;
}
}
// If not a file, then error
else {
status = false;
}
}
// If no SD card
else {
status = false;
}
return status;
}
/**
* Create a new file object from two file paths.
*
@@ -215,8 +104,39 @@ public class DirectoryManager {
*/
private static File constructFilePaths (String file1, String file2) {
File newPath;
newPath = new File(file1+"/"+file2);
if (file2.startsWith(file1)) {
newPath = new File(file2);
}
else {
newPath = new File(file1+"/"+file2);
}
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();
}
}

1414
framework/src/com/phonegap/DroidGap.java Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,365 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
package com.phonegap;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Iterator;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.net.Uri;
import android.util.Log;
import android.webkit.CookieManager;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
public class FileTransfer extends Plugin {
private static final String LOG_TAG = "FileUploader";
private static final String LINE_START = "--";
private static final String LINE_END = "\r\n";
private static final String BOUNDRY = "*****";
public static int FILE_NOT_FOUND_ERR = 1;
public static int INVALID_URL_ERR = 2;
public static int CONNECTION_ERR = 3;
private SSLSocketFactory defaultSSLSocketFactory = null;
private HostnameVerifier defaultHostnameVerifier = null;
/* (non-Javadoc)
* @see com.phonegap.api.Plugin#execute(java.lang.String, org.json.JSONArray, java.lang.String)
*/
@Override
public PluginResult execute(String action, JSONArray args, String callbackId) {
String file = null;
String server = null;
try {
file = args.getString(0);
server = args.getString(1);
}
catch (JSONException e) {
Log.d(LOG_TAG, "Missing filename or server name");
return new PluginResult(PluginResult.Status.JSON_EXCEPTION, "Missing filename or server name");
}
// Setup the options
String fileKey = null;
String fileName = null;
String mimeType = null;
fileKey = getArgument(args, 2, "file");
fileName = getArgument(args, 3, "image.jpg");
mimeType = getArgument(args, 4, "image/jpeg");
try {
JSONObject params = args.optJSONObject(5);
boolean trustEveryone = args.optBoolean(6);
if (action.equals("upload")) {
FileUploadResult r = upload(file, server, fileKey, fileName, mimeType, params, trustEveryone);
Log.d(LOG_TAG, "****** About to return a result from upload");
return new PluginResult(PluginResult.Status.OK, r.toJSONObject());
} else {
return new PluginResult(PluginResult.Status.INVALID_ACTION);
}
} catch (FileNotFoundException e) {
Log.e(LOG_TAG, e.getMessage(), e);
JSONObject error = createFileUploadError(FILE_NOT_FOUND_ERR);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IllegalArgumentException e) {
Log.e(LOG_TAG, e.getMessage(), e);
JSONObject error = createFileUploadError(INVALID_URL_ERR);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (SSLException e) {
Log.e(LOG_TAG, e.getMessage(), e);
Log.d(LOG_TAG, "Got my ssl exception!!!");
JSONObject error = createFileUploadError(CONNECTION_ERR);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage(), e);
JSONObject error = createFileUploadError(CONNECTION_ERR);
return new PluginResult(PluginResult.Status.IO_EXCEPTION, error);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
}
}
// always verify the host - don't check for certificate
final static HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
/**
* This function will install a trust manager that will blindly trust all SSL
* certificates. The reason this code is being added is to enable developers
* to do development using self signed SSL certificates on their web server.
*
* The standard HttpsURLConnection class will throw an exception on self
* signed certificates if this code is not run.
*/
private void trustAllHosts() {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[] {};
}
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}
} };
// Install the all-trusting trust manager
try {
// Backup the current SSL socket factory
defaultSSLSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
// Install our all trusting manager
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
} catch (Exception e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
}
/**
* Create an error object based on the passed in errorCode
* @param errorCode the error
* @return JSONObject containing the error
*/
private JSONObject createFileUploadError(int errorCode) {
JSONObject error = null;
try {
error = new JSONObject();
error.put("code", errorCode);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
return error;
}
/**
* Convenience method to read a parameter from the list of JSON args.
* @param args the args passed to the Plugin
* @param position the position to retrieve the arg from
* @param defaultString the default to be used if the arg does not exist
* @return String with the retrieved value
*/
private String getArgument(JSONArray args, int position, String defaultString) {
String arg = defaultString;
if(args.length() >= position) {
arg = args.optString(position);
if (arg == null || "null".equals(arg)) {
arg = defaultString;
}
}
return arg;
}
/**
* Uploads the specified file to the server URL provided using an HTTP
* multipart request.
* @param file Full path of the file on the file system
* @param server URL of the server to receive the file
* @param fileKey Name of file request parameter
* @param fileName File name to be used on server
* @param mimeType Describes file content type
* @param params key:value pairs of user-defined parameters
* @return FileUploadResult containing result of upload request
*/
public FileUploadResult upload(String file, String server, final String fileKey, final String fileName,
final String mimeType, JSONObject params, boolean trustEveryone) throws IOException, SSLException {
// Create return object
FileUploadResult result = new FileUploadResult();
// Get a input stream of the file on the phone
InputStream fileInputStream = getPathFromUri(file);
HttpURLConnection conn = null;
DataOutputStream dos = null;
int bytesRead, bytesAvailable, bufferSize;
long totalBytes;
byte[] buffer;
int maxBufferSize = 8096;
//------------------ CLIENT REQUEST
// open a URL connection to the server
URL url = new URL(server);
// Open a HTTP connection to the URL based on protocol
if (url.getProtocol().toLowerCase().equals("https")) {
// Using standard HTTPS connection. Will not allow self signed certificate
if (!trustEveryone) {
conn = (HttpsURLConnection) url.openConnection();
}
// Use our HTTPS connection that blindly trusts everyone.
// This should only be used in debug environments
else {
// Setup the HTTPS connection class to trust everyone
trustAllHosts();
HttpsURLConnection https = (HttpsURLConnection) url.openConnection();
// Save the current hostnameVerifier
defaultHostnameVerifier = https.getHostnameVerifier();
// Setup the connection not to verify hostnames
https.setHostnameVerifier(DO_NOT_VERIFY);
conn = https;
}
}
// Return a standard HTTP conneciton
else {
conn = (HttpURLConnection) url.openConnection();
}
// Allow Inputs
conn.setDoInput(true);
// Allow Outputs
conn.setDoOutput(true);
// Don't use a cached copy.
conn.setUseCaches(false);
// Use a post method.
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+BOUNDRY);
// Set the cookies on the response
String cookie = CookieManager.getInstance().getCookie(server);
if (cookie != null) {
conn.setRequestProperty("Cookie", cookie);
}
dos = new DataOutputStream( conn.getOutputStream() );
// Send any extra parameters
try {
for (Iterator iter = params.keys(); iter.hasNext();) {
Object key = iter.next();
dos.writeBytes(LINE_START + BOUNDRY + LINE_END);
dos.writeBytes("Content-Disposition: form-data; name=\"" + key.toString() + "\";");
dos.writeBytes(LINE_END + LINE_END);
dos.writeBytes(params.getString(key.toString()));
dos.writeBytes(LINE_END);
}
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}
dos.writeBytes(LINE_START + BOUNDRY + LINE_END);
dos.writeBytes("Content-Disposition: form-data; name=\"" + fileKey + "\";" + " filename=\"" + fileName +"\"" + LINE_END);
dos.writeBytes("Content-Type: " + mimeType + LINE_END);
dos.writeBytes(LINE_END);
// create a buffer of maximum size
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
buffer = new byte[bufferSize];
// read file and write it into form...
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
totalBytes = 0;
while (bytesRead > 0) {
totalBytes += bytesRead;
result.setBytesSent(totalBytes);
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
// send multipart form data necesssary after file data...
dos.writeBytes(LINE_END);
dos.writeBytes(LINE_START + BOUNDRY + LINE_START + LINE_END);
// close streams
fileInputStream.close();
dos.flush();
dos.close();
//------------------ read the SERVER RESPONSE
StringBuffer responseString = new StringBuffer("");
DataInputStream inStream;
try {
inStream = new DataInputStream ( conn.getInputStream() );
} catch(FileNotFoundException e) {
throw new IOException("Received error from server");
}
String line;
while (( line = inStream.readLine()) != null) {
responseString.append(line);
}
Log.d(LOG_TAG, "got response from server");
Log.d(LOG_TAG, responseString.toString());
// send request and retrieve response
result.setResponseCode(conn.getResponseCode());
result.setResponse(responseString.toString());
inStream.close();
conn.disconnect();
// Revert back to the proper verifier and socket factories
if (trustEveryone && url.getProtocol().toLowerCase().equals("https")) {
((HttpsURLConnection)conn).setHostnameVerifier(defaultHostnameVerifier);
HttpsURLConnection.setDefaultSSLSocketFactory(defaultSSLSocketFactory);
}
return result;
}
/**
* Get an input stream based on file path or content:// uri
*
* @param path
* @return an input stream
* @throws FileNotFoundException
*/
private InputStream getPathFromUri(String path) throws FileNotFoundException {
if (path.startsWith("content:")) {
Uri uri = Uri.parse(path);
return ctx.getContentResolver().openInputStream(uri);
}
else {
return new FileInputStream(path);
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi
* Copyright (c) 2010, IBM Corporation
*/
package com.phonegap;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Encapsulates the result and/or status of uploading a file to a remote server.
*/
public class FileUploadResult {
private long bytesSent = 0; // bytes sent
private int responseCode = -1; // HTTP response code
private String response = null; // HTTP response
public long getBytesSent() {
return bytesSent;
}
public void setBytesSent(long bytes) {
this.bytesSent = bytes;
}
public int getResponseCode() {
return responseCode;
}
public void setResponseCode(int responseCode) {
this.responseCode = responseCode;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
public JSONObject toJSONObject() throws JSONException {
return new JSONObject(
"{bytesSent:" + bytesSent +
",responseCode:" + responseCode +
",response:" + JSONObject.quote(response) + "}");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -86,7 +86,7 @@ public class GeoListener {
* @param msg The error message
*/
void fail(int code, String msg) {
this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', " + ", " + code + ", '" + msg + "');");
this.broker.sendJavascript("navigator._geo.fail('" + this.id + "', '" + code + "', '" + msg + "');");
this.stop();
}

View File

@@ -7,6 +7,8 @@
*/
package com.phonegap;
import com.phonegap.api.PhonegapActivity;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
@@ -19,7 +21,7 @@ import android.os.Bundle;
*/
public class GpsListener implements LocationListener {
private DroidGap mCtx; // DroidGap object
private PhonegapActivity mCtx; // PhonegapActivity object
private LocationManager mLocMan; // Location manager object
private GeoListener owner; // Geolistener object (parent)
@@ -35,7 +37,7 @@ public class GpsListener implements LocationListener {
* @param interval
* @param m
*/
public GpsListener(DroidGap ctx, int interval, GeoListener m) {
public GpsListener(PhonegapActivity ctx, int interval, GeoListener m) {
this.owner = m;
this.mCtx = ctx;
this.mLocMan = (LocationManager) this.mCtx.getSystemService(Context.LOCATION_SERVICE);

View File

@@ -7,6 +7,8 @@
*/
package com.phonegap;
import com.phonegap.api.PhonegapActivity;
import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
@@ -15,7 +17,7 @@ import android.os.Bundle;
public class NetworkListener implements LocationListener {
private DroidGap mCtx; // DroidGap object
private PhonegapActivity mCtx; // PhonegapActivity object
private LocationManager mLocMan; // Location manager object
private GeoListener owner; // Geolistener object (parent)
@@ -31,7 +33,7 @@ public class NetworkListener implements LocationListener {
* @param interval
* @param m
*/
public NetworkListener(DroidGap ctx, int interval, GeoListener m) {
public NetworkListener(PhonegapActivity ctx, int interval, GeoListener m) {
this.owner = m;
this.mCtx = ctx;
this.mLocMan = (LocationManager) this.mCtx.getSystemService(Context.LOCATION_SERVICE);

View File

@@ -3,33 +3,65 @@
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
* Copyright (c) 2010-2011, IBM Corporation
*/
package com.phonegap;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.net.*;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
public class NetworkManager extends Plugin {
public static int NOT_REACHABLE = 0;
public static int NOT_REACHABLE = 0;
public static int REACHABLE_VIA_CARRIER_DATA_NETWORK = 1;
public static int REACHABLE_VIA_WIFI_NETWORK = 2;
public static final String WIFI = "wifi";
public static final String WIMAX = "wimax";
// mobile
public static final String MOBILE = "mobile";
// 2G network types
public static final String GSM = "gsm";
public static final String GPRS = "gprs";
public static final String EDGE = "edge";
// 3G network types
public static final String CDMA = "cdma";
public static final String UMTS = "umts";
// 4G network types
public static final String LTE = "lte";
public static final String UMB = "umb";
// return types
public static final String TYPE_UNKNOWN = "unknown";
public static final String TYPE_ETHERNET = "ethernet";
public static final String TYPE_WIFI = "wifi";
public static final String TYPE_2G = "2g";
public static final String TYPE_3G = "3g";
public static final String TYPE_4G = "4g";
public static final String TYPE_NONE = "none";
ConnectivityManager sockMan;
private static final String LOG_TAG = "NetworkManager";
private String connectionCallbackId;
ConnectivityManager sockMan;
BroadcastReceiver receiver;
/**
* Constructor.
*/
public NetworkManager() {
this.receiver = null;
}
/**
@@ -38,11 +70,26 @@ public class NetworkManager extends Plugin {
*
* @param ctx The context of the main Activity.
*/
public void setContext(DroidGap ctx) {
public void setContext(PhonegapActivity ctx) {
super.setContext(ctx);
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
}
this.sockMan = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);
this.connectionCallbackId = null;
// We need to listen to connectivity events to update navigator.connection
IntentFilter intentFilter = new IntentFilter() ;
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
if (this.receiver == null) {
this.receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updateConnectionInfo((NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
}
};
ctx.registerReceiver(this.receiver, intentFilter);
}
}
/**
* Executes the request and returns PluginResult.
*
@@ -52,25 +99,18 @@ public class NetworkManager extends Plugin {
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
try {
if (action.equals("isAvailable")) {
boolean b = this.isAvailable();
return new PluginResult(status, b);
}
else if (action.equals("isWifiActive")) {
boolean b = this.isWifiActive();
return new PluginResult(status, b);
}
else if (action.equals("isReachable")) {
int i = this.isReachable(args.getString(0), args.getBoolean(1));
return new PluginResult(status, i);
}
return new PluginResult(status, result);
} catch (JSONException e) {
return new PluginResult(PluginResult.Status.JSON_EXCEPTION);
PluginResult.Status status = PluginResult.Status.INVALID_ACTION;
String result = "Unsupported Operation: " + action;
if (action.equals("getConnectionInfo")) {
this.connectionCallbackId = callbackId;
NetworkInfo info = sockMan.getActiveNetworkInfo();
PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, this.getConnectionInfo(info));
pluginResult.setKeepCallback(true);
return pluginResult;
}
return new PluginResult(status, result);
}
/**
@@ -83,70 +123,100 @@ public class NetworkManager extends Plugin {
// All methods take a while, so always use async
return false;
}
/**
* Stop network receiver.
*/
public void onDestroy() {
if (this.receiver != null) {
try {
this.ctx.unregisterReceiver(this.receiver);
} catch (Exception e) {
Log.e(LOG_TAG, "Error unregistering network receiver: " + e.getMessage(), e);
}
}
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
/**
* Determine if a network connection exists.
*
* @return
*/
public boolean isAvailable() {
NetworkInfo info = sockMan.getActiveNetworkInfo();
boolean conn = false;
if (info != null) {
conn = info.isConnected();
}
return conn;
}
/**
* Determine if a WIFI connection exists.
* Updates the JavaScript side whenever the connection changes
*
* @param info the current active network info
* @return
*/
public boolean isWifiActive() {
NetworkInfo info = sockMan.getActiveNetworkInfo();
if (info != null) {
String type = info.getTypeName();
return type.equals("WIFI");
}
return false;
private void updateConnectionInfo(NetworkInfo info) {
// send update to javascript "navigator.network.connection"
sendUpdate(this.getConnectionInfo(info));
}
/**
* Determine if a URI is reachable over the network.
/**
* Get the latest network connection information
*
* @param uri
* @param isIpAddress
* @return
* @param info the current active network info
* @return a JSONObject that represents the network info
*/
public int isReachable(String uri, boolean isIpAddress) {
int reachable = NOT_REACHABLE;
if (uri.indexOf("http://") == -1) {
uri = "http://" + uri;
}
if (this.isAvailable()) {
try {
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpGet httpget = new HttpGet(uri);
httpclient.execute(httpget);
if (this.isWifiActive()) {
reachable = REACHABLE_VIA_WIFI_NETWORK;
}
else {
reachable = REACHABLE_VIA_CARRIER_DATA_NETWORK;
}
} catch (Exception e) {
reachable = NOT_REACHABLE;
private String getConnectionInfo(NetworkInfo info) {
String type = TYPE_NONE;
if (info != null) {
// If we are not connected to any network set type to none
if (!info.isConnected()) {
type = TYPE_NONE;
}
else {
type = getType(info);
}
}
return reachable;
return type;
}
/**
* Create a new plugin result and send it back to JavaScript
*
* @param connection the network info to set as navigator.connection
*/
private void sendUpdate(String type) {
PluginResult result = new PluginResult(PluginResult.Status.OK, type);
result.setKeepCallback(true);
this.success(result, this.connectionCallbackId);
}
/**
* Determine the type of connection
*
* @param info the network info so we can determine connection type.
* @return the type of mobile network we are on
*/
private String getType(NetworkInfo info) {
if (info != null) {
String type = info.getTypeName();
if (type.toLowerCase().equals(WIFI)) {
return TYPE_WIFI;
}
else if (type.toLowerCase().equals(MOBILE)) {
type = info.getSubtypeName();
if (type.toLowerCase().equals(GSM) ||
type.toLowerCase().equals(GPRS) ||
type.toLowerCase().equals(EDGE)) {
return TYPE_2G;
}
else if (type.toLowerCase().equals(CDMA) ||
type.toLowerCase().equals(UMTS)) {
return TYPE_3G;
}
else if (type.toLowerCase().equals(LTE) ||
type.toLowerCase().equals(UMB)) {
return TYPE_4G;
}
}
}
else {
return TYPE_NONE;
}
return TYPE_UNKNOWN;
}
}

View File

@@ -10,6 +10,7 @@ package com.phonegap;
import org.json.JSONArray;
import org.json.JSONException;
import com.phonegap.api.Plugin;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.PluginResult;
import android.app.AlertDialog;
import android.app.ProgressDialog;
@@ -172,7 +173,7 @@ public class Notification extends Plugin {
*/
public synchronized void alert(final String message, final String title, final String buttonLabel, final String callbackId) {
final DroidGap ctx = this.ctx;
final PhonegapActivity ctx = this.ctx;
final Notification notification = this;
Runnable runnable = new Runnable() {
@@ -208,7 +209,7 @@ public class Notification extends Plugin {
*/
public synchronized void confirm(final String message, final String title, String buttonLabels, final String callbackId) {
final DroidGap ctx = this.ctx;
final PhonegapActivity ctx = this.ctx;
final Notification notification = this;
final String[] fButtons = buttonLabels.split(",");
@@ -272,7 +273,7 @@ public class Notification extends Plugin {
this.spinnerDialog = null;
}
final Notification notification = this;
final DroidGap ctx = this.ctx;
final PhonegapActivity ctx = this.ctx;
Runnable runnable = new Runnable() {
public void run() {
notification.spinnerDialog = ProgressDialog.show(ctx, title , message, true, true,
@@ -308,7 +309,7 @@ public class Notification extends Plugin {
this.progressDialog = null;
}
final Notification notification = this;
final DroidGap ctx = this.ctx;
final PhonegapActivity ctx = this.ctx;
Runnable runnable = new Runnable() {
public void run() {
notification.progressDialog = new ProgressDialog(ctx);

View File

@@ -1,21 +0,0 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
*/
package com.phonegap;
public class SplashScreen {
private final DroidGap gap;
public SplashScreen(DroidGap gap) {
this.gap = gap;
}
public void hide() {
gap.runOnUiThread(new Runnable() {
public void run() {
gap.hideSplashScreen();
}
});
}
}

View File

@@ -16,15 +16,21 @@ import android.database.Cursor;
import android.database.sqlite.*;
/**
* This class implements the HTML5 database support for Android 1.X devices.
* It is not used for Android 2.X, since HTML5 database is built in to the browser.
* This class implements the HTML5 database support for Android 1.X devices. It
* is not used for Android 2.X, since HTML5 database is built in to the browser.
*/
public class Storage extends Plugin {
// Data Definition Language
private static final String ALTER = "alter";
private static final String CREATE = "create";
private static final String DROP = "drop";
private static final String TRUNCATE = "truncate";
SQLiteDatabase myDb = null; // Database object
String path = null; // Database path
String dbName = null; // Database name
SQLiteDatabase myDb = null; // Database object
String path = null; // Database path
String dbName = null; // Database name
/**
* Constructor.
*/
@@ -34,29 +40,37 @@ public class Storage extends Plugin {
/**
* Executes the request and returns PluginResult.
*
* @param action The action to execute.
* @param args JSONArry of arguments for the plugin.
* @param callbackId The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
* @param action
* The action to execute.
* @param args
* JSONArry of arguments for the plugin.
* @param callbackId
* The callback id used when calling back into JavaScript.
* @return A PluginResult object with a status and message.
*/
public PluginResult execute(String action, JSONArray args, String callbackId) {
PluginResult.Status status = PluginResult.Status.OK;
String result = "";
String result = "";
try {
// TODO: Do we want to allow a user to do this, since they could get to other app databases?
// TODO: Do we want to allow a user to do this, since they could get
// to other app databases?
if (action.equals("setStorage")) {
this.setStorage(args.getString(0));
}
else if (action.equals("openDatabase")) {
this.openDatabase(args.getString(0), args.getString(1), args.getString(2), args.getLong(3));
}
else if (action.equals("executeSql")) {
JSONArray a = args.getJSONArray(1);
int len = a.length();
String[] s = new String[len];
for (int i=0; i<len; i++) {
s[i] = a.getString(i);
} else if (action.equals("openDatabase")) {
this.openDatabase(args.getString(0), args.getString(1),
args.getString(2), args.getLong(3));
} else if (action.equals("executeSql")) {
String[] s = null;
if (args.isNull(1)) {
s = new String[0];
} else {
JSONArray a = args.getJSONArray(1);
int len = a.length();
s = new String[len];
for (int i = 0; i < len; i++) {
s[i] = a.getString(i);
}
}
this.executeSql(args.getString(0), s, args.getString(2));
}
@@ -67,15 +81,17 @@ public class Storage extends Plugin {
}
/**
* Identifies if action to be executed returns a value and should be run synchronously.
* Identifies if action to be executed returns a value and should be run
* synchronously.
*
* @param action The action to execute
* @return T=returns value
* @param action
* The action to execute
* @return T=returns value
*/
public boolean isSynch(String action) {
return false;
return true;
}
/**
* Clean up and close database.
*/
@@ -87,33 +103,40 @@ public class Storage extends Plugin {
}
}
//--------------------------------------------------------------------------
// LOCAL METHODS
//--------------------------------------------------------------------------
// --------------------------------------------------------------------------
// LOCAL METHODS
// --------------------------------------------------------------------------
/**
* Set the application package for the database. Each application saves its
* database files in a directory with the application package as part of the file name.
* Set the application package for the database. Each application saves its
* database files in a directory with the application package as part of the
* file name.
*
* For example, application "com.phonegap.demo.Demo" would save its database
* files in "/data/data/com.phonegap.demo/databases/" directory.
*
* @param appPackage The application package.
* @param appPackage
* The application package.
*/
public void setStorage(String appPackage) {
this.path = "/data/data/" + appPackage + "/databases/";
}
/**
* Open database.
*
* @param db The name of the database
* @param version The version
* @param display_name The display name
* @param size The size in bytes
* @param db
* The name of the database
* @param version
* The version
* @param display_name
* The display name
* @param size
* The size in bytes
*/
public void openDatabase(String db, String version, String display_name, long size) {
public void openDatabase(String db, String version, String display_name,
long size) {
// If database is open, then close it
if (this.myDb != null) {
this.myDb.close();
@@ -121,27 +144,36 @@ public class Storage extends Plugin {
// If no database path, generate from application package
if (this.path == null) {
Package pack = this.ctx.getClass().getPackage();
String appPackage = pack.getName();
this.setStorage(appPackage);
Package pack = this.ctx.getClass().getPackage();
String appPackage = pack.getName();
this.setStorage(appPackage);
}
this.dbName = this.path + db + ".db";
this.myDb = SQLiteDatabase.openOrCreateDatabase(this.dbName, null);
}
/**
* Execute SQL statement.
*
* @param query The SQL query
* @param params Parameters for the query
* @param tx_id Transaction id
* @param query
* The SQL query
* @param params
* Parameters for the query
* @param tx_id
* Transaction id
*/
public void executeSql(String query, String[] params, String tx_id) {
try {
Cursor myCursor = this.myDb.rawQuery(query, params);
this.processResults(myCursor, tx_id);
myCursor.close();
if (isDDL(query)) {
this.myDb.execSQL(query);
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', '');");
}
else {
Cursor myCursor = this.myDb.rawQuery(query, params);
this.processResults(myCursor, tx_id);
myCursor.close();
}
}
catch (SQLiteException ex) {
ex.printStackTrace();
@@ -151,44 +183,64 @@ public class Storage extends Plugin {
this.sendJavascript("droiddb.fail('" + ex.getMessage() + "','" + tx_id + "');");
}
}
/**
* Checks to see the the query is a Data Definintion command
*
* @param query to be executed
* @return true if it is a DDL command, false otherwise
*/
private boolean isDDL(String query) {
String cmd = query.toLowerCase();
if (cmd.startsWith(DROP) || cmd.startsWith(CREATE) || cmd.startsWith(ALTER) || cmd.startsWith(TRUNCATE)) {
return true;
}
return false;
}
/**
* Process query results.
*
* @param cur Cursor into query results
* @param tx_id Transaction id
* @param cur
* Cursor into query results
* @param tx_id
* Transaction id
*/
public void processResults(Cursor cur, String tx_id) {
String result = "[]";
// If query result has rows
if (cur.moveToFirst()) {
JSONArray fullresult = new JSONArray();
String key = "";
String value = "";
int colCount = cur.getColumnCount();
// Build up JSON result object for each row
do {
JSONObject result = new JSONObject();
JSONObject row = new JSONObject();
try {
for (int i = 0; i < colCount; ++i) {
key = cur.getColumnName(i);
value = cur.getString(i).replace("\"", "\\\""); // must escape " with \" for JavaScript
result.put(key, value);
value = cur.getString(i);
row.put(key, value);
}
fullresult.put(row);
// Send row back to JavaScript
this.sendJavascript("droiddb.addResult('" + result.toString() + "','" + tx_id + "');");
} catch (JSONException e) {
e.printStackTrace();
}
} while (cur.moveToNext());
result = fullresult.toString();
}
// Let JavaScript know that there are no more rows
this.sendJavascript("droiddb.completeQuery('" + tx_id + "');");
this.sendJavascript("droiddb.completeQuery('" + tx_id + "', " + result
+ ");");
}
}

3
framework/src/com/phonegap/TempListener.java Normal file → Executable file
View File

@@ -11,6 +11,7 @@ import java.util.List;
import org.json.JSONArray;
import com.phonegap.api.PhonegapActivity;
import com.phonegap.api.Plugin;
import com.phonegap.api.PluginResult;
@@ -37,7 +38,7 @@ public class TempListener extends Plugin implements SensorEventListener {
*
* @param ctx The context of the main Activity.
*/
public void setContext(DroidGap ctx) {
public void setContext(PhonegapActivity ctx) {
super.setContext(ctx);
this.sensorManager = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE);
}

View File

@@ -8,7 +8,6 @@
package com.phonegap.api;
import org.json.JSONArray;
import com.phonegap.DroidGap;
import android.content.Intent;
import android.webkit.WebView;
@@ -43,7 +42,7 @@ public interface IPlugin {
*
* @param ctx The context of the main Activity.
*/
void setContext(DroidGap ctx);
void setContext(PhonegapActivity ctx);
/**
* Sets the main View of the application, this is the WebView within which
@@ -55,14 +54,23 @@ public interface IPlugin {
/**
* Called when the system is about to start resuming a previous activity.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
void onPause();
void onPause(boolean multitasking);
/**
* Called when the activity will start interacting with the user.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
void onResume();
void onResume(boolean multitasking);
/**
* Called when the activity receives a new intent.
*/
void onNewIntent(Intent intent);
/**
* The final call you receive before your activity is destroyed.
*/

View File

@@ -0,0 +1,50 @@
/*
* PhoneGap is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2005-2010, Nitobi Software Inc.
* Copyright (c) 2010, IBM Corporation
*/
package com.phonegap.api;
import android.app.Activity;
import android.content.Intent;
/**
* The Phonegap activity abstract class that is extended by DroidGap.
* 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.
*
* @param serviceType
* @param className
*/
abstract public void addService(String serviceType, String className);
/**
* Send JavaScript statement back to JavaScript.
*
* @param message
*/
abstract public void sendJavascript(String statement);
/**
* Launch an activity for which you would like a result when it finished. When this activity exits,
* your onActivityResult() method will be called.
*
* @param command The command object
* @param intent The intent to start
* @param requestCode The request code that is passed to callback to identify the activity
*/
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,7 +8,8 @@
package com.phonegap.api;
import org.json.JSONArray;
import com.phonegap.DroidGap;
import org.json.JSONObject;
import android.content.Intent;
import android.webkit.WebView;
@@ -19,8 +20,9 @@ import android.webkit.WebView;
*/
public abstract class Plugin implements IPlugin {
public String id;
public WebView webView; // WebView object
public DroidGap ctx; // DroidGap object
public PhonegapActivity ctx; // PhonegapActivity object
/**
* Executes the request and returns PluginResult.
@@ -48,7 +50,7 @@ public abstract class Plugin implements IPlugin {
*
* @param ctx The context of the main Activity.
*/
public void setContext(DroidGap ctx) {
public void setContext(PhonegapActivity ctx) {
this.ctx = ctx;
}
@@ -64,14 +66,24 @@ public abstract class Plugin implements IPlugin {
/**
* Called when the system is about to start resuming a previous activity.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onPause() {
public void onPause(boolean multitasking) {
}
/**
* Called when the activity will start interacting with the user.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onResume() {
public void onResume(boolean multitasking) {
}
/**
* Called when the activity receives a new intent.
*/
public void onNewIntent(Intent intent) {
}
/**
@@ -116,6 +128,26 @@ public abstract class Plugin implements IPlugin {
this.ctx.sendJavascript(pluginResult.toSuccessCallbackString(callbackId));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void success(JSONObject message, String callbackId) {
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
}
/**
* Helper for success callbacks that just returns the Status.OK by default
*
* @param message The message to add to the success result.
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void success(String message, String callbackId) {
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.OK, message).toSuccessCallbackString(callbackId));
}
/**
* Call the JavaScript error callback for this plugin.
*
@@ -125,4 +157,24 @@ public abstract class Plugin implements IPlugin {
public void error(PluginResult pluginResult, String callbackId) {
this.ctx.sendJavascript(pluginResult.toErrorCallbackString(callbackId));
}
/**
* Helper for error callbacks that just returns the Status.ERROR by default
*
* @param message The message to add to the error result.
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void error(JSONObject message, String callbackId) {
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
}
/**
* Helper for error callbacks that just returns the Status.ERROR by default
*
* @param message The message to add to the error result.
* @param callbackId The callback id used when calling back into JavaScript.
*/
public void error(String message, String callbackId) {
this.ctx.sendJavascript(new PluginResult(PluginResult.Status.ERROR, message).toErrorCallbackString(callbackId));
}
}

View File

@@ -7,14 +7,17 @@
*/
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;
import com.phonegap.DroidGap;
/**
* PluginManager is exposed to JavaScript in the PhoneGap WebView.
@@ -24,10 +27,10 @@ import com.phonegap.DroidGap;
*/
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 DroidGap ctx;
private final PhonegapActivity ctx;
private final WebView app;
/**
@@ -36,9 +39,38 @@ public final class PluginManager {
* @param app
* @param ctx
*/
public PluginManager(WebView app, DroidGap ctx) {
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,8 +107,8 @@ public final class PluginManager {
c = getClassByName(clazz);
}
if (isPhoneGapPlugin(c)) {
final Plugin plugin = this.addPlugin(clazz, c);
final DroidGap ctx = this.ctx;
final IPlugin plugin = this.addPlugin(clazz, c);
final PhonegapActivity ctx = this.ctx;
runAsync = async && !plugin.isSynch(action);
if (runAsync) {
// Run this on a different thread so that this one can return back to JS
@@ -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,14 +203,14 @@ 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((DroidGap)this.ctx);
plugin.setContext(this.ctx);
plugin.setView(this.app);
return plugin;
}
@@ -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;
}
@@ -230,27 +245,31 @@ public final class PluginManager {
/**
* Called when the system is about to start resuming a previous activity.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onPause() {
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
public void onPause(boolean multitasking) {
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
while(it.hasNext()) {
Entry<String,Plugin> entry = it.next();
Plugin plugin = entry.getValue();
plugin.onPause();
Entry<String,IPlugin> entry = it.next();
IPlugin plugin = entry.getValue();
plugin.onPause(multitasking);
}
}
/**
* Called when the activity will start interacting with the user.
*
* @param multitasking Flag indicating if multitasking is turned on for app
*/
public void onResume() {
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
public void onResume(boolean multitasking) {
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
while(it.hasNext()) {
Entry<String,Plugin> entry = it.next();
Plugin plugin = entry.getValue();
plugin.onResume();
Entry<String,IPlugin> entry = it.next();
IPlugin plugin = entry.getValue();
plugin.onResume(multitasking);
}
}
@@ -258,12 +277,32 @@ public final class PluginManager {
* The final call you receive before your activity is destroyed.
*/
public void onDestroy() {
java.util.Set<Entry<String,Plugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,Plugin>> it = s.iterator();
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
while(it.hasNext()) {
Entry<String,Plugin> entry = it.next();
Plugin plugin = entry.getValue();
Entry<String,IPlugin> entry = it.next();
IPlugin plugin = entry.getValue();
plugin.onDestroy();
}
}
/**
* Called when the activity receives a new intent.
*/
public void onNewIntent(Intent intent) {
java.util.Set<Entry<String,IPlugin>> s = this.plugins.entrySet();
java.util.Iterator<Entry<String,IPlugin>> it = s.iterator();
while(it.hasNext()) {
Entry<String,IPlugin> entry = it.next();
IPlugin plugin = entry.getValue();
plugin.onNewIntent(intent);
}
}
private void pluginConfigurationMissing() {
System.err.println("=====================================================================================");
System.err.println("ERROR: plugin.xml is missing. Add res/xml/plugins.xml to your project.");
System.err.println("https://raw.github.com/phonegap/phonegap-android/master/framework/res/xml/plugins.xml");
System.err.println("=====================================================================================");
}
}

View File

@@ -10,10 +10,13 @@ package com.phonegap.api;
import org.json.JSONArray;
import org.json.JSONObject;
import android.util.Log;
public class PluginResult {
private final int status;
private final String message;
private boolean keepCallback = false;
private String cast = null;
public PluginResult(Status status) {
this.status = status.ordinal();
@@ -22,7 +25,19 @@ public class PluginResult {
public PluginResult(Status status, String message) {
this.status = status.ordinal();
this.message = "'" + message + "'";
this.message = JSONObject.quote(message);
}
public PluginResult(Status status, JSONArray message, String cast) {
this.status = status.ordinal();
this.message = message.toString();
this.cast = cast;
}
public PluginResult(Status status, JSONObject message, String cast) {
this.status = status.ordinal();
this.message = message.toString();
this.cast = cast;
}
public PluginResult(Status status, JSONArray message) {
@@ -71,7 +86,15 @@ public class PluginResult {
}
public String toSuccessCallbackString(String callbackId) {
return "PhoneGap.callbackSuccess('"+callbackId+"', " + this.getJSONString() + " );";
StringBuffer buf = new StringBuffer("");
if (cast != null) {
buf.append("var temp = "+cast+"("+this.getJSONString() + ");\n");
buf.append("PhoneGap.callbackSuccess('"+callbackId+"',temp);");
}
else {
buf.append("PhoneGap.callbackSuccess('"+callbackId+"',"+this.getJSONString()+");");
}
return buf.toString();
}
public String toErrorCallbackString(String callbackId) {

View File

@@ -0,0 +1,9 @@
package com.phonegap.file;
public class EncodingException extends Exception {
public EncodingException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,9 @@
package com.phonegap.file;
public class FileExistsException extends Exception {
public FileExistsException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,9 @@
package com.phonegap.file;
public class InvalidModificationException extends Exception {
public InvalidModificationException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,9 @@
package com.phonegap.file;
public class NoModificationAllowedException extends Exception {
public NoModificationAllowedException(String message) {
super(message);
}
}

View File

@@ -0,0 +1,9 @@
package com.phonegap.file;
public class TypeMismatchException extends Exception {
public TypeMismatchException(String message) {
super(message);
}
}

63
lib/classic.rb Normal file → Executable file
View File

@@ -5,7 +5,7 @@ class Classic
@android_sdk_path, @name, @pkg, @www, @path = a
build
end
def build
setup
clobber
@@ -16,23 +16,29 @@ class Classic
copy_libs
add_name_to_strings
write_java
end
end
def setup
@android_dir = File.expand_path(File.dirname(__FILE__).gsub('lib',''))
@android_dir = File.expand_path(File.dirname(__FILE__).gsub(/lib$/,''))
@framework_dir = File.join(@android_dir, "framework")
@icon = File.join(@www, 'icon.png')
@icon = File.join(@www, 'icon.png') unless !@icon.nil? && File.exists?(@icon)
# Hash that stores the location of icons for each resolution type. Uses the default icon for all resolutions as a baseline.
@icons = {
:"drawable-ldpi" => @icon,
:"drawable-mdpi" => @icon,
:"drawable-hdpi" => @icon
} if @icons.nil?
@app_js_dir = ''
@content = 'index.html'
end
# replaces @path with new android project
def clobber
FileUtils.rm_r(@path) if File.exists? @path
FileUtils.mkdir_p @path
end
# removes local.properties and recreates based on android_sdk_path
# removes local.properties and recreates based on android_sdk_path
# then generates framework/phonegap.jar
def build_jar
%w(local.properties phonegap.js phonegap.jar).each do |f|
@@ -40,7 +46,7 @@ class Classic
end
open(File.join(@framework_dir, "local.properties"), 'w') do |f|
f.puts "sdk.dir=#{ @android_sdk_path }"
end
end
Dir.chdir(@framework_dir)
`ant jar`
Dir.chdir(@android_dir)
@@ -49,9 +55,9 @@ class Classic
# runs android create project
# TODO need to allow more flexible SDK targetting via config.xml
def create_android
IO.popen("android list targets") { |f|
IO.popen("android list targets") { |f|
targets = f.readlines(nil)[0].scan(/id\:.*$/)
if (targets.length > 0)
if (targets.length > 0)
target_id = targets.last.match(/\d+/).to_a.first
`android create project -t #{ target_id } -k #{ @pkg } -a #{ @name } -n #{ @name } -p #{ @path }`
else
@@ -60,7 +66,7 @@ class Classic
end
}
end
# copies the project/www folder into tmp/android/www
def include_www
FileUtils.mkdir_p File.join(@path, "assets", "www")
@@ -82,25 +88,37 @@ class Classic
# copies stuff from src directory into the android project directory (@path)
def copy_libs
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.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|
FileUtils.cp File.join(framework_res_dir, "layout", f), File.join(app_res_dir, "layout", f)
end
# icon file copy
# if it is not in the www directory use the default one in the src dir
@icon = File.join(framework_res_dir, "drawable", "icon.png") unless File.exists?(@icon)
%w(drawable-hdpi drawable-ldpi drawable-mdpi).each do |e|
# if specific resolution icons are specified, use those. if not, see if a general purpose icon was defined.
# finally, fall back to using the default PhoneGap one.
currentIcon = ""
if !@icons[e.to_sym].nil? && File.exists?(File.join(@www, @icons[e.to_sym]))
currentIcon = File.join(@www, @icons[e.to_sym])
elsif File.exists?(@icon)
currentIcon = @icon
else
currentIcon = File.join(framework_res_dir, "drawable", "icon.png")
end
FileUtils.mkdir_p(File.join(app_res_dir, e))
FileUtils.cp(@icon, File.join(app_res_dir, e, "icon.png"))
FileUtils.cp(currentIcon, File.join(app_res_dir, e, "icon.png"))
end
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
js_dir = File.join(@framework_dir, "assets", "js")
@@ -110,9 +128,9 @@ class Classic
phonegapjs << IO.read(File.join(js_dir, script))
phonegapjs << "\n\n"
end
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap.js"), 'w') {|f| f.write(phonegapjs) }
File.open(File.join(@path, "assets", "www", @app_js_dir, "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
end
# puts app name in strings
def add_name_to_strings
x = "<?xml version=\"1.0\" encoding=\"utf-8\"?>
@@ -123,11 +141,10 @@ class Classic
"
open(File.join(@path, "res", "values", "strings.xml"), 'w') do |f|
f.puts x.gsub(' ','')
end
end
end
end
# this is so fucking unholy yet oddly beautiful
# not sure if I should thank Ruby or apologize for this abusive use of string interpolation
# create java source file
def write_java
j = "
package #{ @pkg };
@@ -150,7 +167,7 @@ class Classic
FileUtils.mkdir_p(code_dir)
open(File.join(code_dir, "#{ @name }.java"),'w') { |f| f.puts j }
end
# friendly output for now
def msg
puts "Created #{ @path }"

View File

@@ -1,5 +1,5 @@
# Create
#
#
# Generates an Android project from a valid WWW directory and puts it in ../[PROJECT NAME]_android
#
class Create < Classic
@@ -8,65 +8,90 @@ class Create < Classic
read_config
build
end
def guess_paths(path)
# if no path is supplied uses current directory for project
path = FileUtils.pwd if path.nil?
# if a www is found use it for the project
path = File.join(path, 'www') if File.exists? File.join(path, 'www')
# defaults
@name = path.split("/").last.gsub('-','').gsub(' ','') # no dashses nor spaces
@path = File.join(path, '..', "#{ @name }_android")
@www = path
@pkg = "com.phonegap.#{ @name }"
@www = path
@pkg = "com.phonegap.#{ @name }"
@android_sdk_path = Dir.getwd[0,1] != "/" ? `android-sdk-path.bat android.bat`.gsub('\\tools','').gsub('\\', '\\\\\\\\') : `which android`.gsub(/\/tools\/android$/,'').chomp
@android_dir = File.expand_path(File.dirname(__FILE__).gsub('lib',''))
@framework_dir = File.join(@android_dir, "framework")
@icon = File.join(@www, 'icon.png')
@app_js_dir = ''
@content = 'index.html'
# stop executation on errors
raise 'Expected index.html in the following folder #{ path }.\nThe path is expected to be the directory droidgap create is run from or specified as a command line arg like droidgap create my_path.' unless File.exists? File.join(path, 'index.html')
raise "Expected index.html in the following folder #{ path }.\nThe path is expected to be the directory droidgap create is run from or specified as a command line arg like droidgap create my_path." unless File.exists? File.join(path, 'index.html')
raise 'Could not find android in your PATH!' if @android_sdk_path.empty?
end
# reads in a config.xml file
def read_config
config_file = File.join(@www, 'config.xml')
if File.exists?(config_file)
require 'rexml/document'
f = File.new config_file
doc = REXML::Document.new(f)
@config = {}
doc = REXML::Document.new(f)
@config = {}
@config[:id] = doc.root.attributes["id"]
@config[:version] = doc.root.attributes["version"]
@config[:icons] = {}
defaultIconSize = 0
doc.root.elements.each do |n|
@config[:name] = n.text.gsub('-','').gsub(' ','') if n.name == 'name'
@config[:description] = n.text if n.name == 'description'
@config[:icon] = n.attributes["src"] if n.name == 'icon'
@config[:content] = n.attributes["src"] if n.name == 'content'
@config[:name] = n.text.gsub('-','').gsub(' ','') if n.name == 'name'
@config[:description] = n.text if n.name == 'description'
@config[:content] = n.attributes["src"] if n.name == 'content'
if n.name == 'icon'
if n.attributes["width"] == '72' && n.attributes["height"] == '72'
@config[:icons]["drawable-hdpi".to_sym] = n.attributes["src"]
if 72 > defaultIconSize
@config[:icon] = n.attributes["src"]
defaultIconSize = 72
end
elsif n.attributes["width"] == '48' && n.attributes["height"] == '48'
@config[:icons]["drawable-mdpi".to_sym] = n.attributes["src"]
if 48 > defaultIconSize
@config[:icon] = n.attributes["src"]
defaultIconSize = 48
end
elsif n.attributes["width"] == '36' && n.attributes["height"] == '36'
@config[:icons]["drawable-ldpi".to_sym] = n.attributes["src"]
if 36 > defaultIconSize
@config[:icon] = n.attributes["src"]
defaultIconSize = 36
end
else
@config[:icon] = n.attributes["src"]
end
end
if n.name == "preference" && n.attributes["name"] == 'javascript_folder'
@config[:js_dir] = n.attributes["value"]
end
end
end
end
# extract android specific stuff
@config[:versionCode] = doc.elements["//android:versionCode"] ? doc.elements["//android:versionCode"].text : 3
@config[:minSdkVersion] = doc.elements["//android:minSdkVersion"] ? doc.elements["//android:minSdkVersion"].text : 1
# will change the name from the directory to the name element text
@name = @config[:name] if @config[:name]
# set the icon from the config
@icon = File.join(@www, @config[:icon])
@icon = File.join(@www, @config[:icon]) if @config[:icon]
@icons = @config[:icons] if @config[:icons].length > 0
# sets the app js dir where phonegap.js gets copied
@app_js_dir = @config[:js_dir] ? @config[:js_dir] : ''
# sets the start page
@content = @config[:content] ? @config[:content] : 'index.html'
end
end
end
end
end

19
lib/update.rb Normal file → Executable file
View File

@@ -15,9 +15,9 @@ class Update
end
# removes local.properties and recreates based on android_sdk_path
# then generates framework/phonegap.jar
# then generates framework/phonegap.jar & framework/assets/www/phonegap.js
def build_jar
puts "Building the JAR..."
puts "Building the JAR and combining JS files..."
%w(local.properties phonegap.js phonegap.jar).each do |f|
FileUtils.rm File.join(@framework_dir, f) if File.exists? File.join(@framework_dir, f)
end
@@ -32,23 +32,20 @@ class Update
# copies stuff from framework into the project
# TODO need to allow for www import inc icon
def copy_libs
puts "Copying over libraries and assets and creating phonegap.js..."
FileUtils.mkdir_p File.join(@path, "libs")
FileUtils.cp File.join(@framework_dir, "phonegap.jar"), File.join(@path, "libs")
puts "Copying over libraries and assets..."
version = IO.read(File.join(@framework_dir, '../VERSION'))
# concat JS and put into www folder.
FileUtils.cp File.join(@framework_dir, "phonegap-#{ version }.jar"), File.join(@path, "libs")
# concat JS and put into www folder. this can be overridden in the config.xml via @app_js_dir
js_dir = File.join(@framework_dir, "assets", "js")
phonegapjs = IO.read(File.join(js_dir, 'phonegap.js.base'))
Dir.new(js_dir).entries.each do |script|
next if script[0].chr == "." or script == "phonegap.js.base"
phonegapjs << IO.read(File.join(js_dir, script))
phonegapjs << "\n\n"
end
File.open(File.join(@path, "assets", "www", "phonegap.js"), 'w') {|f| f.write(phonegapjs) }
File.open(File.join(@path, "assets", "www", "phonegap-#{ version }.js"), 'w') {|f| f.write(phonegapjs) }
end
#
end