Compare commits

...

76 Commits

Author SHA1 Message Date
Carlos Santana
37dd471d0e CB-10820 Updated version and RELEASENOTES.md for release 2.1.1 2016-03-09 22:11:36 -05:00
Richard Knoll
a19c75253a CB-10825 android: Always request READ permission for gallery source
This closes #191
2016-03-09 17:56:20 -08:00
Carlos Santana
c20e031d42 added apache license header to appium files 2016-03-08 22:21:47 -05:00
Dmitry Blotsky
68a1150939 CB-10720 Fixed spelling, capitalization, and other small issues. 2016-03-03 15:21:40 -08:00
Raghav Katyal
9c906b2ab7 CB-10414: Adding focus handler to resume video when user comes back on leaving the app while preview was running 2016-03-02 14:15:01 -08:00
Alexander Sorokin
56b9469313 Appium tests: adjust swipe distance on Android 2016-03-02 17:39:59 +03:00
Alexander Sorokin
d51e23ad7b CB-10750 Appium tests: fail fast if session is irrecoverable 2016-03-02 13:23:51 +03:00
Raghav Katyal
2cd2528d1c Adding missing semi colon 2016-02-29 10:30:40 -08:00
Raghav Katyal
5f7f4f3e55 Adding focus handler to make sure filepicker gets launched when app is active 2016-02-26 14:15:27 -08:00
Miguel Revetria
20dcaf2cb3 CB-10128: [iOS] Fixed how checks access authorization to camera & library. This closes #146 2016-02-24 16:10:20 -08:00
daserge
b16c5234d5 CB-10636 Add JSHint for plugins 2016-02-24 17:14:58 +03:00
Alexander Sorokin
c1948fc0d4 CB-10639 Appium tests: Added some timeouts,
Taking a screenshot on failure,
Retry taking a picture up to 3 times,
Try to restart the Appium session if it's lost
2016-02-24 16:44:29 +03:00
Dmitry Blotsky
f792aaacc3 CB-10552 Replacing images in README.md. 2016-02-22 12:06:29 -08:00
Julio César
16636d18f2 Added a lot of more cases to get the real path
Added a lot of more cases to get the real path

This closes #175
2016-02-22 11:54:22 -08:00
Julio César
019346d188 Fix for CB-10625
getDocumentId was crashing in some cases.
Now, in case it crashes, it will use the original uri to query.
2016-02-22 11:23:53 -08:00
Alexander Sorokin
61b77951e1 CB-10619 Appium tests: Properly switch to webview 2016-02-16 16:28:43 +03:00
Alexander Sorokin
a060fb36f3 CB-10397 Added Appium tests 2016-02-12 14:13:46 +03:00
Sarangan Rajamanickam
77653183dd CB-10576: MobileSpec can't get results for Windows-Store 8.1 Builds
Fixing a minor syntax issue
2016-02-09 16:17:17 -08:00
t1st3
df734a522c chore: edit package.json license to match SPDX id
See [NPM package.json spec for licenses](https://docs.npmjs.com/files/package.json#license) and [SPDX license IDs](https://spdx.org/licenses/)

X-ref: https://github.com/apache/cordova-plugin-device/pull/48
2016-02-06 11:16:08 +01:00
Raghav Katyal
654286d373 CB-10539: Commenting out the verySmallQvga maxResolution option 2016-02-05 15:03:19 -08:00
Raghav Katyal
76ad059c9c CB-10541: Changing default maxResoltion to be highestAvailable for CameraCaptureUI 2016-02-04 12:14:16 -08:00
Laurent Deketelaere
5b38453262 CB-10113 Browser - Layer camera UI on top of all!
Adds CSS style {position: 'relative', z-index: 2147483647} (2147483647 is the highest possible z-index) on DOM appended elements.

This closes #134
2016-02-02 17:02:15 +10:00
Tim Barham
e48a7e5c5c CB-10502 Fix camera plugin exception in Chrome when click capture.
The MediaStream.stop() method has been deprecated as of Chrome 47. We were using it, and it was generating an exception.

If stop() method is not found, instead stop each individual track (the new way of doing it).
2016-02-02 16:31:39 +10:00
Raghav Katyal
06fcbf05a2 Adding comments 2016-01-21 14:11:59 -08:00
PerfectionCSGO
9a9081b0d4 Camera tapping fix 2016-01-21 14:07:51 -08:00
Steve Gill
6f7ce333cc CB-10368 Incremented plugin version. 2016-01-15 16:58:32 -08:00
Steve Gill
0f32b78c82 CB-10368 Updated version and RELEASENOTES.md for release 2.1.0 2016-01-15 16:35:05 -08:00
Steve Gill
eb009471ab added .ratignore 2016-01-15 15:09:14 -08:00
riknoll
1d32ea46f0 CB-10319 android: Adding reflective helper methods for permission requests 2016-01-12 17:42:29 -08:00
riknoll
e2193631d5 CB-9189 android: Implementing save/restore API to handle Activity destruction 2016-01-05 14:18:43 -08:00
Shazron Abdullah
e8fa1695c4 CB-10241 - App Crash cause by Camera Plugin ios 7 2015-12-22 17:54:07 -08:00
Steve Gill
e1911a3c78 CB-10035 Incremented plugin version. 2015-11-30 17:57:07 -08:00
Raghav Katyal
ef5484a2aa CB-8940 Setting z-index values to maximum for UI buttons. This closes #140. 2015-11-19 17:36:06 -08:00
Steve Gill
b3376e2389 CB-10035 linked issues in RELEASENOTES.md 2015-11-18 22:11:41 -08:00
Steve Gill
ee9884e97b CB-10035 Updated version and RELEASENOTES.md for release 2.0.0 2015-11-18 19:45:05 -08:00
Steve Gill
1cf4feee60 removed r prefix from tags 2015-11-18 14:53:10 -08:00
Steve Gill
847add6870 CB-10035 Updated RELEASENOTES to be newest to oldest 2015-11-18 13:54:08 -08:00
Jesse MacFadyen
63ac782b32 remove unneeded weakness 2015-11-17 16:56:35 -08:00
Jesse MacFadyen
7f3a0a5483 CB-8863 correct block usage for async calls 2015-11-17 16:37:45 -08:00
riknoll
036cdfdeb8 CB-5479 android: changed saveToPhotoAlbum to save uncompressed images 2015-11-17 12:03:30 -08:00
riknoll
2bb134bf6b CB-9169 android: Fixed filetype for uncompressed images and added quirk 2015-11-10 17:26:07 -08:00
Joe Bowser
88592575fe CB-9446 related: Removing CordovaResource library code in favour of the code we're supposed to be deprecating because that at least works. 2015-11-09 14:28:58 -08:00
daserge
f5217bf02b CB-9942 Normalize line endings in Camera plugin docs
Adds .gitattributes to ensure LF line endings in the repo
2015-11-04 12:01:21 +03:00
riknoll
9b444c39ba CB-9910 android: Add permission request for some gallery requests 2015-11-02 15:25:33 -08:00
Richard Knoll
5ff225d8df CB-7668 android: Adding a sterner warning for allowedit on Android 2015-10-29 14:35:58 -07:00
Dmitry Blotsky
3a8880d0c3 Actually fixing the contribute link. 2015-10-23 12:11:49 -07:00
Dmitry Blotsky
d0ad2f5065 Fixing contribute link. 2015-10-23 12:03:42 -07:00
Joe Bowser
44475d9df9 Using the CordovaResourceApi to fine paths of files in the background thread. If the file doesn't exist, return the content URI
We also do a refactor to bring this in line.  This code got bike-shedded
a bit.
2015-10-22 16:19:13 -07:00
Joe Bowser
2714060b09 Add engine tag for Cordova-Android 5.0.x 2015-10-22 16:19:13 -07:00
Joe Bowser
cbe17eec21 Fix permission handling 2015-10-22 16:19:12 -07:00
Joe Bowser
8024c5de49 CB-9583: Permissions for Marshmallow 2015-10-22 16:19:12 -07:00
Vladimir Kotikov
933b35b0d2 Closes stale pull requests. Close #91, close #82, close #59, close #20 2015-10-19 13:10:04 +03:00
Nelson Antunes
dbe3e3d2ca Try to use realpath filename instead of default modified.jpg 2015-10-15 01:21:05 +01:00
Jesse MacFadyen
a030c52f7f change 'case' to else-if. This closes #113 2015-10-14 18:08:01 +03:00
Julio César
b8a68af63f CB-6190 - iOS camera plugin ignores quality parameter
CB-6190 - iOS camera plugin ignores quality parameter in some
circunstances
Added a check to not downscale if quality is 100 or sourceType !=
UIImagePickerControllerSourceTypeCamera (according to the docs, images
from gallery aren’t downscaled). This closes #108
2015-10-14 17:09:44 +03:00
sgrebnov
837d6721f2 CB-9633 iOS Taking a Picture With Option destinationType:NATIVE_URI Doesn't Show Image
This closes #122
2015-10-14 16:19:48 +03:00
daserge
f20dfae735 CB-9745 Camera plugin docs should be generated from the source
Moved docs to jsdocs comments in the code, removed redundancy.
Moved Quirks and extra examples to errata sections of the docs TEMPLATE.md, linked the jsdoc comments to these sections.
Added dependencies to common plugin docs template and husky (precommit hook to regen the docs automatically).
Added gen-docs command to re-generate the docs.
Extended TOC manually [to include classes containing only one member](https://github.com/jsdoc2md/jsdoc-to-markdown/issues/34) (CameraPopoverHandle and CameraPopoverOptions).
Extended the docs via DMD template to include "Installation", "How to contribute" and "Docs are generated" warning.

github: close #129
2015-10-12 12:42:47 +03:00
sgrebnov
454a6f518c CB-9622 Windows Phone 8 Camera Option destinationType:NATIVE_URI is a NO-OP
•	WP8 proxy now supports NATIVE_URI param
•	Treat NATIVE_URI same way as FILE_URI (similar to Android).

This closes #119
2015-09-09 13:01:48 +03:00
Vladimir Kotikov
dca8bd1943 CB-9623 Fixes various issues when encodingType set to png 2015-09-08 17:18:57 +03:00
Jakub Navrátil
cad9ab0419 CB-9591 Retaining aspect ratio when resizing 2015-09-01 10:49:12 -07:00
Murat Sutunc
ee5cfe89a5 CB-9443 Pick correct maxResolution
This closes #111, closes #56
2015-08-03 15:45:55 -07:00
Murat Sutunc
813d143667 CB-9151 Trigger captureAction only once 2015-08-03 10:09:15 -07:00
Murat Sutunc
110b3b3388 CB-9413 Close RandomAccessStream once copied 2015-07-27 11:39:38 -07:00
Andrey Kurdyumov
eeb5880580 CB-5661 Remove outdated iOS quirks about memory 2015-07-27 15:53:13 +06:00
Murat Sutunc
9dfd37cd66 Closing stale pull request: close #106 2015-07-13 16:02:12 -07:00
Gillardo
c50757c245 CB-9349 Focus control and nice UI
Removed old comment, move style text. close #106
2015-07-10 10:03:36 -07:00
Murat Sutunc
0264e56162 Closing stale pull request: close #73 2015-07-09 15:22:02 -07:00
Murat Sutunc
8ebaaa62bd Closing stale pull request: close #77 2015-07-09 15:19:19 -07:00
Jesse MacFadyen
11eaaa8b9f remove travis-ci integration 2015-07-07 17:36:10 -07:00
Joe Bowser
899802a202 CB-9259: Forgot to add another check on which URI we're using when fixing this thing the first time 2015-07-07 14:27:44 -07:00
Shazron Abdullah
1e607ddcc8 CB-9247 - typo 2015-06-25 06:22:24 -07:00
Shazron Abdullah
63110ea54c CB-9247 - Added macro to conditionally add NSData+Base64.h 2015-06-25 06:20:25 -07:00
Shazron Abdullah
b683315be6 CB-9247 - Fixes compilation errors with cordova-ios 4.x 2015-06-25 06:09:59 -07:00
Jesse MacFadyen
2e147ba9ca Merge branch 'CB-8498-take2' of https://github.com/vldmrrr/cordova-plugin-camera 2015-06-17 17:38:01 -07:00
Steve Gill
11498404fc CB-9192 Incremented plugin version. 2015-06-17 17:35:34 -07:00
vladimir
363dd02584 Fix returning native url on windows. 2015-06-17 16:14:58 -05:00
34 changed files with 3085 additions and 763 deletions

94
.gitattributes vendored Normal file
View File

@@ -0,0 +1,94 @@
* text eol=lf
# source code
*.php text
*.css text
*.sass text
*.scss text
*.less text
*.styl text
*.js text
*.coffee text
*.json text
*.htm text
*.html text
*.xml text
*.svg text
*.txt text
*.ini text
*.inc text
*.pl text
*.rb text
*.py text
*.scm text
*.sql text
*.sh text
*.bat text
# templates
*.ejs text
*.hbt text
*.jade text
*.haml text
*.hbs text
*.dot text
*.tmpl text
*.phtml text
# server config
.htaccess text
# git config
.gitattributes text
.gitignore text
.gitconfig text
# code analysis config
.jshintrc text
.jscsrc text
.jshintignore text
.csslintrc text
# misc config
*.yaml text
*.yml text
.editorconfig text
# build config
*.npmignore text
*.bowerrc text
# Heroku
Procfile text
.slugignore text
# Documentation
*.md text
LICENSE text
AUTHORS text
#
## These files are binary and should be left untouched
#
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.pyc binary
*.pdf binary

10
.gitignore vendored
View File

@@ -12,12 +12,4 @@ Thumbs.db
*.swp
*.user
/node_modules/**

16
.jshintrc Normal file
View File

@@ -0,0 +1,16 @@
{
"browser": true
, "devel": true
, "bitwise": true
, "undef": true
, "trailing": true
, "quotmark": false
, "indent": 4
, "unused": "vars"
, "latedef": "nofunc"
, "globals": {
"module": false,
"exports": false,
"require": false
}
}

1
.ratignore Normal file
View File

@@ -0,0 +1 @@
TEMPLATE.md

View File

@@ -1,13 +1,4 @@
language: objective-c
git:
depth: 2
language: node_js
sudo: false
node_js:
- "0.10"
install:
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
- cd ..
- npm install -g cordova-paramedic
- npm install -g cordova
- npm install -g ios-sim
script:
- cordova-paramedic --platform ios --plugin ${TRAVIS_BUILD_DIR}
- "4.2"

View File

@@ -27,7 +27,7 @@ There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/#contribute).
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Sign and submit an Apache ICLA (Contributor License Agreement).

545
README.md
View File

@@ -17,9 +17,9 @@
# under the License.
-->
# cordova-plugin-camera
[![Build Status](https://travis-ci.org/apache/cordova-plugin-camera.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-camera)
[![Build Status](https://travis-ci.org/apache/cordova-plugin-camera.svg)](https://travis-ci.org/apache/cordova-plugin-camera)
# cordova-plugin-camera
This plugin defines a global `navigator.camera` object, which provides an API for taking pictures and for choosing images from
the system's image library.
@@ -31,50 +31,93 @@ Although the object is attached to the global scoped `navigator`, it is not avai
console.log(navigator.camera);
}
## Installation
This requires cordova 5.0+
cordova plugin add cordova-plugin-camera
Older versions of cordova can still install via the __deprecated__ id
## API
- Camera
- navigator.camera.getPicture(success, fail, options)
- CameraOptions
- CameraPopoverHandle
- CameraPopoverOptions
- navigator.camera.cleanup
cordova plugin add org.apache.cordova.camera
It is also possible to install via repo url directly ( unstable )
cordova plugin add https://github.com/apache/cordova-plugin-camera.git
## How to Contribute
## navigator.camera.getPicture
Contributors are welcome! And we need your contributions to keep the project moving forward. You can [report bugs](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Camera%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC), improve the documentation, or [contribute code](https://github.com/apache/cordova-plugin-camera/pulls).
There is a specific [contributor workflow](http://wiki.apache.org/cordova/ContributorWorkflow) we recommend. Start reading there. More information is available on [our wiki](http://wiki.apache.org/cordova).
:warning: **Found an issue?** File it on [JIRA issue tracker](https://issues.apache.org/jira/issues/?jql=project%20%3D%20CB%20AND%20status%20in%20(Open%2C%20%22In%20Progress%22%2C%20Reopened)%20AND%20resolution%20%3D%20Unresolved%20AND%20component%20%3D%20%22Plugin%20Camera%22%20ORDER%20BY%20priority%20DESC%2C%20summary%20ASC%2C%20updatedDate%20DESC).
**Have a solution?** Send a [Pull Request](https://github.com/apache/cordova-plugin-camera/pulls).
In order for your changes to be accepted, you need to sign and submit an Apache [ICLA](http://www.apache.org/licenses/#clas) (Individual Contributor License Agreement). Then your name will appear on the list of CLAs signed by [non-committers](https://people.apache.org/committer-index.html#unlistedclas) or [Cordova committers](http://people.apache.org/committers-by-project.html#cordova).
**And don't forget to test and document your code.**
## This documentation is generated by a tool
:warning: Run `npm install` in the plugin repo to enable automatic docs generation if you plan to send a PR.
[jsdoc-to-markdown](https://www.npmjs.com/package/jsdoc-to-markdown) is used to generate the docs.
Documentation consists of template and API docs produced from the plugin JS code and should be regenerated before each commit (done automatically via [husky](https://github.com/typicode/husky), running `npm run gen-docs` script as a `precommit` hook - see `package.json` for details).
---
# API Reference
* [camera](#module_camera)
* [.getPicture(successCallback, errorCallback, options)](#module_camera.getPicture)
* [.cleanup()](#module_camera.cleanup)
* [.onError](#module_camera.onError) : <code>function</code>
* [.onSuccess](#module_camera.onSuccess) : <code>function</code>
* [.CameraOptions](#module_camera.CameraOptions) : <code>Object</code>
* [Camera](#module_Camera)
* [.DestinationType](#module_Camera.DestinationType) : <code>enum</code>
* [.EncodingType](#module_Camera.EncodingType) : <code>enum</code>
* [.MediaType](#module_Camera.MediaType) : <code>enum</code>
* [.PictureSourceType](#module_Camera.PictureSourceType) : <code>enum</code>
* [.PopoverArrowDirection](#module_Camera.PopoverArrowDirection) : <code>enum</code>
* [.Direction](#module_Camera.Direction) : <code>enum</code>
* [CameraPopoverHandle](#module_CameraPopoverHandle)
* [CameraPopoverOptions](#module_CameraPopoverOptions)
---
<a name="module_camera"></a>
## camera
<a name="module_camera.getPicture"></a>
### camera.getPicture(successCallback, errorCallback, options)
Takes a photo using the camera, or retrieves a photo from the device's
image gallery. The image is passed to the success callback as a
base64-encoded `String`, or as the URI for the image file. The method
itself returns a `CameraPopoverHandle` object that can be used to
reposition the file selection popover.
navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
#### Description
Base64-encoded `String`, or as the URI for the image file.
The `camera.getPicture` function opens the device's default camera
application that allows users to snap pictures. This behavior occurs
by default, when `Camera.sourceType` equals
`Camera.PictureSourceType.CAMERA`. Once the user snaps the photo, the
camera application closes and the application is restored.
application that allows users to snap pictures by default - this behavior occurs,
when `Camera.sourceType` equals [`Camera.PictureSourceType.CAMERA`](#module_Camera.PictureSourceType).
Once the user snaps the photo, the camera application closes and the application is restored.
If `Camera.sourceType` is `Camera.PictureSourceType.PHOTOLIBRARY` or
`Camera.PictureSourceType.SAVEDPHOTOALBUM`, then a dialog displays
that allows users to select an existing image. The
`camera.getPicture` function returns a `CameraPopoverHandle` object,
`camera.getPicture` function returns a [`CameraPopoverHandle`](#module_CameraPopoverHandle) object,
which can be used to reposition the image selection dialog, for
example, when the device orientation changes.
The return value is sent to the `cameraSuccess` callback function, in
The return value is sent to the [`cameraSuccess`](#module_camera.onSuccess) callback function, in
one of the following formats, depending on the specified
`cameraOptions`:
- A `String` containing the base64-encoded photo image.
- A `String` containing the Base64-encoded photo image.
- A `String` representing the image file location on local storage (default).
@@ -93,13 +136,234 @@ quality, even if a `quality` parameter is specified. To avoid common
memory problems, set `Camera.destinationType` to `FILE_URI` rather
than `DATA_URL`.
#### Supported Platforms
__Supported Platforms__
![](doc/img/android-success.png) ![](doc/img/blackberry-success.png) ![](doc/img/browser-success.png) ![](doc/img/firefox-success.png) ![](doc/img/fireos-success.png) ![](doc/img/ios-success.png) ![](doc/img/windows-success.png) ![](doc/img/wp8-success.png) ![](doc/img/ubuntu-success.png)
- Android
- BlackBerry
- Browser
- Firefox
- FireOS
- iOS
- Windows
- WP8
- Ubuntu
#### Example
More examples [here](#camera-getPicture-examples). Quirks [here](#camera-getPicture-quirks).
Take a photo and retrieve it as a base64-encoded image:
**Kind**: static method of <code>[camera](#module_camera)</code>
| Param | Type | Description |
| --- | --- | --- |
| successCallback | <code>[onSuccess](#module_camera.onSuccess)</code> | |
| errorCallback | <code>[onError](#module_camera.onError)</code> | |
| options | <code>[CameraOptions](#module_camera.CameraOptions)</code> | CameraOptions |
**Example**
```js
navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
```
<a name="module_camera.cleanup"></a>
### camera.cleanup()
Removes intermediate image files that are kept in temporary storage
after calling [`camera.getPicture`](#module_camera.getPicture). Applies only when the value of
`Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the
`Camera.destinationType` equals `Camera.DestinationType.FILE_URI`.
__Supported Platforms__
- iOS
**Kind**: static method of <code>[camera](#module_camera)</code>
**Example**
```js
navigator.camera.cleanup(onSuccess, onFail);
function onSuccess() {
console.log("Camera cleanup success.")
}
function onFail(message) {
alert('Failed because: ' + message);
}
```
<a name="module_camera.onError"></a>
### camera.onError : <code>function</code>
Callback function that provides an error message.
**Kind**: static typedef of <code>[camera](#module_camera)</code>
| Param | Type | Description |
| --- | --- | --- |
| message | <code>string</code> | The message is provided by the device's native code. |
<a name="module_camera.onSuccess"></a>
### camera.onSuccess : <code>function</code>
Callback function that provides the image data.
**Kind**: static typedef of <code>[camera](#module_camera)</code>
| Param | Type | Description |
| --- | --- | --- |
| imageData | <code>string</code> | Base64 encoding of the image data, _or_ the image file URI, depending on [`cameraOptions`](#module_camera.CameraOptions) in effect. |
**Example**
```js
// Show image
//
function cameraCallback(imageData) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + imageData;
}
```
<a name="module_camera.CameraOptions"></a>
### camera.CameraOptions : <code>Object</code>
Optional parameters to customize the camera settings.
* [Quirks](#CameraOptions-quirks)
**Kind**: static typedef of <code>[camera](#module_camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| quality | <code>number</code> | <code>50</code> | Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. (Note that information about the camera's resolution is unavailable.) |
| destinationType | <code>[DestinationType](#module_Camera.DestinationType)</code> | <code>FILE_URI</code> | Choose the format of the return value. |
| sourceType | <code>[PictureSourceType](#module_Camera.PictureSourceType)</code> | <code>CAMERA</code> | Set the source of the picture. |
| allowEdit | <code>Boolean</code> | <code>true</code> | Allow simple editing of image before selection. |
| encodingType | <code>[EncodingType](#module_Camera.EncodingType)</code> | <code>JPEG</code> | Choose the returned image file's encoding. |
| targetWidth | <code>number</code> | | Width in pixels to scale image. Must be used with `targetHeight`. Aspect ratio remains constant. |
| targetHeight | <code>number</code> | | Height in pixels to scale image. Must be used with `targetWidth`. Aspect ratio remains constant. |
| mediaType | <code>[MediaType](#module_Camera.MediaType)</code> | <code>PICTURE</code> | Set the type of media to select from. Only works when `PictureSourceType` is `PHOTOLIBRARY` or `SAVEDPHOTOALBUM`. |
| correctOrientation | <code>Boolean</code> | | Rotate the image to correct for the orientation of the device during capture. |
| saveToPhotoAlbum | <code>Boolean</code> | | Save the image to the photo album on the device after capture. |
| popoverOptions | <code>[CameraPopoverOptions](#module_CameraPopoverOptions)</code> | | iOS-only options that specify popover location in iPad. |
| cameraDirection | <code>[Direction](#module_Camera.Direction)</code> | <code>BACK</code> | Choose the camera to use (front- or back-facing). |
---
<a name="module_Camera"></a>
## Camera
<a name="module_Camera.DestinationType"></a>
### Camera.DestinationType : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| DATA_URL | <code>number</code> | <code>0</code> | Return base64 encoded string |
| FILE_URI | <code>number</code> | <code>1</code> | Return file uri (content://media/external/images/media/2 for Android) |
| NATIVE_URI | <code>number</code> | <code>2</code> | Return native uri (eg. asset-library://... for iOS) |
<a name="module_Camera.EncodingType"></a>
### Camera.EncodingType : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| JPEG | <code>number</code> | <code>0</code> | Return JPEG encoded image |
| PNG | <code>number</code> | <code>1</code> | Return PNG encoded image |
<a name="module_Camera.MediaType"></a>
### Camera.MediaType : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| PICTURE | <code>number</code> | <code>0</code> | Allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType |
| VIDEO | <code>number</code> | <code>1</code> | Allow selection of video only, ONLY RETURNS URL |
| ALLMEDIA | <code>number</code> | <code>2</code> | Allow selection from all media types |
<a name="module_Camera.PictureSourceType"></a>
### Camera.PictureSourceType : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| PHOTOLIBRARY | <code>number</code> | <code>0</code> | Choose image from picture library (same as SAVEDPHOTOALBUM for Android) |
| CAMERA | <code>number</code> | <code>1</code> | Take picture from camera |
| SAVEDPHOTOALBUM | <code>number</code> | <code>2</code> | Choose image from picture library (same as PHOTOLIBRARY for Android) |
<a name="module_Camera.PopoverArrowDirection"></a>
### Camera.PopoverArrowDirection : <code>enum</code>
Matches iOS UIPopoverArrowDirection constants to specify arrow location on popover.
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default |
| --- | --- | --- |
| ARROW_UP | <code>number</code> | <code>1</code> |
| ARROW_DOWN | <code>number</code> | <code>2</code> |
| ARROW_LEFT | <code>number</code> | <code>4</code> |
| ARROW_RIGHT | <code>number</code> | <code>8</code> |
| ARROW_ANY | <code>number</code> | <code>15</code> |
<a name="module_Camera.Direction"></a>
### Camera.Direction : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| BACK | <code>number</code> | <code>0</code> | Use the back-facing camera |
| FRONT | <code>number</code> | <code>1</code> | Use the front-facing camera |
---
<a name="module_CameraPopoverOptions"></a>
## CameraPopoverOptions
iOS-only parameters that specify the anchor element location and arrow
direction of the popover when selecting images from an iPad's library
or album.
Note that the size of the popover may change to adjust to the
direction of the arrow and orientation of the screen. Make sure to
account for orientation changes when specifying the anchor element
location.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [x] | <code>Number</code> | <code>0</code> | x pixel coordinate of screen element onto which to anchor the popover. |
| [y] | <code>Number</code> | <code>32</code> | y pixel coordinate of screen element onto which to anchor the popover. |
| [width] | <code>Number</code> | <code>320</code> | width, in pixels, of the screen element onto which to anchor the popover. |
| [height] | <code>Number</code> | <code>480</code> | height, in pixels, of the screen element onto which to anchor the popover. |
| [arrowDir] | <code>[PopoverArrowDirection](#module_Camera.PopoverArrowDirection)</code> | <code>ARROW_ANY</code> | Direction the arrow on the popover should point. |
---
<a name="module_CameraPopoverHandle"></a>
## CameraPopoverHandle
A handle to an image picker popover.
__Supported Platforms__
- iOS
**Example**
```js
var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail,
{
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
popoverOptions: new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY)
});
// Reposition the popover if the orientation changes.
window.onorientationchange = function() {
var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY);
cameraPopoverHandle.setPosition(cameraPopoverOptions);
}
```
---
## `camera.getPicture` Errata
#### Example <a name="camera-getPicture-examples"></a>
Take a photo and retrieve it as a Base64-encoded image:
navigator.camera.getPicture(onSuccess, onFail, { quality: 50,
destinationType: Camera.DestinationType.DATA_URL
@@ -134,25 +398,30 @@ Take a photo and retrieve the image's file location:
<preference name="CameraUsesGeolocation" value="false" />
#### Amazon Fire OS Quirks
#### Amazon Fire OS Quirks <a name="camera-getPicture-quirks"></a>
Amazon Fire OS uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the cordova activity is restored.
scenario, the image may not appear when the Cordova activity is restored.
#### Android Quirks
Android uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the Cordova activity is restored.
scenario, the result from the plugin call will be delivered via the resume event.
See [the Android Lifecycle guide][android_lifecycle]
for more information. The `pendingResult.result` value will contain the value that
would be passed to the callbacks (either the URI/URL or an error message). Check
the `pendingResult.pluginStatus` to determine whether or not the call was
successful.
#### Browser Quirks
Can only return photos as base64-encoded image.
Can only return photos as Base64-encoded image.
#### Firefox OS Quirks
Camera plugin is currently implemented using [Web Activities](https://hacks.mozilla.org/2013/01/introducing-web-activities/).
Camera plugin is currently implemented using [Web Activities][web_activities].
#### iOS Quirks
@@ -176,71 +445,8 @@ Tizen only supports a `destinationType` of
`Camera.DestinationType.FILE_URI` and a `sourceType` of
`Camera.PictureSourceType.PHOTOLIBRARY`.
## CameraOptions
Optional parameters to customize the camera settings.
{ quality : 75,
destinationType : Camera.DestinationType.DATA_URL,
sourceType : Camera.PictureSourceType.CAMERA,
allowEdit : true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 100,
targetHeight: 100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false };
- __quality__: Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. The default is 50. _(Number)_ (Note that information about the camera's resolution is unavailable.)
- __destinationType__: Choose the format of the return value. The default is FILE_URI. Defined in `navigator.camera.DestinationType` _(Number)_
Camera.DestinationType = {
DATA_URL : 0, // Return image as base64-encoded string
FILE_URI : 1, // Return image file URI
NATIVE_URI : 2 // Return image native URI (e.g., assets-library:// on iOS or content:// on Android)
};
- __sourceType__: Set the source of the picture. The default is CAMERA. Defined in `navigator.camera.PictureSourceType` _(Number)_
Camera.PictureSourceType = {
PHOTOLIBRARY : 0,
CAMERA : 1,
SAVEDPHOTOALBUM : 2
};
- __allowEdit__: Allow simple editing of image before selection. _(Boolean)_
- __encodingType__: Choose the returned image file's encoding. Default is JPEG. Defined in `navigator.camera.EncodingType` _(Number)_
Camera.EncodingType = {
JPEG : 0, // Return JPEG encoded image
PNG : 1 // Return PNG encoded image
};
- __targetWidth__: Width in pixels to scale image. Must be used with __targetHeight__. Aspect ratio remains constant. _(Number)_
- __targetHeight__: Height in pixels to scale image. Must be used with __targetWidth__. Aspect ratio remains constant. _(Number)_
- __mediaType__: Set the type of media to select from. Only works when `PictureSourceType` is `PHOTOLIBRARY` or `SAVEDPHOTOALBUM`. Defined in `nagivator.camera.MediaType` _(Number)_
Camera.MediaType = {
PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
VIDEO: 1, // allow selection of video only, WILL ALWAYS RETURN FILE_URI
ALLMEDIA : 2 // allow selection from all media types
};
- __correctOrientation__: Rotate the image to correct for the orientation of the device during capture. _(Boolean)_
- __saveToPhotoAlbum__: Save the image to the photo album on the device after capture. _(Boolean)_
- __popoverOptions__: iOS-only options that specify popover location in iPad. Defined in `CameraPopoverOptions`.
- __cameraDirection__: Choose the camera to use (front- or back-facing). The default is BACK. Defined in `navigator.camera.Direction` _(Number)_
Camera.Direction = {
BACK : 0, // Use the back-facing camera
FRONT : 1 // Use the front-facing camera
};
## `CameraOptions` Errata <a name="CameraOptions-quirks"></a>
#### Amazon Fire OS Quirks
@@ -254,11 +460,12 @@ Optional parameters to customize the camera settings.
- Any `cameraDirection` value results in a back-facing photo.
- Android also uses the Crop Activity for allowEdit, even though crop should work and actually pass the cropped image back to Cordova, the only one that works consistently is the one bundled
with the Google Plus Photos application. Other crops may not work.
- **`allowEdit` is unpredictable on Android and it should not be used!** The Android implementation of this plugin tries to find and use an application on the user's device to do image cropping. The plugin has no control over what application the user selects to perform the image cropping and it is very possible that the user could choose an incompatible option and cause the plugin to fail. This sometimes works because most devices come with an application that handles cropping in a way that is compatible with this plugin (Google Plus Photos), but it is unwise to rely on that being the case. If image editing is essential to your application, consider seeking a third party library or plugin that provides its own image editing utility for a more robust solution.
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
- Ignores the `encodingType` parameter if the image is unedited (i.e. `quality` is 100, `correctOrientation` is false, and no `targetHeight` or `targetWidth` are specified). The `CAMERA` source will always return the JPEG file given by the native camera and the `PHOTOLIBRARY` and `SAVEDPHOTOALBUM` sources will return the selected file in its existing encoding.
#### BlackBerry 10 Quirks
- Ignores the `quality` parameter.
@@ -293,10 +500,10 @@ with the Google Plus Photos application. Other crops may not work.
#### iOS Quirks
- Set `quality` below 50 to avoid memory errors on some devices.
- When using `destinationType.FILE_URI`, photos are saved in the application's temporary directory. The contents of the application's temporary directory is deleted when the application ends.
- When using `destinationType.NATIVE_URI` and `sourceType.CAMERA`, photos are saved in the saved photo album regardless on the value of `saveToPhotoAlbum` parameter.
#### Tizen Quirks
- options not supported
@@ -311,137 +518,11 @@ with the Google Plus Photos application. Other crops may not work.
- Ignores the `cameraDirection` parameter.
- Ignores the `saveToPhotoAlbum` parameter. IMPORTANT: All images taken with the wp7/8 cordova camera API are always copied to the phone's camera roll. Depending on the user's settings, this could also mean the image is auto-uploaded to their OneDrive. This could potentially mean the image is available to a wider audience than your app intended. If this a blocker for your application, you will need to implement the CameraCaptureTask as documented on msdn : [http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394006.aspx](http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394006.aspx)
You may also comment or up-vote the related issue in the [issue tracker](https://issues.apache.org/jira/browse/CB-2083)
- Ignores the `saveToPhotoAlbum` parameter. IMPORTANT: All images taken with the WP8/8 Cordova camera API are always copied to the phone's camera roll. Depending on the user's settings, this could also mean the image is auto-uploaded to their OneDrive. This could potentially mean the image is available to a wider audience than your app intended. If this is a blocker for your application, you will need to implement the CameraCaptureTask as [documented on MSDN][msdn_wp8_docs]. You may also comment or up-vote the related issue in the [issue tracker][wp8_bug].
- Ignores the `mediaType` property of `cameraOptions` as the Windows Phone SDK does not provide a way to choose videos from PHOTOLIBRARY.
## CameraError
onError callback function that provides an error message.
function(message) {
// Show a helpful message
}
#### Description
- __message__: The message is provided by the device's native code. _(String)_
## cameraSuccess
onSuccess callback function that provides the image data.
function(imageData) {
// Do something with the image
}
#### Description
- __imageData__: Base64 encoding of the image data, _or_ the image file URI, depending on `cameraOptions` in effect. _(String)_
#### Example
// Show image
//
function cameraCallback(imageData) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + imageData;
}
## CameraPopoverHandle
A handle to the popover dialog created by `navigator.camera.getPicture`.
#### Description
- __setPosition__: Set the position of the popover. Takes the `CameraPopoverOptions` that specify the new position.
#### Supported Platforms
![](doc/img/android-fail.png) ![](doc/img/blackberry-fail.png) ![](doc/img/browser-fail.png) ![](doc/img/firefox-fail.png) ![](doc/img/fireos-fail.png) ![](doc/img/ios-success.png) ![](doc/img/windows-fail.png) ![](doc/img/wp8-fail.png) ![](doc/img/ubuntu-fail.png)
#### Example
var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail,
{ destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
popoverOptions: new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY)
});
// Reposition the popover if the orientation changes.
window.onorientationchange = function() {
var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY);
cameraPopoverHandle.setPosition(cameraPopoverOptions);
}
## CameraPopoverOptions
iOS-only parameters that specify the anchor element location and arrow
direction of the popover when selecting images from an iPad's library
or album.
{ x : 0,
y : 32,
width : 320,
height : 480,
arrowDir : Camera.PopoverArrowDirection.ARROW_ANY
};
#### Description
- __x__: x pixel coordinate of screen element onto which to anchor the popover. _(Number)_
- __y__: y pixel coordinate of screen element onto which to anchor the popover. _(Number)_
- __width__: width, in pixels, of the screen element onto which to anchor the popover. _(Number)_
- __height__: height, in pixels, of the screen element onto which to anchor the popover. _(Number)_
- __arrowDir__: Direction the arrow on the popover should point. Defined in `Camera.PopoverArrowDirection` _(Number)_
Camera.PopoverArrowDirection = {
ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants
ARROW_DOWN : 2,
ARROW_LEFT : 4,
ARROW_RIGHT : 8,
ARROW_ANY : 15
};
Note that the size of the popover may change to adjust to the
direction of the arrow and orientation of the screen. Make sure to
account for orientation changes when specifying the anchor element
location.
## navigator.camera.cleanup
Removes intermediate photos taken by the camera from temporary
storage.
navigator.camera.cleanup( cameraSuccess, cameraError );
#### Description
Removes intermediate image files that are kept in temporary storage
after calling `camera.getPicture`. Applies only when the value of
`Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the
`Camera.destinationType` equals `Camera.DestinationType.FILE_URI`.
#### Supported Platforms
![](doc/img/android-fail.png) ![](doc/img/blackberry-fail.png) ![](doc/img/browser-fail.png) ![](doc/img/firefox-fail.png) ![](doc/img/fireos-fail.png) ![](doc/img/ios-success.png) ![](doc/img/windows-fail.png) ![](doc/img/wp8-fail.png) ![](doc/img/ubuntu-fail.png)
#### Example
navigator.camera.cleanup(onSuccess, onFail);
function onSuccess() {
console.log("Camera cleanup success.")
}
function onFail(message) {
alert('Failed because: ' + message);
}
[android_lifecycle]: http://cordova.apache.org/docs/en/dev/guide/platforms/android/lifecycle.html
[web_activities]: https://hacks.mozilla.org/2013/01/introducing-web-activities/
[wp8_bug]: https://issues.apache.org/jira/browse/CB-2083
[msdn_wp8_docs]: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394006.aspx

View File

@@ -20,33 +20,230 @@
-->
# Release Notes
### 0.2.1 (Sept 5, 2013)
* [CB-4656] Don't add line-breaks to base64-encoded images (Fixes type=DataURI)
* [CB-4432] copyright notice change
### 2.1.1 (Mar 09, 2016)
* [CB-10825](https://issues.apache.org/jira/browse/CB-10825) android: Always request READ permission for gallery source
* added apache license header to appium files
* [CB-10720](https://issues.apache.org/jira/browse/CB-10720) Fixed spelling, capitalization, and other small issues.
* [CB-10414](https://issues.apache.org/jira/browse/CB-10414) Adding focus handler to resume video when user comes back on leaving the app while preview was running
* Appium tests: adjust swipe distance on ** Android **
* [CB-10750](https://issues.apache.org/jira/browse/CB-10750) Appium tests: fail fast if session is irrecoverable
* Adding missing semi colon
* Adding focus handler to make sure filepicker gets launched when app is active on ** Windows **
* [CB-10128](https://issues.apache.org/jira/browse/CB-10128) **iOS** Fixed how checks access authorization to camera & library. This closes #146
* [CB-10636](https://issues.apache.org/jira/browse/CB-10636) Add JSHint for plugins
* [CB-10639](https://issues.apache.org/jira/browse/CB-10639) Appium tests: Added some timeouts, Taking a screenshot on failure, Retry taking a picture up to 3 times, Try to restart the Appium session if it's lost
* [CB-10552](https://issues.apache.org/jira/browse/CB-10552) Replacing images in README.md.
* Added a lot of more cases to get the real path on ** Android **
* [CB-10625](https://issues.apache.org/jira/browse/CB-10625) ** Android ** getPicture fails when getting a photo from the Photo Library - Google Photos
* [CB-10619](https://issues.apache.org/jira/browse/CB-10619) Appium tests: Properly switch to webview on ** Android **
* [CB-10397](https://issues.apache.org/jira/browse/CB-10397) Added Appium tests
* [CB-10576](https://issues.apache.org/jira/browse/CB-10576) MobileSpec can't get results for **Windows**-Store 8.1 Builds
* chore: edit package.json license to match SPDX id
* [CB-10539](https://issues.apache.org/jira/browse/CB-10539) Commenting out the verySmallQvga maxResolution option on ** Windows **
* [CB-10541](https://issues.apache.org/jira/browse/CB-10541) Changing default maxResoltion to be highestAvailable for CameraCaptureUI on ** Windows **
* [CB-10113](https://issues.apache.org/jira/browse/CB-10113) ** Browser ** - Layer camera UI on top of all!
* [CB-10502](https://issues.apache.org/jira/browse/CB-10502) ** Browser ** - Fix camera plugin exception in Chrome when click capture.
* Adding comments
* Camera tapping fix on ** Windows **
### 0.2.3 (Sept 25, 2013)
* CB-4889 bumping&resetting version
* CB-4889 forgot index.html
* CB-4889 renaming core inside cameraProxy
* [Windows8] commandProxy has moved
* [Windows8] commandProxy has moved
* added Camera API for FirefoxOS
* Rename CHANGELOG.md -> RELEASENOTES.md
* [CB-4823] Fix XCode 5 camera plugin warnings
* Fix compiler warnings
* [CB-4765] Move ExifHelper.java into Camera Plugin
* [CB-4764] Remove reference to DirectoryManager from CameraLauncher
* [CB-4763] Use a copy of FileHelper.java within camera-plugin.
* [CB-4752] Incremented plugin version on dev branch.
* CB-4633: We really should close cursors. It's just the right thing to do.
* No longer causes a stack trace, but it doesn't cause the error to be called.
* CB-4889 renaming org.apache.cordova.core.camera to org.apache.cordova.camera
### 2.1.0 (Jan 15, 2016)
* added `.ratignore`
* CB-10319 **Android** Adding reflective helper methods for permission requests
* CB-9189 **Android** Implementing `save/restore` API to handle Activity destruction
* CB-10241 App Crash cause by Camera Plugin **iOS 7**
* CB-8940 Setting `z-index` values to maximum for UI buttons.
### 0.2.4 (Oct 28, 2013)
* CB-5128: added repo + issue tag to plugin.xml for camera plugin
* CB-4958 - iOS - Camera plugin should not show the status bar
* [CB-4919] updated plugin.xml for FxOS
* [CB-4915] Incremented plugin version on dev branch.
### 2.0.0 (Nov 18, 2015)
* [CB-10035](https://issues.apache.org/jira/browse/CB-10035) Updated `RELEASENOTES` to be newest to oldest
* [CB-8863](https://issues.apache.org/jira/browse/CB-8863) correct block usage for `async` calls
* [CB-5479](https://issues.apache.org/jira/browse/CB-5479) changed `saveToPhotoAlbum` to save uncompressed images for **Android**
* [CB-9169](https://issues.apache.org/jira/browse/CB-9169) Fixed `filetype` for uncompressed images and added quirk for **Android**
* [CB-9446](https://issues.apache.org/jira/browse/CB-9446) Removing `CordovaResource` library code in favour of the code we're supposed to be deprecating because that at least works.
* [CB-9942](https://issues.apache.org/jira/browse/CB-9942) Normalize line endings in Camera plugin docs
* [CB-9910](https://issues.apache.org/jira/browse/CB-9910) Add permission request for some gallery requests for **Android**
* [CB-7668](https://issues.apache.org/jira/browse/CB-7668) Adding a sterner warning for `allowedit` on **Android**
* Fixing contribute link.
* Using the `CordovaResourceApi` to fine paths of files in the background thread. If the file doesn't exist, return the content `URI`.
* Add engine tag for **Cordova-Android 5.0.x**
* [CB-9583](https://issues.apache.org/jira/browse/CB-9583): Added support for **Marshmallow** permissions (**Android 6.0**)
* Try to use `realpath` filename instead of default `modified.jpg`
* [CB-6190](https://issues.apache.org/jira/browse/CB-6190) **iOS** camera plugin ignores quality parameter
* [CB-9633](https://issues.apache.org/jira/browse/CB-9633) **iOS** Taking a Picture With Option `destinationType:NATIVE_URI` doesn't show image
* [CB-9745](https://issues.apache.org/jira/browse/CB-9745) Camera plugin docs should be generated from the source
* [CB-9622](https://issues.apache.org/jira/browse/CB-9622) **WP8** Camera Option `destinationType:NATIVE_URI` is a `NO-OP`
* [CB-9623](https://issues.apache.org/jira/browse/CB-9623) Fixes various issues when `encodingType` set to `png`
* [CB-9591](https://issues.apache.org/jira/browse/CB-9591) Retaining aspect ratio when resizing
* [CB-9443](https://issues.apache.org/jira/browse/CB-9443) Pick correct `maxResolution`
* [CB-9151](https://issues.apache.org/jira/browse/CB-9151) Trigger `captureAction` only once
* [CB-9413](https://issues.apache.org/jira/browse/CB-9413) Close `RandomAccessStream` once copied
* [CB-5661](https://issues.apache.org/jira/browse/CB-5661) Remove outdated **iOS** quirks about memory
* [CB-9349](https://issues.apache.org/jira/browse/CB-9349) Focus control and nice UI
* [CB-9259](https://issues.apache.org/jira/browse/CB-9259) Forgot to add another check on which `URI` we're using when fixing this thing the first time
* [CB-9247](https://issues.apache.org/jira/browse/CB-9247) Added macro to conditionally add `NSData+Base64.h`
* [CB-9247](https://issues.apache.org/jira/browse/CB-9247) Fixes compilation errors with **cordova-ios 4.x**
* Fix returning native url on **Windows**.
### 1.2.0 (Jun 17, 2015)
* Closing stale pull request: close #84
* Closing stale pull request: close #66
* [CB-9128](https://issues.apache.org/jira/browse/CB-9128) cordova-plugin-camera documentation translation: cordova-plugin-camera
* Update docs. This closes #100
* attempt to fix npm markdown issue
* [CB-8883](https://issues.apache.org/jira/browse/CB-8883) fix picture rotation issue
* one more alias
* Fixed some nit white-space issues, aliased a little more
* major refactor : readability
* Patch for [CB-8498](https://issues.apache.org/jira/browse/CB-8498), this closes #64
* [CB-8879](https://issues.apache.org/jira/browse/CB-8879) fix stripe issue with correct aspect ratio
* [CB-8601](https://issues.apache.org/jira/browse/CB-8601) - iOS camera unit tests broken
* [CB-7667](https://issues.apache.org/jira/browse/CB-7667) iOS8: Handle case where camera is not authorized (closes #49)
* add missing license header
### 1.1.0 (May 06, 2015)
* [CB-8943](https://issues.apache.org/jira/browse/CB-8943) fix `PickAndContinue` issue on *Win10Phone*
* [CB-8253](https://issues.apache.org/jira/browse/CB-8253) Fix potential unreleased resources
* [CB-8909](https://issues.apache.org/jira/browse/CB-8909): Remove unused import from File
* [CB-8404](https://issues.apache.org/jira/browse/CB-8404) typo fix `cameraproxy.js`
* [CB-8404](https://issues.apache.org/jira/browse/CB-8404) Rotate camera feed with device orientation
* [CB-8054](https://issues.apache.org/jira/browse/CB-8054) Support taking pictures from file for *WP8*
* [CB-8405](https://issues.apache.org/jira/browse/CB-8405) Use `z-index` instead of `z-order`
### 1.0.0 (Apr 15, 2015)
* [CB-8780](https://issues.apache.org/jira/browse/CB-8780) - Display popover using main thread. Fixes popover slowness (closes #81)
* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) bumped version of file dependency
* [CB-8746](https://issues.apache.org/jira/browse/CB-8746) gave plugin major version bump
* [CB-8707](https://issues.apache.org/jira/browse/CB-8707) refactoring windows code to improve readability
* [CB-8706](https://issues.apache.org/jira/browse/CB-8706) use filePicker if saveToPhotoAlbum is true
* [CB-8706](https://issues.apache.org/jira/browse/CB-8706) remove unnecessary capabilities from xml
* [CB-8747](https://issues.apache.org/jira/browse/CB-8747) updated dependency, added peer dependency
* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) updated blackberry specific references of org.apache.cordova.camera to cordova-plugin-camera
* [CB-8782](https://issues.apache.org/jira/browse/CB-8782): Updated the docs to talk about the allowEdit quirks, it's not 100% working, but better than it was
* [CB-8782](https://issues.apache.org/jira/browse/CB-8782): Fixed the flow so that we save the cropped image and use it, not the original non-cropped. Crop only supports G+ Photos Crop, other crops may not work, depending on the OEM
* [CB-8740](https://issues.apache.org/jira/browse/CB-8740): Removing FileHelper call that was failing on Samsung Galaxy S3, now that we have a real path, we only need to update the MediaStore, not pull from it in this case
* [CB-8740](https://issues.apache.org/jira/browse/CB-8740): Partial fix for Save Image to Gallery error found in MobileSpec
* [CB-8683](https://issues.apache.org/jira/browse/CB-8683) changed plugin-id to pacakge-name
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) properly updated translated docs to use new id
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) updated translated docs to use new id
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) Fix custom implementation of integerValueForKey (close #79)
* Fix cordova-paramedic path change, build with TRAVIS_BUILD_DIR, use npm to install paramedic
* docs: added 'Windows' to supported platforms
* [CB-8653](https://issues.apache.org/jira/browse/CB-8653) Updated Readme
* [CB-8659](https://issues.apache.org/jira/browse/CB-8659): ios: 4.0.x Compatibility: Remove use of deprecated headers
### 0.3.6 (Mar 10, 2015)
* Fix localize key for Videos. This closes #58
* [CB-8235](https://issues.apache.org/jira/browse/CB-8235) android: Fix crash when selecting images from DropBox with spaces in path (close #65)
* add try ... catch for getting image orientation
* [CB-8599](https://issues.apache.org/jira/browse/CB-8599) fix threading issue with cameraPicker (fixes #72)
* [CB-8559](https://issues.apache.org/jira/browse/CB-8559) Integrate TravisCI
* [CB-8438](https://issues.apache.org/jira/browse/CB-8438) cordova-plugin-camera documentation translation: cordova-plugin-camera
* [CB-8538](https://issues.apache.org/jira/browse/CB-8538) Added package.json file
### 0.3.5 (Feb 04, 2015)
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Stop using now-deprecated [NSData base64EncodedString]
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Stop using now-deprecated integerValueForKey: class extension
* [CB-8351](https://issues.apache.org/jira/browse/CB-8351) ios: Use argumentForIndex rather than NSArray extension
* [CB-8032](https://issues.apache.org/jira/browse/CB-8032) ios: Add nativeURL external method support for CDVFileSystem->makeEntryForPath:isDirectory:
* [CB-7938](https://issues.apache.org/jira/browse/CB-7938) ios: Added XCTest unit tests project, with stubs (adapted from SplashScreen unit test setup)
* [CB-7937](https://issues.apache.org/jira/browse/CB-7937) ios: Re-factor iOS Camera plugin so that it is testable
### 0.3.4 (Dec 02, 2014)
* [CB-7977](https://issues.apache.org/jira/browse/CB-7977) Mention `deviceready` in plugin docs
* [CB-7979](https://issues.apache.org/jira/browse/CB-7979) Each plugin doc should have a ## Installation section
* Fix memory leak of image data in `imagePickerControllerReturnImageResult`
* Pass uri to crop instead of pulling the low resolution image out of the intent return (close #43)
* Add orientation support for PNG to Android (closes #45)
* [CB-7700](https://issues.apache.org/jira/browse/CB-7700) cordova-plugin-camera documentation translation: cordova-plugin-camera
### 0.3.3 (Oct 03, 2014)
* [CB-7600](https://issues.apache.org/jira/browse/CB-7600) Adds informative message to error callback in manual test.
### 0.3.2 (Sep 17, 2014)
* [CB-7551](https://issues.apache.org/jira/browse/CB-7551) [Camera][iOS 8] Scaled images show a white line
* [CB-7558](https://issues.apache.org/jira/browse/CB-7558) hasPendingOperation flag in Camera plugin's takePicture should be reversed to fix memory errors
* [CB-7557](https://issues.apache.org/jira/browse/CB-7557) Camera plugin tests is missing a File dependency
* [CB-7423](https://issues.apache.org/jira/browse/CB-7423) do cleanup after copyImage manual test
* [CB-7471](https://issues.apache.org/jira/browse/CB-7471) cordova-plugin-camera documentation translation: cordova-plugin-camera
* [CB-7413](https://issues.apache.org/jira/browse/CB-7413) Resolve 'ms-appdata' URIs with File plugin
* Fixed minor bugs with the browser
* [CB-7433](https://issues.apache.org/jira/browse/CB-7433) Adds missing window reference to prevent manual tests failure on Android and iOS
* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-camera documentation translation: cordova-plugin-camera
* [CB-4003](https://issues.apache.org/jira/browse/CB-4003) Add config option to not use location information in Camera plugin (and default to not use it)
* [CB-7461](https://issues.apache.org/jira/browse/CB-7461) Geolocation fails in Camera plugin in iOS 8
* [CB-7378](https://issues.apache.org/jira/browse/CB-7378) Use single Proxy for both windows8 and windows.
* [CB-7378](https://issues.apache.org/jira/browse/CB-7378) Adds support for windows platform
* [CB-7433](https://issues.apache.org/jira/browse/CB-7433) Fixes manual tests failure on windows
* [CB-6958](https://issues.apache.org/jira/browse/CB-6958) Get the correct default for "quality" in the test
* add documentation for manual tests
* [CB-7249](https://issues.apache.org/jira/browse/CB-7249) cordova-plugin-camera documentation translation: cordova-plugin-camera
* [CB-4003](https://issues.apache.org/jira/browse/CB-4003) Add config option to not use location information in Camera plugin (and default to not use it)
* [CB-7461](https://issues.apache.org/jira/browse/CB-7461) Geolocation fails in Camera plugin in iOS 8
* [CB-7433](https://issues.apache.org/jira/browse/CB-7433) Fixes manual tests failure on windows
* [CB-7378](https://issues.apache.org/jira/browse/CB-7378) Use single Proxy for both windows8 and windows.
* [CB-7378](https://issues.apache.org/jira/browse/CB-7378) Adds support for windows platform
* [CB-6958](https://issues.apache.org/jira/browse/CB-6958) Get the correct default for "quality" in the test
* add documentation for manual tests
* Updated docs for browser
* Added support for the browser
* [CB-7286](https://issues.apache.org/jira/browse/CB-7286) [BlackBerry10] Use getUserMedia if camera card is unavailable
* [CB-7180](https://issues.apache.org/jira/browse/CB-7180) Update Camera plugin to support generic plugin webView UIView (which can be either a UIWebView or WKWebView)
* Renamed test dir, added nested plugin.xml
* [CB-6958](https://issues.apache.org/jira/browse/CB-6958) added manual tests
* [CB-6958](https://issues.apache.org/jira/browse/CB-6958) Port camera tests to plugin-test-framework
### 0.3.1 (Aug 06, 2014)
* **FFOS** update CameraProxy.js
* [CB-7187](https://issues.apache.org/jira/browse/CB-7187) ios: Add explicit dependency on CoreLocation.framework
* [BlackBerry10] Doc correction - sourceType is supported
* [CB-7071](https://issues.apache.org/jira/browse/CB-7071) android: Fix callback firing before CROP intent is sent when allowEdit=true
* [CB-6875](https://issues.apache.org/jira/browse/CB-6875) android: Handle exception when SDCard is not mounted
* ios: Delete postImage (dead code)
* Prevent NPE on processResiultFromGallery when intent comes null
* Remove iOS doc reference to non-existing navigator.fileMgr API
* Docs updated with some default values
* Removes File plugin dependency from windows8 code.
* Use WinJS functionality to resize image instead of File plugin functionality
* [CB-6127](https://issues.apache.org/jira/browse/CB-6127) Updated translations for docs
### 0.3.0 (Jun 05, 2014)
* [CB-5895](https://issues.apache.org/jira/browse/CB-5895) documented saveToPhotoAlbum quirk on WP8
* Remove deprecated symbols for iOS < 6
* documentation translation: cordova-plugin-camera
* ubuntu: use application directory for images
* [CB-6795](https://issues.apache.org/jira/browse/CB-6795) Add license
* Little fix in code formatting
* [CB-6613](https://issues.apache.org/jira/browse/CB-6613) Use WinJS functionality to get base64-encoded content of image instead of File plugin functionality
* [CB-6612](https://issues.apache.org/jira/browse/CB-6612) camera.getPicture now always returns encoded JPEG image
* Removed invalid note from [CB-5398](https://issues.apache.org/jira/browse/CB-5398)
* [CB-6576](https://issues.apache.org/jira/browse/CB-6576) - Returns a specific error message when app has no access to library.
* [CB-6491](https://issues.apache.org/jira/browse/CB-6491) add CONTRIBUTING.md
* [CB-6546](https://issues.apache.org/jira/browse/CB-6546) android: Fix a couple bugs with allowEdit pull request
* [CB-6546](https://issues.apache.org/jira/browse/CB-6546) android: Add support for allowEdit Camera option
### 0.2.9 (Apr 17, 2014)
* [CB-6460](https://issues.apache.org/jira/browse/CB-6460): Update license headers
* [CB-6422](https://issues.apache.org/jira/browse/CB-6422): [windows8] use cordova/exec/proxy
* [WP8] When only targetWidth or targetHeight is provided, use it as the only bound
* [CB-4027](https://issues.apache.org/jira/browse/CB-4027), [CB-5102](https://issues.apache.org/jira/browse/CB-5102), [CB-2737](https://issues.apache.org/jira/browse/CB-2737), [CB-2387](https://issues.apache.org/jira/browse/CB-2387): [WP] Fix camera issues, cropping, memory leaks
* [CB-6212](https://issues.apache.org/jira/browse/CB-6212): [iOS] fix warnings compiled under arm64 64-bit
* [BlackBerry10] Add rim xml namespaces declaration
* Add NOTICE file
### 0.2.8 (Feb 26, 2014)
* [CB-1826](https://issues.apache.org/jira/browse/CB-1826) Catch OOM on gallery image resize
### 0.2.7 (Feb 05, 2014)
* [CB-4919](https://issues.apache.org/jira/browse/CB-4919) firefox os quirks added and supported platforms list is updated
* getPicture via web activities
* Documented quirk for [CB-5335](https://issues.apache.org/jira/browse/CB-5335) + [CB-5206](https://issues.apache.org/jira/browse/CB-5206) for WP7+8
* reference the correct firefoxos implementation
* [BlackBerry10] Add permission to access_shared
### 0.2.6 (Jan 02, 2014)
* [CB-5658](https://issues.apache.org/jira/browse/CB-5658) Add doc/index.md for Camera plugin
* [CB-2442](https://issues.apache.org/jira/browse/CB-2442) [CB-2419](https://issues.apache.org/jira/browse/CB-2419) Use Windows.Storage.ApplicationData.current.localFolder, instead of writing to app package.
* [BlackBerry10] Adding platform level permissions
* [CB-5599](https://issues.apache.org/jira/browse/CB-5599) Android: Catch and ignore OutOfMemoryError in getRotatedBitmap()
### 0.2.5 (Dec 4, 2013)
* fix camera for firefox os
@@ -56,169 +253,30 @@
* 1. User Agent detection now detects AmazonWebView. 2. Change to use amazon-fireos as the platform if user agent string contains 'cordova-amazon-fireos'
* Added amazon-fireos platform.
### 0.2.6 (Jan 02, 2014)
* CB-5658 Add doc/index.md for Camera plugin
* CB-2442 CB-2419 Use Windows.Storage.ApplicationData.current.localFolder, instead of writing to app package.
* [BlackBerry10] Adding platform level permissions
* CB-5599 Android: Catch and ignore OutOfMemoryError in getRotatedBitmap()
### 0.2.4 (Oct 28, 2013)
* [CB-5128](https://issues.apache.org/jira/browse/CB-5128): added repo + issue tag to plugin.xml for camera plugin
* [CB-4958](https://issues.apache.org/jira/browse/CB-4958) - iOS - Camera plugin should not show the status bar
* [CB-4919](https://issues.apache.org/jira/browse/CB-4919) updated plugin.xml for FxOS
* [CB-4915](https://issues.apache.org/jira/browse/CB-4915) Incremented plugin version on dev branch.
### 0.2.7 (Feb 05, 2014)
* CB-4919 firefox os quirks added and supported platforms list is updated
* getPicture via web activities
* Documented quirk for CB-5335 + CB-5206 for WP7+8
* reference the correct firefoxos implementation
* [BlackBerry10] Add permission to access_shared
### 0.2.3 (Sept 25, 2013)
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) bumping&resetting version
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) forgot index.html
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming core inside cameraProxy
* [Windows8] commandProxy has moved
* [Windows8] commandProxy has moved
* added Camera API for FirefoxOS
* Rename CHANGELOG.md -> RELEASENOTES.md
* [CB-4823](https://issues.apache.org/jira/browse/CB-4823) Fix XCode 5 camera plugin warnings
* Fix compiler warnings
* [CB-4765](https://issues.apache.org/jira/browse/CB-4765) Move ExifHelper.java into Camera Plugin
* [CB-4764](https://issues.apache.org/jira/browse/CB-4764) Remove reference to DirectoryManager from CameraLauncher
* [CB-4763](https://issues.apache.org/jira/browse/CB-4763) Use a copy of FileHelper.java within camera-plugin.
* [CB-4752](https://issues.apache.org/jira/browse/CB-4752) Incremented plugin version on dev branch.
* [CB-4633](https://issues.apache.org/jira/browse/CB-4633): We really should close cursors. It's just the right thing to do.
* No longer causes a stack trace, but it doesn't cause the error to be called.
* [CB-4889](https://issues.apache.org/jira/browse/CB-4889) renaming org.apache.cordova.core.camera to org.apache.cordova.camera
### 0.2.8 (Feb 26, 2014)
* CB-1826 Catch OOM on gallery image resize
### 0.2.9 (Apr 17, 2014)
* CB-6460: Update license headers
* CB-6422: [windows8] use cordova/exec/proxy
* [WP8] When only targetWidth or targetHeight is provided, use it as the only bound
* CB-4027, CB-5102, CB-2737, CB-2387: [WP] Fix camera issues, cropping, memory leaks
* CB-6212: [iOS] fix warnings compiled under arm64 64-bit
* [BlackBerry10] Add rim xml namespaces declaration
* Add NOTICE file
### 0.3.0 (Jun 05, 2014)
* CB-2083 documented saveToPhotoAlbum quirk on WP8
* CB-5895 documented saveToPhotoAlbum quirk on WP8
* Remove deprecated symbols for iOS < 6
* documentation translation: cordova-plugin-camera
* Lisa testing pulling in plugins for plugin: cordova-plugin-camera
* Lisa testing pulling in plugins for plugin: cordova-plugin-camera
* Lisa testing pulling in plugins for plugin: cordova-plugin-camera
* Lisa testing pulling in plugins for plugin: cordova-plugin-camera
* ubuntu: use application directory for images
* CB-6795 Add license
* Little fix in code formatting
* CB-6613 Use WinJS functionality to get base64-encoded content of image instead of File plugin functionality
* CB-6612 camera.getPicture now always returns encoded JPEG image
* Removed invalid note from CB-5398
* CB-6576 - Returns a specific error message when app has no access to library.
* CB-6491 add CONTRIBUTING.md
* CB-6546 android: Fix a couple bugs with allowEdit pull request
* CB-6546 android: Add support for allowEdit Camera option
### 0.3.1 (Aug 06, 2014)
* **FFOS** update CameraProxy.js
* CB-7187 ios: Add explicit dependency on CoreLocation.framework
* [BlackBerry10] Doc correction - sourceType is supported
* CB-7071 android: Fix callback firing before CROP intent is sent when allowEdit=true
* CB-6875 android: Handle exception when SDCard is not mounted
* ios: Delete postImage (dead code)
* Prevent NPE on processResiultFromGallery when intent comes null
* Remove iOS doc reference to non-existing navigator.fileMgr API
* Docs updated with some default values
* Removes File plugin dependency from windows8 code.
* Use WinJS functionality to resize image instead of File plugin functionality
* CB-6127 Updated translations for docs
### 0.3.2 (Sep 17, 2014)
* CB-7551 [Camera][iOS 8] Scaled images show a white line
* CB-7558 hasPendingOperation flag in Camera plugin's takePicture should be reversed to fix memory errors
* CB-7557 Camera plugin tests is missing a File dependency
* CB-7423 do cleanup after copyImage manual test
* CB-7471 cordova-plugin-camera documentation translation: cordova-plugin-camera
* CB-7413 Resolve 'ms-appdata' URIs with File plugin
* Fixed minor bugs with the browser
* CB-7433 Adds missing window reference to prevent manual tests failure on Android and iOS
* CB-7249 cordova-plugin-camera documentation translation: cordova-plugin-camera
* CB-4003 Add config option to not use location information in Camera plugin (and default to not use it)
* CB-7461 Geolocation fails in Camera plugin in iOS 8
* CB-7378 Use single Proxy for both windows8 and windows.
* CB-7378 Adds support for windows platform
* CB-7433 Fixes manual tests failure on windows
* CB-6958 Get the correct default for "quality" in the test
* add documentation for manual tests
* CB-7249 cordova-plugin-camera documentation translation: cordova-plugin-camera
* CB-4003 Add config option to not use location information in Camera plugin (and default to not use it)
* CB-7461 Geolocation fails in Camera plugin in iOS 8
* CB-7433 Fixes manual tests failure on windows
* CB-7378 Use single Proxy for both windows8 and windows.
* CB-7378 Adds support for windows platform
* CB-6958 Get the correct default for "quality" in the test
* add documentation for manual tests
* Updated docs for browser
* Added support for the browser
* CB-7286 [BlackBerry10] Use getUserMedia if camera card is unavailable
* CB-7180 Update Camera plugin to support generic plugin webView UIView (which can be either a UIWebView or WKWebView)
* Renamed test dir, added nested plugin.xml
* CB-6958 added manual tests
* CB-6958 Port camera tests to plugin-test-framework
### 0.3.3 (Oct 03, 2014)
* CB-7600 Adds informative message to error callback in manual test.
### 0.3.4 (Dec 02, 2014)
* CB-7977 Mention `deviceready` in plugin docs
* CB-7979 Each plugin doc should have a ## Installation section
* Fix memory leak of image data in `imagePickerControllerReturnImageResult`
* Pass uri to crop instead of pulling the low resolution image out of the intent return (close #43)
* Add orientation support for PNG to Android (closes #45)
* CB-7700 cordova-plugin-camera documentation translation: cordova-plugin-camera
### 0.3.5 (Feb 04, 2015)
* CB-8351 ios: Stop using now-deprecated [NSData base64EncodedString]
* CB-8351 ios: Stop using now-deprecated integerValueForKey: class extension
* CB-8351 ios: Use argumentForIndex rather than NSArray extension
* CB-8032 ios: Add nativeURL external method support for CDVFileSystem->makeEntryForPath:isDirectory:
* CB-7938 ios: Added XCTest unit tests project, with stubs (adapted from SplashScreen unit test setup)
* CB-7937 ios: Re-factor iOS Camera plugin so that it is testable
### 0.3.6 (Mar 10, 2015)
* Fix localize key for Videos. This closes #58
* CB-8235 android: Fix crash when selecting images from DropBox with spaces in path (close #65)
* add try ... catch for getting image orientation
* CB-8599 fix threading issue with cameraPicker (fixes #72)
* CB-8559 Integrate TravisCI
* CB-8438 cordova-plugin-camera documentation translation: cordova-plugin-camera
* CB-8538 Added package.json file
### 1.0.0 (Apr 15, 2015)
* CB-8780 - Display popover using main thread. Fixes popover slowness (closes #81)
* CB-8746 bumped version of file dependency
* CB-8746 gave plugin major version bump
* CB-8707 refactoring windows code to improve readability
* CB-8706 use filePicker if saveToPhotoAlbum is true
* CB-8706 remove unnecessary capabilities from xml
* CB-8747 updated dependency, added peer dependency
* CB-8683 updated blackberry specific references of org.apache.cordova.camera to cordova-plugin-camera
* CB-8782: Updated the docs to talk about the allowEdit quirks, it's not 100% working, but better than it was
* CB-8782: Fixed the flow so that we save the cropped image and use it, not the original non-cropped. Crop only supports G+ Photos Crop, other crops may not work, depending on the OEM
* CB-8740: Removing FileHelper call that was failing on Samsung Galaxy S3, now that we have a real path, we only need to update the MediaStore, not pull from it in this case
* CB-8740: Partial fix for Save Image to Gallery error found in MobileSpec
* CB-8683 changed plugin-id to pacakge-name
* CB-8653 properly updated translated docs to use new id
* CB-8653 updated translated docs to use new id
* CB-8351 Fix custom implementation of integerValueForKey (close #79)
* Fix cordova-paramedic path change, build with TRAVIS_BUILD_DIR, use npm to install paramedic
* docs: added 'Windows' to supported platforms
* CB-8653 Updated Readme
* CB-8659: ios: 4.0.x Compatibility: Remove use of deprecated headers
### 1.1.0 (May 06, 2015)
* CB-8943 fix `PickAndContinue` issue on *Win10Phone*
* CB-8253 Fix potential unreleased resources
* CB-8909: Remove unused import from File
* CB-8404 typo fix `cameraproxy.js`
* CB-8404 Rotate camera feed with device orientation
* CB-8054 Support taking pictures from file for *WP8*
* CB-8405 Use `z-index` instead of `z-order`
### 1.2.0 (Jun 17, 2015)
* Closing stale pull request: close #84
* Closing stale pull request: close #66
* CB-9128 cordova-plugin-camera documentation translation: cordova-plugin-camera
* Update docs. This closes #100
* attempt to fix npm markdown issue
* CB-8883 fix picture rotation issue
* one more alias
* Fixed some nit white-space issues, aliased a little more
* major refactor : readability
* Patch for CB-8498, this closes #64
* CB-8879 fix stripe issue with correct aspect ratio
* CB-8601 - iOS camera unit tests broken
* CB-7667 iOS8: Handle case where camera is not authorized (closes #49)
* add missing license header
### 0.2.1 (Sept 5, 2013)
* [CB-4656](https://issues.apache.org/jira/browse/CB-4656) Don't add line-breaks to base64-encoded images (Fixes type=DataURI)
* [CB-4432](https://issues.apache.org/jira/browse/CB-4432) copyright notice change

View File

@@ -0,0 +1,592 @@
/*jshint node: true, jasmine: true */
/*
*
* 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.
*
*/
// these tests are meant to be executed by Cordova Medic Appium runner
// you can find it here: https://github.com/apache/cordova-medic/
// it is not necessary to do a full CI setup to run these tests
// just run "node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-camera"
'use strict';
var wdHelper = require('../helpers/wdHelper');
var wd = wdHelper.getWD();
var cameraConstants = require('../../www/CameraConstants');
var cameraHelper = require('../helpers/cameraHelper');
var screenshotHelper = require('../helpers/screenshotHelper');
var STARTING_MESSAGE = 'Ready for action!';
var RETRY_COUNT = 3; // how many times to retry taking a picture before failing
var MINUTE = 60 * 1000;
var DEFAULT_SCREEN_WIDTH = 360;
var DEFAULT_SCREEN_HEIGHT = 567;
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW';
describe('Camera tests Android.', function () {
var driver;
// the name of webview context, it will be changed to match needed context if there are named ones:
var webviewContext = DEFAULT_WEBVIEW_CONTEXT;
// this indicates that the device library has the test picture:
var isTestPictureSaved = false;
// this indicates that there was a critical error and we should try to recover:
var errorFlag = false;
// this indicates that we couldn't restore Appium session and should fail fast:
var stopFlag = false;
// we need to know the screen width and height to properly click on an image in the gallery:
var screenWidth = DEFAULT_SCREEN_WIDTH;
var screenHeight = DEFAULT_SCREEN_HEIGHT;
function win() {
expect(true).toBe(true);
}
function fail(error) {
screenshotHelper.saveScreenshot(driver);
if (error && error.message) {
console.log('An error occured: ' + error.message);
expect(true).toFailWithMessage(error.message);
throw error.message;
}
if (error) {
console.log('Failed expectation: ' + error);
expect(true).toFailWithMessage(error);
throw error;
}
// no message provided :(
expect(true).toBe(false);
throw 'An error without description occured';
}
// generates test specs by combining all the specified options
// you can add more options to test more scenarios
function generateSpecs() {
var sourceTypes = [
cameraConstants.PictureSourceType.CAMERA,
cameraConstants.PictureSourceType.PHOTOLIBRARY
],
destinationTypes = cameraConstants.DestinationType,
encodingTypes = [
cameraConstants.EncodingType.JPEG,
cameraConstants.EncodingType.PNG
],
allowEditOptions = [
true,
false
];
return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions);
}
function getPicture(options, skipUiInteractions, retry) {
if (!options) {
options = {};
}
if (typeof retry === 'undefined') {
retry = 1;
}
var command = "navigator.camera.getPicture(function (result) { document.getElementById('info').innerHTML = result.slice(0, 100); }, " +
"function (err) { document.getElementById('info').innerHTML = 'ERROR: ' + err; }," + JSON.stringify(options) + ");";
return driver
.context(webviewContext)
.execute(command)
.sleep(7000)
.context('NATIVE_APP')
.sleep(5000)
.then(function () {
if (skipUiInteractions) {
return;
}
if (options.hasOwnProperty('sourceType') &&
(options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY ||
options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM)) {
var touchTile = new wd.TouchAction(),
swipeRight = new wd.TouchAction();
touchTile.press({x: Math.round(screenWidth / 4), y: Math.round(screenHeight / 5)}).release();
swipeRight.press({x: 10, y: Math.round(screenHeight * 0.8)})
.wait(300)
.moveTo({x: Math.round(screenWidth / 2), y: Math.round(screenHeight / 2)})
.release();
return driver
.performTouchAction(swipeRight)
.sleep(3000)
.elementByXPath('//*[@text="Gallery"]')
.then(function (element) {
return element.click().sleep(5000);
}, function () {
// if the gallery is already opened, we'd just go on:
return driver;
})
.performTouchAction(touchTile);
}
return driver
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
.click()
.sleep(3000)
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
.click()
.sleep(10000);
})
.then(function () {
if (skipUiInteractions) {
return;
}
if (options.hasOwnProperty('allowEdit') && options.allowEdit === true) {
return driver
.elementByXPath('//*[contains(@resource-id,\'save\')]')
.click();
}
})
.then(function () {
if (!skipUiInteractions) {
return driver.sleep(10000);
}
})
.fail(function (error) {
if (retry < RETRY_COUNT) {
console.log('Failed to get a picture. Let\'s try it again... ');
return getPicture(options, skipUiInteractions, ++retry);
} else {
console.log('Tried ' + RETRY_COUNT + ' times but couldn\'t get the picture. Failing...');
fail(error);
}
});
}
function enterTest() {
return driver
// trying to determine where we are
.context(webviewContext)
.fail(function (error) {
fail(error);
})
.elementById('info')
.then(function () {
return driver; //we're already on the test screen
}, function () {
return driver
.elementById('middle')
.then(function () {
return driver
// we're on autotests page, we should go to start page
.execute('window.location = "../index.html"')
.sleep(5000)
.fail(function () {
errorFlag = true;
throw 'Couldn\'t find start page.';
});
}, function () {
return; // no-op
})
// unknown starting page: no 'info' div
// adding it manually
.execute('var info = document.createElement("div"); ' +
'info.id = "info"; ' +
'document.body.appendChild(info);');
})
.sleep(5000);
}
function checkPicture(shouldLoad) {
return driver
.context(webviewContext)
.elementById('info')
.getAttribute('innerHTML')
.then(function (html) {
if (html.indexOf(STARTING_MESSAGE) >= 0) {
expect(true).toFailWithMessage('No callback was fired');
} else if (shouldLoad) {
expect(html.length).toBeGreaterThan(0);
if (html.indexOf('ERROR') >= 0) {
fail(html);
}
} else {
if (html.indexOf('ERROR') === -1) {
fail('Unexpected success callback with result: ' + html);
}
expect(html.indexOf('ERROR')).toBe(0);
}
});
}
function runCombinedSpec(spec) {
return enterTest()
.then(function () {
return getPicture(spec.options);
})
.then(function () {
return checkPicture(true);
})
.then(win, fail);
}
function deleteImage() {
var holdTile = new wd.TouchAction();
holdTile.press({x: Math.round(screenWidth / 3), y: Math.round(screenHeight / 5)}).wait(1000).release();
return driver
.performTouchAction(holdTile)
.elementByXPath('//android.widget.TextView[@text="Delete"]')
.then(function (element) {
return element
.click()
.elementByXPath('//android.widget.Button[@text="OK"]')
.click();
}, function () {
// couldn't find Delete menu item. Possibly there is no image.
return;
});
}
function getDriver() {
driver = wdHelper.getDriver('Android');
return driver;
}
function checkStopFlag() {
if (stopFlag) {
fail('Something went wrong: the stopFlag is on. Please see the log for more details.');
}
return stopFlag;
}
beforeEach(function () {
jasmine.addMatchers({
toFailWithMessage : function () {
return {
compare: function (actual, msg) {
console.log('Failing with message: ' + msg);
var result = {
pass: false,
message: msg
};
// status 6 means that we've lost the session
// status 7 means that Appium couldn't find an element
// both these statuses mean that the test has failed but
// we should try to recreate the session for the following tests
if (msg.indexOf('Error response status: 6') >= 0 ||
msg.indexOf('Error response status: 7') >= 0) {
errorFlag = true;
}
return result;
}
};
}
});
});
it('camera.ui.util configuring driver and starting a session', function (done) {
stopFlag = true; // just in case of timeout
getDriver().then(function () {
stopFlag = false;
}, function (error) {
fail(error);
})
.finally(done);
}, 5 * MINUTE);
it('camera.ui.util determine webview context name', function (done) {
var i = 0;
return driver
.contexts(function (err, contexts) {
if (err) {
console.log(err);
}
for (i = 0; i < contexts.length; i++) {
if (contexts[i].indexOf('mobilespec') >= 0) {
webviewContext = contexts[i];
}
}
done();
});
}, MINUTE);
it('camera.ui.util determine screen dimensions', function (done) {
return enterTest()
.execute('document.getElementById(\'info\').innerHTML = window.innerWidth;')
.sleep(5000)
.elementById('info')
.getAttribute('innerHTML')
.then(function (html) {
if (html !== STARTING_MESSAGE) {
screenWidth = Number(html);
}
})
.execute('document.getElementById(\'info\').innerHTML = \'' + STARTING_MESSAGE + '\';')
.execute('document.getElementById(\'info\').innerHTML = window.innerHeight;')
.sleep(5000)
.elementById('info')
.getAttribute('innerHTML')
.then(function (html) {
if (html !== STARTING_MESSAGE) {
screenHeight = Number(html);
}
done();
});
}, MINUTE);
describe('Specs.', function () {
beforeEach(function (done) {
// prepare the app for the test
if (!stopFlag) {
return driver
.context(webviewContext)
.then(function () {
return driver; // no-op
}, function (error) {
expect(true).toFailWithMessage(error);
})
.execute('document.getElementById("info").innerHTML = "' + STARTING_MESSAGE + '";')
.finally(done);
}
done();
}, 3 * MINUTE);
afterEach(function (done) {
if (!errorFlag || stopFlag) {
// either there's no error or we've failed irrecoverably
// nothing to worry about!
done();
return;
}
// recreate the session if there was a critical error in a previous spec
stopFlag = true; // we're going to set this to false if we're able to restore the session
return driver
.quit()
.then(function () {
return getDriver()
.then(function () {
errorFlag = false;
stopFlag = false;
}, function (error) {
fail(error);
stopFlag = true;
});
}, function (error) {
fail(error);
stopFlag = true;
})
.finally(done);
}, 3 * MINUTE);
// getPicture() with saveToPhotoLibrary = true
it('camera.ui.spec.1 Saving the picture to photo library', function (done) {
var options = {
quality: 50,
allowEdit: false,
sourceType: cameraConstants.PictureSourceType.CAMERA,
saveToPhotoAlbum: true
};
enterTest()
.context(webviewContext)
.then(function () {
return getPicture(options);
})
.then(function () {
isTestPictureSaved = true;
return checkPicture(true);
})
.then(win, fail)
.finally(done);
}, 3 * MINUTE);
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
it('camera.ui.spec.2 Selecting only videos', function (done) {
if (checkStopFlag()) {
done();
return;
}
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
mediaType: cameraConstants.MediaType.VIDEO };
enterTest()
.then(function () {
return getPicture(options, true);
})
.sleep(5000)
.context(webviewContext)
.elementById('info')
.getAttribute('innerHTML')
.then(function (html) {
if (html.indexOf('ERROR') >= 0) {
throw html;
}
})
.context('NATIVE_APP')
.sleep(5000)
.then(function () {
// try to find "Gallery" menu item
// if there's none, the gallery should be already opened
return driver
.elementByXPath('//*[@text="Gallery"]')
.then(function (element) {
return element.click().sleep(2000);
}, function () {
return driver;
});
})
.then(function () {
// if the gallery is opened on the videos page,
// there should be a "Choose video" caption
return driver
.elementByXPath('//*[@text="Choose video"]')
.fail(function () {
throw 'Couldn\'t find "Choose video" element.';
});
})
.then(win, fail)
.deviceKeyEvent(4)
.sleep(2000)
.deviceKeyEvent(4)
.sleep(2000)
.elementById('action_bar_title')
.then(function () {
// success means we're still in native app
return driver
.deviceKeyEvent(4)
.sleep(2000);
}, function () {
// error means we're already in webview
return driver;
})
.finally(done);
}, 3 * MINUTE);
// getPicture(), then dismiss
// wait for the error callback to bee called
it('camera.ui.spec.3 Dismissing the camera', function (done) {
if (checkStopFlag()) {
done();
return;
}
var options = { quality: 50,
allowEdit: true,
sourceType: cameraConstants.PictureSourceType.CAMERA,
destinationType: cameraConstants.DestinationType.FILE_URI };
enterTest()
.context(webviewContext)
.then(function () {
return getPicture(options, true);
})
.sleep(5000)
.context("NATIVE_APP")
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]')
.click()
.context(webviewContext)
.then(function () {
return driver
.elementByXPath('//*[contains(text(),"Camera cancelled")]')
.then(function () {
return checkPicture(false);
}, function () {
throw 'Couldn\'t find "Camera cancelled" message.';
});
})
.then(win, fail)
.finally(done);
}, 3 * MINUTE);
// getPicture(), then take picture but dismiss the edit
// wait for the error cllback to be called
it('camera.ui.spec.4 Dismissing the edit', function (done) {
if (checkStopFlag()) {
done();
return;
}
var options = { quality: 50,
allowEdit: true,
sourceType: cameraConstants.PictureSourceType.CAMERA,
destinationType: cameraConstants.DestinationType.FILE_URI };
enterTest()
.context(webviewContext)
.then(function () {
return getPicture(options, true);
})
.sleep(5000)
.context('NATIVE_APP')
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
.click()
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
.click()
.elementByXPath('//*[contains(@resource-id,\'discard\')]')
.click()
.sleep(5000)
.context(webviewContext)
.then(function () {
return driver
.elementByXPath('//*[contains(text(),"Camera cancelled")]')
.then(function () {
return checkPicture(false);
}, function () {
throw 'Couldn\'t find "Camera cancelled" message.';
});
})
.then(win, fail)
.finally(done);
}, 3 * MINUTE);
// combine various options for getPicture()
generateSpecs().forEach(function (spec) {
it('camera.ui.spec.5.' + spec.id + ' Combining options', function (done) {
if (checkStopFlag()) {
done();
return;
}
runCombinedSpec(spec).then(done);
}, 3 * MINUTE);
});
it('camera.ui.util Delete test image from device library', function (done) {
if (checkStopFlag()) {
done();
return;
}
if (isTestPictureSaved) {
// delete exactly one last picture
// this should be the picture we've taken in the first spec
return driver
.context('NATIVE_APP')
.deviceKeyEvent(3)
.sleep(5000)
.elementByName('Apps')
.click()
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
.click()
.elementByXPath('//android.widget.TextView[contains(@text,"Pictures")]')
.then(function (element) {
return element
.click()
.sleep(3000)
.then(deleteImage)
.then(function () { done(); }, function () { done(); });
}, function () {
done();
});
}
// couldn't save test picture earlier, so nothing to delete here
done();
}, 3 * MINUTE);
});
it('camera.ui.util Destroy the session', function (done) {
return driver.quit(done);
}, 10000);
});

View File

@@ -0,0 +1,86 @@
/*jshint node: true */
/*
*
* 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.
*
*/
'use strict';
var cameraConstants = require('../../www/CameraConstants');
module.exports.generateSpecs = function (sourceTypes, destinationTypes, encodingTypes, allowEditOptions) {
var destinationType,
sourceType,
encodingType,
allowEdit,
specs = [],
id = 1;
for (destinationType in destinationTypes) {
if (destinationTypes.hasOwnProperty(destinationType)) {
for (sourceType in sourceTypes) {
if (sourceTypes.hasOwnProperty(sourceType)) {
for (encodingType in encodingTypes) {
if (encodingTypes.hasOwnProperty(encodingType)) {
for (allowEdit in allowEditOptions) {
if (allowEditOptions.hasOwnProperty(allowEdit)) {
// if taking picture from photolibrary, don't vary 'correctOrientation' option
if (sourceTypes[sourceType] === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
specs.push({
'id': id++,
'options': {
'destinationType': destinationTypes[destinationType],
'sourceType': sourceTypes[sourceType],
'encodingType': encodingTypes[encodingType],
'allowEdit': allowEditOptions[allowEdit],
'saveToPhotoAlbum': false,
}
});
} else {
specs.push({
'id': id++,
'options': {
'destinationType': destinationTypes[destinationType],
'sourceType': sourceTypes[sourceType],
'encodingType': encodingTypes[encodingType],
'correctOrientation': true,
'allowEdit': allowEditOptions[allowEdit],
'saveToPhotoAlbum': false,
}
}, {
'id': id++,
'options': {
'destinationType': destinationTypes[destinationType],
'sourceType': sourceTypes[sourceType],
'encodingType': encodingTypes[encodingType],
'correctOrientation': false,
'allowEdit': allowEditOptions[allowEdit],
'saveToPhotoAlbum': false,
}
});
}
}
}
}
}
}
}
}
}
return specs;
};

View File

@@ -0,0 +1,58 @@
/* jshint node: true */
/*
*
* 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.
*
*/
'use strict';
var path = require('path');
var screenshotPath = global.SCREENSHOT_PATH || path.join(__dirname, '../../appium_screenshots/');
function generateScreenshotName() {
var date = new Date();
var month = date.getMonth() + 1;
var day = date.getDate();
var hour = date.getHours();
var min = date.getMinutes();
var sec = date.getSeconds();
month = (month < 10 ? "0" : "") + month;
day = (day < 10 ? "0" : "") + day;
hour = (hour < 10 ? "0" : "") + hour;
min = (min < 10 ? "0" : "") + min;
sec = (sec < 10 ? "0" : "") + sec;
return date.getFullYear() + '-' + month + '-' + day + '_' + hour + '.' + min + '.' + sec + '.png';
}
module.exports.saveScreenshot = function (driver) {
var oldContext;
return driver
.currentContext()
.then(function (cc) {
oldContext = cc;
})
.context('NATIVE_APP')
.saveScreenshot(screenshotPath + generateScreenshotName())
.then(function () {
return driver.context(oldContext);
});
};

View File

@@ -0,0 +1,68 @@
/* jshint node: true */
/*
*
* 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.
*
*/
'use strict';
var wd = global.WD || require('wd');
var driver;
module.exports.getDriver = function (platform, callback) {
var serverConfig = {
host: 'localhost',
port: 4723
},
driverConfig = {
browserName: '',
'appium-version': '1.5',
platformName: platform,
platformVersion: global.PLATFORM_VERSION || '',
deviceName: global.DEVICE_NAME || '',
app: global.PACKAGE_PATH,
autoAcceptAlerts: true,
};
if (process.env.CHROMEDRIVER_EXECUTABLE) {
driverConfig.chromedriverExecutable = process.env.CHROMEDRIVER_EXECUTABLE;
}
driver = wd.promiseChainRemote(serverConfig);
module.exports.configureLogging(driver);
return driver.init(driverConfig).setImplicitWaitTimeout(10000)
.sleep(20000) // wait for the app to load
.then(callback);
};
module.exports.getWD = function () {
return wd;
};
module.exports.configureLogging = function (driver) {
driver.on('status', function (info) {
console.log(info);
});
driver.on('command', function (meth, path, data) {
console.log(' > ' + meth, path, data || '');
});
driver.on('http', function (meth, path, data) {
console.log(' > ' + meth, path, data || '');
});
};

View File

@@ -0,0 +1,288 @@
/*jshint node: true, jasmine: true */
/*
*
* 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.
*
*/
// these tests are meant to be executed by Cordova Medic Appium runner
// you can find it here: https://github.com/apache/cordova-medic/
// it is not necessary to do a full CI setup to run these tests
// just run "node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-camera"
'use strict';
var wdHelper = require('../helpers/wdHelper');
var wd = wdHelper.getWD();
var isDevice = global.DEVICE;
var cameraConstants = require('../../www/CameraConstants');
var cameraHelper = require('../helpers/cameraHelper');
var screenshotHelper = require('../helpers/screenshotHelper');
var MINUTE = 60 * 1000;
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW_1';
describe('Camera tests iOS.', function () {
var driver;
var webviewContext = DEFAULT_WEBVIEW_CONTEXT;
var startingMessage = 'Ready for action!';
function win() {
expect(true).toBe(true);
}
function fail(error) {
screenshotHelper.saveScreenshot(driver);
if (error && error.message) {
console.log('An error occured: ' + error.message);
expect(true).toFailWithMessage(error.message);
throw error.message;
}
if (error) {
console.log('Failed expectation: ' + error);
expect(true).toFailWithMessage(error);
throw error;
}
// no message provided :(
expect(true).toBe(false);
throw 'An error without description occured';
}
// generates test specs by combining all the specified options
// you can add more options to test more scenarios
function generateSpecs() {
var sourceTypes = [
cameraConstants.PictureSourceType.CAMERA,
cameraConstants.PictureSourceType.PHOTOLIBRARY
],
destinationTypes = cameraConstants.DestinationType,
encodingTypes = [
cameraConstants.EncodingType.JPEG,
cameraConstants.EncodingType.PNG
],
allowEditOptions = [
true,
false
];
return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions);
}
function getPicture(options, cancelCamera, skipUiInteractions) {
if (!options) {
options = {};
}
var command = "navigator.camera.getPicture(function (result) { document.getElementById('info').innerHTML = 'Success: ' + result.slice(0, 100); }, " +
"function (err) { document.getElementById('info').innerHTML = 'ERROR: ' + err; }," + JSON.stringify(options) + ");";
return driver
.sleep(2000)
.context(webviewContext)
.execute(command)
.sleep(5000)
.context('NATIVE_APP')
.then(function () {
if (skipUiInteractions) {
return;
}
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
return driver
.elementByName('Camera Roll')
.click()
.elementByXPath('//UIACollectionCell')
.click()
.then(function () {
if (options.hasOwnProperty('allowEdit') && options.allowEdit === true) {
return driver
.elementByName('Use')
.click();
}
return driver;
});
}
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) {
return driver
.elementByXPath('//UIACollectionCell')
.click()
.then(function () {
if (options.hasOwnProperty('allowEdit') && options.allowEdit === true) {
return driver
.elementByName('Use')
.click();
}
return driver;
});
}
if (cancelCamera) {
return driver
.elementByName('Cancel')
.click();
}
return driver
.elementByName('PhotoCapture')
.click()
.elementByName('Use Photo')
.click();
})
.sleep(3000);
}
function enterTest() {
return driver
.contexts(function (err, contexts) {
if (err) {
fail(err);
} else {
// if WEBVIEW context is available, use it
// if not, use NATIVE_APP
webviewContext = contexts[contexts.length - 1];
}
})
.then(function () {
return driver
.context(webviewContext);
})
.fail(fail)
.elementById('info')
.fail(function () {
// unknown starting page: no 'info' div
// adding it manually
return driver
.execute('var info = document.createElement("div"); ' +
'info.id = "info"' +
'document.body.appendChild(info);')
.fail(fail);
})
.execute('document.getElementById("info").innerHTML = "' + startingMessage + '";')
.fail(fail);
}
function checkPicture(shouldLoad) {
return driver
.contexts(function (err, contexts) {
// if WEBVIEW context is available, use it
// if not, use NATIVE_APP
webviewContext = contexts[contexts.length - 1];
})
.context(webviewContext)
.elementById('info')
.getAttribute('innerHTML')
.then(function (html) {
if (html.indexOf(startingMessage) >= 0) {
expect(true).toFailWithMessage('No callback was fired');
} else if (shouldLoad) {
expect(html.length).toBeGreaterThan(0);
if (html.indexOf('ERROR') >= 0) {
expect(true).toFailWithMessage(html);
}
} else {
if (html.indexOf('ERROR') === -1) {
expect(true).toFailWithMessage('Unexpected success callback with result: ' + html);
}
expect(html.indexOf('ERROR')).toBe(0);
}
})
.context('NATIVE_APP');
}
function runCombinedSpec(spec) {
return enterTest()
.then(function () {
return getPicture(spec.options);
})
.then(function () {
return checkPicture(true);
})
.then(win, fail);
}
beforeEach(function () {
jasmine.addMatchers({
toFailWithMessage : function () {
return {
compare: function (actual, msg) {
console.log('Failing with message: ' + msg);
var result = {
pass: false,
message: msg
};
return result;
}
};
}
});
});
it('camera.ui.util Configuring driver and starting a session', function (done) {
driver = wdHelper.getDriver('iOS', done);
}, 3 * MINUTE);
describe('Specs.', function () {
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
it('camera.ui.spec.1 Selecting only videos', function (done) {
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
mediaType: cameraConstants.MediaType.VIDEO };
enterTest()
.then(function () { return getPicture(options, false, true); }) // skip ui unteractions
.sleep(5000)
.elementByName('Videos')
.then(win, fail)
.elementByName('Cancel')
.click()
.finally(done);
}, 3 * MINUTE);
// getPicture(), then dismiss
// wait for the error callback to bee called
it('camera.ui.spec.2 Dismissing the camera', function (done) {
// camera is not available on iOS simulator
if (!isDevice) {
pending();
}
var options = { sourceType: cameraConstants.PictureSourceType.CAMERA };
enterTest()
.then(function () {
return getPicture(options, true);
})
.then(function () {
return checkPicture(false);
})
.elementByXPath('//UIAStaticText[contains(@label,"no image selected")]')
.then(function () {
return checkPicture(false);
}, fail)
.finally(done);
}, 3 * MINUTE);
// combine various options for getPicture()
generateSpecs().forEach(function (spec) {
it('camera.ui.spec.3.' + spec.id + ' Combining options', function (done) {
// camera is not available on iOS simulator
if (!isDevice) {
pending();
}
runCombinedSpec(spec).then(done);
}, 3 * MINUTE);
});
});
it('camera.ui.util.4 Destroy the session', function (done) {
driver.quit(done);
}, 10000);
});

199
jsdoc2md/TEMPLATE.md Normal file
View File

@@ -0,0 +1,199 @@
{{>cdv-license~}}
[![Build Status](https://travis-ci.org/apache/cordova-plugin-camera.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-camera)
# cordova-plugin-camera
This plugin defines a global `navigator.camera` object, which provides an API for taking pictures and for choosing images from
the system's image library.
{{>cdv-header device-ready-warning-obj='navigator.camera' npmName='cordova-plugin-camera' cprName='org.apache.cordova.camera' pluginName='Plugin Camera' repoUrl='https://github.com/apache/cordova-plugin-camera' }}
---
# API Reference
{{#orphans~}}
{{>member-index}}
{{/orphans}}
* [CameraPopoverHandle](#module_CameraPopoverHandle)
* [CameraPopoverOptions](#module_CameraPopoverOptions)
---
{{#modules~}}
{{>header~}}
{{>body~}}
{{>members~}}
---
{{/modules}}
## `camera.getPicture` Errata
#### Example <a name="camera-getPicture-examples"></a>
Take a photo and retrieve it as a Base64-encoded image:
navigator.camera.getPicture(onSuccess, onFail, { quality: 50,
destinationType: Camera.DestinationType.DATA_URL
});
function onSuccess(imageData) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + imageData;
}
function onFail(message) {
alert('Failed because: ' + message);
}
Take a photo and retrieve the image's file location:
navigator.camera.getPicture(onSuccess, onFail, { quality: 50,
destinationType: Camera.DestinationType.FILE_URI });
function onSuccess(imageURI) {
var image = document.getElementById('myImage');
image.src = imageURI;
}
function onFail(message) {
alert('Failed because: ' + message);
}
#### Preferences (iOS)
- __CameraUsesGeolocation__ (boolean, defaults to false). For capturing JPEGs, set to true to get geolocation data in the EXIF header. This will trigger a request for geolocation permissions if set to true.
<preference name="CameraUsesGeolocation" value="false" />
#### Amazon Fire OS Quirks <a name="camera-getPicture-quirks"></a>
Amazon Fire OS uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the Cordova activity is restored.
#### Android Quirks
Android uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the result from the plugin call will be delivered via the resume event.
See [the Android Lifecycle guide][android_lifecycle]
for more information. The `pendingResult.result` value will contain the value that
would be passed to the callbacks (either the URI/URL or an error message). Check
the `pendingResult.pluginStatus` to determine whether or not the call was
successful.
#### Browser Quirks
Can only return photos as Base64-encoded image.
#### Firefox OS Quirks
Camera plugin is currently implemented using [Web Activities][web_activities].
#### iOS Quirks
Including a JavaScript `alert()` in either of the callback functions
can cause problems. Wrap the alert within a `setTimeout()` to allow
the iOS image picker or popover to fully close before the alert
displays:
setTimeout(function() {
// do your thing here!
}, 0);
#### Windows Phone 7 Quirks
Invoking the native camera application while the device is connected
via Zune does not work, and triggers an error callback.
#### Tizen Quirks
Tizen only supports a `destinationType` of
`Camera.DestinationType.FILE_URI` and a `sourceType` of
`Camera.PictureSourceType.PHOTOLIBRARY`.
## `CameraOptions` Errata <a name="CameraOptions-quirks"></a>
#### Amazon Fire OS Quirks
- Any `cameraDirection` value results in a back-facing photo.
- Ignores the `allowEdit` parameter.
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
#### Android Quirks
- Any `cameraDirection` value results in a back-facing photo.
- **`allowEdit` is unpredictable on Android and it should not be used!** The Android implementation of this plugin tries to find and use an application on the user's device to do image cropping. The plugin has no control over what application the user selects to perform the image cropping and it is very possible that the user could choose an incompatible option and cause the plugin to fail. This sometimes works because most devices come with an application that handles cropping in a way that is compatible with this plugin (Google Plus Photos), but it is unwise to rely on that being the case. If image editing is essential to your application, consider seeking a third party library or plugin that provides its own image editing utility for a more robust solution.
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
- Ignores the `encodingType` parameter if the image is unedited (i.e. `quality` is 100, `correctOrientation` is false, and no `targetHeight` or `targetWidth` are specified). The `CAMERA` source will always return the JPEG file given by the native camera and the `PHOTOLIBRARY` and `SAVEDPHOTOALBUM` sources will return the selected file in its existing encoding.
#### BlackBerry 10 Quirks
- Ignores the `quality` parameter.
- Ignores the `allowEdit` parameter.
- `Camera.MediaType` is not supported.
- Ignores the `correctOrientation` parameter.
- Ignores the `cameraDirection` parameter.
#### Firefox OS Quirks
- Ignores the `quality` parameter.
- `Camera.DestinationType` is ignored and equals `1` (image file URI)
- Ignores the `allowEdit` parameter.
- Ignores the `PictureSourceType` parameter (user chooses it in a dialog window)
- Ignores the `encodingType`
- Ignores the `targetWidth` and `targetHeight`
- `Camera.MediaType` is not supported.
- Ignores the `correctOrientation` parameter.
- Ignores the `cameraDirection` parameter.
#### iOS Quirks
- When using `destinationType.FILE_URI`, photos are saved in the application's temporary directory. The contents of the application's temporary directory is deleted when the application ends.
- When using `destinationType.NATIVE_URI` and `sourceType.CAMERA`, photos are saved in the saved photo album regardless on the value of `saveToPhotoAlbum` parameter.
#### Tizen Quirks
- options not supported
- always returns a FILE URI
#### Windows Phone 7 and 8 Quirks
- Ignores the `allowEdit` parameter.
- Ignores the `correctOrientation` parameter.
- Ignores the `cameraDirection` parameter.
- Ignores the `saveToPhotoAlbum` parameter. IMPORTANT: All images taken with the WP8/8 Cordova camera API are always copied to the phone's camera roll. Depending on the user's settings, this could also mean the image is auto-uploaded to their OneDrive. This could potentially mean the image is available to a wider audience than your app intended. If this is a blocker for your application, you will need to implement the CameraCaptureTask as [documented on MSDN][msdn_wp8_docs]. You may also comment or up-vote the related issue in the [issue tracker][wp8_bug].
- Ignores the `mediaType` property of `cameraOptions` as the Windows Phone SDK does not provide a way to choose videos from PHOTOLIBRARY.
[android_lifecycle]: http://cordova.apache.org/docs/en/dev/guide/platforms/android/lifecycle.html
[web_activities]: https://hacks.mozilla.org/2013/01/introducing-web-activities/
[wp8_bug]: https://issues.apache.org/jira/browse/CB-2083
[msdn_wp8_docs]: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394006.aspx

View File

@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-camera",
"version": "1.2.0",
"version": "2.1.1",
"description": "Cordova Camera Plugin",
"cordova": {
"id": "cordova-plugin-camera",
@@ -41,6 +41,18 @@
"peerDependencies": {
"cordova-plugin-file": ">=2.0.0"
},
"scripts": {
"precommit": "npm run gen-docs && git add README.md",
"gen-docs": "jsdoc2md --template \"jsdoc2md/TEMPLATE.md\" \"www/**/*.js\" --plugin \"dmd-plugin-cordova-plugin\" > README.md",
"test": "npm run jshint",
"jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests"
},
"author": "Apache Software Foundation",
"license": "Apache 2.0"
"license": "Apache-2.0",
"devDependencies": {
"dmd-plugin-cordova-plugin": "^0.1.0",
"husky": "^0.10.1",
"jsdoc-to-markdown": "^1.2.0",
"jshint": "^2.6.0"
}
}

View File

@@ -22,7 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rim="http://www.blackberry.com/ns/widgets"
id="cordova-plugin-camera"
version="1.2.0">
version="2.1.1">
<name>Camera</name>
<description>Cordova Camera Plugin</description>
<license>Apache 2.0</license>
@@ -50,12 +50,12 @@
<feature name="Camera">
<param name="firefoxos-package" value="Camera" />
</feature>
</config-file>
</config-file>
<js-module src="src/firefoxos/CameraProxy.js" name="CameraProxy">
<runs />
</js-module>
</platform>
</platform>
<!-- android -->
<platform name="android">
@@ -71,6 +71,7 @@
<source-file src="src/android/CameraLauncher.java" target-dir="src/org/apache/cordova/camera" />
<source-file src="src/android/FileHelper.java" target-dir="src/org/apache/cordova/camera" />
<source-file src="src/android/ExifHelper.java" target-dir="src/org/apache/cordova/camera" />
<source-file src="src/android/PermissionHelper.java" target-dir="src/org/apache/cordova/camera" />
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
<clobbers target="CameraPopoverHandle" />
@@ -98,7 +99,7 @@
</js-module>
</platform>
<!-- ubuntu -->
<platform name="ubuntu">
<config-file target="config.xml" parent="/*">
@@ -147,11 +148,11 @@
<framework src="MobileCoreServices.framework" />
<framework src="CoreGraphics.framework" />
<framework src="AVFoundation.framework" />
<config-file target="*-Info.plist" parent="NSLocationWhenInUseUsageDescription">
<string></string>
</config-file>
</platform>
<!-- blackberry10 -->

View File

@@ -32,11 +32,13 @@ import java.util.Date;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaResourceApi;
import org.apache.cordova.LOG;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;
import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentValues;
@@ -49,6 +51,7 @@ import android.graphics.Matrix;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
@@ -79,7 +82,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
private static final String GET_PICTURE = "Get Picture";
private static final String GET_VIDEO = "Get Video";
private static final String GET_All = "Get All";
public static final int PERMISSION_DENIED_ERROR = 20;
public static final int TAKE_PIC_SEC = 0;
public static final int SAVE_TO_ALBUM_SEC = 1;
private static final String LOG_TAG = "CameraLauncher";
//Where did this come from?
@@ -91,11 +98,15 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
private Uri imageUri; // Uri of captured image
private int encodingType; // Type of encoding to use
private int mediaType; // What type of media to retrieve
private int destType; // Source type (needs to be saved for the permission handling)
private int srcType; // Destination type (needs to be saved for permission handling)
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
private boolean correctOrientation; // Should the pictures orientation be corrected
private boolean orientationCorrected; // Has the picture's orientation been corrected
private boolean allowEdit; // Should we allow the user to crop the image.
protected final static String[] permissions = { Manifest.permission.READ_EXTERNAL_STORAGE };
public CallbackContext callbackContext;
private int numPics;
@@ -115,8 +126,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
this.callbackContext = callbackContext;
if (action.equals("takePicture")) {
int srcType = CAMERA;
int destType = FILE_URI;
this.srcType = CAMERA;
this.destType = FILE_URI;
this.saveToPhotoAlbum = false;
this.targetHeight = 0;
this.targetWidth = 0;
@@ -124,9 +135,10 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
this.mediaType = PICTURE;
this.mQuality = 80;
//Take the values from the arguments if they're not already defined (this is tricky)
this.destType = args.getInt(1);
this.srcType = args.getInt(2);
this.mQuality = args.getInt(0);
destType = args.getInt(1);
srcType = args.getInt(2);
this.targetWidth = args.getInt(3);
this.targetHeight = args.getInt(4);
this.encodingType = args.getInt(5);
@@ -144,12 +156,24 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
this.targetHeight = -1;
}
// We don't return full-quality PNG files. The camera outputs a JPEG
// so requesting it as a PNG provides no actual benefit
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
!this.correctOrientation && this.encodingType == PNG && this.srcType == CAMERA) {
this.encodingType = JPEG;
}
try {
if (srcType == CAMERA) {
this.takePicture(destType, encodingType);
if (this.srcType == CAMERA) {
this.callTakePicture(destType, encodingType);
}
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
this.getImage(srcType, destType, encodingType);
else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
// FIXME: Stop always requesting the permission
if(!PermissionHelper.hasPermission(this, permissions[0])) {
PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
} else {
this.getImage(this.srcType, destType, encodingType);
}
}
}
catch (IllegalArgumentException e)
@@ -159,11 +183,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
callbackContext.sendPluginResult(r);
return true;
}
PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
r.setKeepCallback(true);
callbackContext.sendPluginResult(r);
return true;
}
return false;
@@ -178,8 +202,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// SD Card Mounted
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
cache = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
"/Android/data/" + cordova.getActivity().getPackageName() + "/cache/");
cache = cordova.getActivity().getExternalCacheDir();
}
// Use internal storage
else {
@@ -205,7 +228,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
* @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 returnType, int encodingType) {
public void callTakePicture(int returnType, int encodingType) {
if (PermissionHelper.hasPermission(this, permissions[0])) {
takePicture(returnType, encodingType);
} else {
PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
}
}
public void takePicture(int returnType, int encodingType)
{
// Save the number of images currently on disk for later
this.numPics = queryImgDB(whichContentStore()).getCount();
@@ -241,15 +274,30 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
* @return a File object pointing to the temporary picture
*/
private File createCaptureFile(int encodingType) {
File photo = null;
return createCaptureFile(encodingType, "");
}
/**
* Create a file in the applications temporary directory based upon the supplied encoding.
*
* @param encodingType of the image to be taken
* @param fileName or resultant File object.
* @return a File object pointing to the temporary picture
*/
private File createCaptureFile(int encodingType, String fileName) {
if (fileName.isEmpty()) {
fileName = ".Pic";
}
if (encodingType == JPEG) {
photo = new File(getTempDirectoryPath(), ".Pic.jpg");
fileName = fileName + ".jpg";
} else if (encodingType == PNG) {
photo = new File(getTempDirectoryPath(), ".Pic.png");
fileName = fileName + ".png";
} else {
throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
}
return photo;
return new File(getTempDirectoryPath(), fileName);
}
@@ -260,7 +308,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
* @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.
* @param encodingType
* @param encodingType
*/
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
// TODO: Images from kitkat filechooser not going into crop function
@@ -311,7 +359,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
/**
* Brings up the UI to perform crop on passed image URI
*
*
* @param picUri
*/
private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
@@ -334,7 +382,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
cropIntent.putExtra("aspectY", 1);
}
// create new file handle to get full resolution crop
croppedUri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
cropIntent.putExtra("output", croppedUri);
// start the activity - we handle returning in onActivityResult
@@ -367,43 +415,49 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// Create an ExifHelper to save the exif data that is lost during compression
ExifHelper exif = new ExifHelper();
String sourcePath;
try {
if(allowEdit && croppedUri != null)
{
sourcePath = FileHelper.stripFileProtocol(croppedUri.toString());
}
else
{
sourcePath = getTempDirectoryPath() + "/.Pic.jpg";
}
String sourcePath = (this.allowEdit && this.croppedUri != null) ?
FileHelper.stripFileProtocol(this.croppedUri.toString()) :
FileHelper.stripFileProtocol(this.imageUri.toString());
//We don't support PNG, so let's not pretend we do
exif.createInFile(getTempDirectoryPath() + "/.Pic.jpg");
exif.readExifData();
rotate = exif.getOrientation();
if (this.encodingType == JPEG) {
try {
//We don't support PNG, so let's not pretend we do
exif.createInFile(sourcePath);
exif.readExifData();
rotate = exif.getOrientation();
} catch (IOException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
Bitmap bitmap = null;
Uri uri = null;
Uri galleryUri = null;
// CB-5479 When this option is given the unchanged image should be saved
// in the gallery and the modified image is saved in the temporary
// directory
if (this.saveToPhotoAlbum) {
galleryUri = Uri.fromFile(new File(getPicutresPath()));
if(this.allowEdit && this.croppedUri != null) {
writeUncompressedImage(this.croppedUri, galleryUri);
} else {
writeUncompressedImage(this.imageUri, galleryUri);
}
refreshGallery(galleryUri);
}
// If sending base64 image back
if (destType == DATA_URL) {
if(croppedUri != null) {
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(croppedUri.toString()));
}
else
{
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
}
bitmap = getScaledBitmap(sourcePath);
if (bitmap == null) {
// Try to get the bitmap from intent.
bitmap = (Bitmap)intent.getExtras().get("data");
}
// Double-check the bitmap.
if (bitmap == null) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
@@ -415,34 +469,44 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
bitmap = getRotatedBitmap(rotate, bitmap, exif);
}
this.processPicture(bitmap);
checkForDuplicateImage(DATA_URL);
this.processPicture(bitmap, this.encodingType);
if (!this.saveToPhotoAlbum) {
checkForDuplicateImage(DATA_URL);
}
}
// If sending filename back
else if (destType == FILE_URI || destType == NATIVE_URI) {
uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
if (this.saveToPhotoAlbum) {
//Create a URI on the filesystem so that we can write the file.
uri = Uri.fromFile(new File(getPicutresPath()));
} else {
uri = Uri.fromFile(new File(getTempDirectoryPath(), System.currentTimeMillis() + ".jpg"));
}
if (uri == null) {
this.failPicture("Error capturing image - no media storage found.");
return;
}
// If all this is true we shouldn't compress the image.
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
!this.correctOrientation) {
writeUncompressedImage(uri);
this.callbackContext.success(uri.toString());
// If we saved the uncompressed photo to the album, we can just
// return the URI we already created
if (this.saveToPhotoAlbum) {
this.callbackContext.success(galleryUri.toString());
} else {
Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
if(this.allowEdit && this.croppedUri != null) {
writeUncompressedImage(this.croppedUri, uri);
} else {
writeUncompressedImage(this.imageUri, uri);
}
this.callbackContext.success(uri.toString());
}
} else {
bitmap = getScaledBitmap(FileHelper.stripFileProtocol(imageUri.toString()));
Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
bitmap = getScaledBitmap(sourcePath);
// Double-check the bitmap.
if (bitmap == null) {
Log.d(LOG_TAG, "I either have a null image path or bitmap");
this.failPicture("Unable to create bitmap!");
return;
}
if (rotate != 0 && this.correctOrientation) {
bitmap = getRotatedBitmap(rotate, bitmap, exif);
@@ -450,7 +514,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// Add compressed version of captured image to returned media store Uri
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
CompressFormat compressFormat = encodingType == JPEG ?
CompressFormat.JPEG :
CompressFormat.PNG;
bitmap.compress(compressFormat, this.mQuality, os);
os.close();
// Restore exif data to file
@@ -461,12 +529,6 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
exif.writeExifData();
}
//Broadcast change to File System on MediaStore
if(this.saveToPhotoAlbum) {
refreshGallery(uri);
}
// Send Uri back to JavaScript for viewing image
this.callbackContext.success(uri.toString());
@@ -475,14 +537,14 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
throw new IllegalStateException();
}
this.cleanup(FILE_URI, this.imageUri, uri, bitmap);
this.cleanup(FILE_URI, this.imageUri, galleryUri, bitmap);
bitmap = null;
}
private String getPicutresPath()
{
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "IMG_" + timeStamp + ".jpg";
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
File storageDir = Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES);
String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
@@ -498,17 +560,28 @@ private void refreshGallery(Uri contentUri)
private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
// Create an ExifHelper to save the exif data that is lost during compression
String modifiedPath = getTempDirectoryPath() + "/modified.jpg";
OutputStream os = new FileOutputStream(modifiedPath);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
os.close();
// Some content: URIs do not map to file paths (e.g. picasa).
String realPath = FileHelper.getRealPath(uri, this.cordova);
ExifHelper exif = new ExifHelper();
// Get filename from uri
String fileName = realPath != null ?
realPath.substring(realPath.lastIndexOf('/') + 1) :
"modified." + (this.encodingType == JPEG ? "jpg" : "png");
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
OutputStream os = new FileOutputStream(modifiedPath);
CompressFormat compressFormat = this.encodingType == JPEG ?
CompressFormat.JPEG :
CompressFormat.PNG;
bitmap.compress(compressFormat, this.mQuality, os);
os.close();
if (realPath != null && this.encodingType == JPEG) {
// Create an ExifHelper to save the exif data that is lost during compression
ExifHelper exif = new ExifHelper();
try {
exif.createInFile(realPath);
exif.readExifData();
@@ -524,6 +597,8 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
return modifiedPath;
}
/**
* Applies all needed transformation to the image received from the gallery.
*
@@ -542,10 +617,13 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
}
int rotate = 0;
String fileLocation = FileHelper.getRealPath(uri, this.cordova);
Log.d(LOG_TAG, "File locaton is: " + fileLocation);
// If you ask for video or all media type you will automatically get back a file URI
// and there will be no attempt to resize any returned data
if (this.mediaType != PICTURE) {
this.callbackContext.success(uri.toString());
this.callbackContext.success(fileLocation);
}
else {
// This is a special case to just return the path as no scaling,
@@ -591,7 +669,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
// If sending base64 image back
if (destType == DATA_URL) {
this.processPicture(bitmap);
this.processPicture(bitmap, this.encodingType);
}
// If sending filename back
@@ -604,13 +682,14 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
// The modified image is cached by the app in order to get around this and not have to delete you
// application cache I'm adding the current system time to the end of the file url.
this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis());
} catch (Exception e) {
e.printStackTrace();
this.failPicture("Error retrieving image.");
}
}
else {
this.callbackContext.success(uri.toString());
this.callbackContext.success(fileLocation);
}
}
if (bitmap != null) {
@@ -621,7 +700,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
}
}
}
/**
* Called when the camera view exits.
*
@@ -667,7 +746,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
try {
if(this.allowEdit)
{
Uri tmpFile = Uri.fromFile(new File(getTempDirectoryPath(), ".Pic.jpg"));
Uri tmpFile = Uri.fromFile(createCaptureFile(this.encodingType));
performCrop(tmpFile, destType, intent);
}
else {
@@ -692,7 +771,13 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
// If retrieving photo from library
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
if (resultCode == Activity.RESULT_OK && intent != null) {
this.processResultFromGallery(destType, intent);
final Intent i = intent;
final int finalDestType = destType;
cordova.getThreadPool().execute(new Runnable() {
public void run() {
processResultFromGallery(finalDestType, i);
}
});
}
else if (resultCode == Activity.RESULT_CANCELED) {
this.failPicture("Selection cancelled.");
@@ -760,13 +845,13 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
* @throws FileNotFoundException
* @throws IOException
*/
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
private void writeUncompressedImage(Uri src, Uri dest) throws FileNotFoundException,
IOException {
FileInputStream fis = null;
OutputStream os = null;
try {
fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
fis = new FileInputStream(FileHelper.stripFileProtocol(src.toString()));
os = this.cordova.getActivity().getContentResolver().openOutputStream(dest);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
@@ -819,7 +904,7 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
*
* @param imagePath
* @return
* @throws IOException
* @throws IOException
*/
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
// If no new width or height were specified return the original bitmap
@@ -857,13 +942,13 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
}
}
}
//CB-2292: WTF? Why is the width null?
if(options.outWidth == 0 || options.outHeight == 0)
{
return null;
}
// determine the correct aspect ratio
int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
@@ -1040,10 +1125,14 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
*
* @param bitmap
*/
public void processPicture(Bitmap bitmap) {
public void processPicture(Bitmap bitmap, int encodingType) {
ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
CompressFormat compressFormat = encodingType == JPEG ?
CompressFormat.JPEG :
CompressFormat.PNG;
try {
if (bitmap.compress(CompressFormat.JPEG, mQuality, jpeg_data)) {
if (bitmap.compress(compressFormat, mQuality, jpeg_data)) {
byte[] code = jpeg_data.toByteArray();
byte[] output = Base64.encode(code, Base64.NO_WRAP);
String js_out = new String(output);
@@ -1088,4 +1177,81 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
public void onScanCompleted(String path, Uri uri) {
this.conn.disconnect();
}
public void onRequestPermissionResult(int requestCode, String[] permissions,
int[] grantResults) throws JSONException
{
for(int r:grantResults)
{
if(r == PackageManager.PERMISSION_DENIED)
{
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
return;
}
}
switch(requestCode)
{
case TAKE_PIC_SEC:
takePicture(this.destType, this.encodingType);
break;
case SAVE_TO_ALBUM_SEC:
this.getImage(this.srcType, this.destType, this.encodingType);
break;
}
}
/**
* Taking or choosing a picture launches another Activity, so we need to implement the
* save/restore APIs to handle the case where the CordovaActivity is killed by the OS
* before we get the launched Activity's result.
*/
public Bundle onSaveInstanceState() {
Bundle state = new Bundle();
state.putInt("destType", this.destType);
state.putInt("srcType", this.srcType);
state.putInt("mQuality", this.mQuality);
state.putInt("targetWidth", this.targetWidth);
state.putInt("targetHeight", this.targetHeight);
state.putInt("encodingType", this.encodingType);
state.putInt("mediaType", this.mediaType);
state.putInt("numPics", this.numPics);
state.putBoolean("allowEdit", this.allowEdit);
state.putBoolean("correctOrientation", this.correctOrientation);
state.putBoolean("saveToPhotoAlbum", this.saveToPhotoAlbum);
if(this.croppedUri != null) {
state.putString("croppedUri", this.croppedUri.toString());
}
if(this.imageUri != null) {
state.putString("imageUri", this.imageUri.toString());
}
return state;
}
public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {
this.destType = state.getInt("destType");
this.srcType = state.getInt("srcType");
this.mQuality = state.getInt("mQuality");
this.targetWidth = state.getInt("targetWidth");
this.targetHeight = state.getInt("targetHeight");
this.encodingType = state.getInt("encodingType");
this.mediaType = state.getInt("mediaType");
this.numPics = state.getInt("numPics");
this.allowEdit = state.getBoolean("allowEdit");
this.correctOrientation = state.getBoolean("correctOrientation");
this.saveToPhotoAlbum = state.getBoolean("saveToPhotoAlbum");
if(state.containsKey("croppedUri")) {
this.croppedUri = Uri.parse(state.getString("croppedUri"));
}
if(state.containsKey("imageUri")) {
this.imageUri = Uri.parse(state.getString("imageUri"));
}
this.callbackContext = callbackContext;
}
}

View File

@@ -17,11 +17,13 @@
package org.apache.cordova.camera;
import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.webkit.MimeTypeMap;
@@ -77,33 +79,70 @@ public class FileHelper {
}
@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(Context context, Uri uri) {
String filePath = "";
try {
String wholeID = DocumentsContract.getDocumentId(uri);
public static String getRealPathFromURI_API19(final Context context, final Uri uri) {
// Split at colon, use second item in the array
String id = wholeID.indexOf(":") > -1 ? wholeID.split(":")[1] : wholeID.indexOf(";") > -1 ? wholeID
.split(";")[1] : wholeID;
// DocumentProvider
if ( DocumentsContract.isDocumentUri(context, uri)) {
String[] column = { MediaStore.Images.Media.DATA };
// ExternalStorageProvider
if (isExternalStorageDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
// where id is equal to
String sel = MediaStore.Images.Media._ID + "=?";
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, column,
sel, new String[] { id }, null);
int columnIndex = cursor.getColumnIndex(column[0]);
if (cursor.moveToFirst()) {
filePath = cursor.getString(columnIndex);
// TODO handle non-primary volumes
}
// DownloadsProvider
else if (isDownloadsDocument(uri)) {
final String id = DocumentsContract.getDocumentId(uri);
final Uri contentUri = ContentUris.withAppendedId(
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
}
// MediaProvider
else if (isMediaDocument(uri)) {
final String docId = DocumentsContract.getDocumentId(uri);
final String[] split = docId.split(":");
final String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
final String selection = "_id=?";
final String[] selectionArgs = new String[] {
split[1]
};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
cursor.close();
} catch (Exception e) {
filePath = "";
}
return filePath;
// MediaStore (and general)
else if ("content".equalsIgnoreCase(uri.getScheme())) {
// Return the remote address
if (isGooglePhotosUri(uri))
return uri.getLastPathSegment();
return getDataColumn(context, uri, null, null);
}
// File
else if ("file".equalsIgnoreCase(uri.getScheme())) {
return uri.getPath();
}
return null;
}
@SuppressLint("NewApi")
@@ -228,4 +267,74 @@ public class FileHelper {
return mimeType;
}
/**
* Get the value of the data column for this Uri. This is useful for
* MediaStore Uris, and other file-based ContentProviders.
*
* @param context The context.
* @param uri The Uri to query.
* @param selection (Optional) Filter used in the query.
* @param selectionArgs (Optional) Selection arguments used in the query.
* @return The value of the _data column, which is typically a file path.
* @author paulburke
*/
public static String getDataColumn(Context context, Uri uri, String selection,
String[] selectionArgs) {
Cursor cursor = null;
final String column = "_data";
final String[] projection = {
column
};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
null);
if (cursor != null && cursor.moveToFirst()) {
final int column_index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(column_index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
* @author paulburke
*/
public static boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
* @author paulburke
*/
public static boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
* @author paulburke
*/
public static boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public static boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}

View File

@@ -0,0 +1,138 @@
/*
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.
*/
package org.apache.cordova.camera;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.LOG;
import android.content.pm.PackageManager;
/**
* This class provides reflective methods for permission requesting and checking so that plugins
* written for cordova-android 5.0.0+ can still compile with earlier cordova-android versions.
*/
public class PermissionHelper {
private static final String LOG_TAG = "CordovaPermissionHelper";
/**
* Requests a "dangerous" permission for the application at runtime. This is a helper method
* alternative to cordovaInterface.requestPermission() that does not require the project to be
* built with cordova-android 5.0.0+
*
* @param plugin The plugin the permission is being requested for
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
* along with the result of the permission request
* @param permission The permission to be requested
*/
public static void requestPermission(CordovaPlugin plugin, int requestCode, String permission) {
PermissionHelper.requestPermissions(plugin, requestCode, new String[] {permission});
}
/**
* Requests "dangerous" permissions for the application at runtime. This is a helper method
* alternative to cordovaInterface.requestPermissions() that does not require the project to be
* built with cordova-android 5.0.0+
*
* @param plugin The plugin the permissions are being requested for
* @param requestCode A requestCode to be passed to the plugin's onRequestPermissionResult()
* along with the result of the permissions request
* @param permissions The permissions to be requested
*/
public static void requestPermissions(CordovaPlugin plugin, int requestCode, String[] permissions) {
try {
Method requestPermission = CordovaInterface.class.getDeclaredMethod(
"requestPermissions", CordovaPlugin.class, int.class, String[].class);
// If there is no exception, then this is cordova-android 5.0.0+
requestPermission.invoke(plugin.cordova, plugin, requestCode, permissions);
} catch (NoSuchMethodException noSuchMethodException) {
// cordova-android version is less than 5.0.0, so permission is implicitly granted
LOG.d(LOG_TAG, "No need to request permissions " + Arrays.toString(permissions));
// Notify the plugin that all were granted by using more reflection
deliverPermissionResult(plugin, requestCode, permissions);
} catch (IllegalAccessException illegalAccessException) {
// Should never be caught; this is a public method
LOG.e(LOG_TAG, "IllegalAccessException when requesting permissions " + Arrays.toString(permissions), illegalAccessException);
} catch(InvocationTargetException invocationTargetException) {
// This method does not throw any exceptions, so this should never be caught
LOG.e(LOG_TAG, "invocationTargetException when requesting permissions " + Arrays.toString(permissions), invocationTargetException);
}
}
/**
* Checks at runtime to see if the application has been granted a permission. This is a helper
* method alternative to cordovaInterface.hasPermission() that does not require the project to
* be built with cordova-android 5.0.0+
*
* @param plugin The plugin the permission is being checked against
* @param permission The permission to be checked
*
* @return True if the permission has already been granted and false otherwise
*/
public static boolean hasPermission(CordovaPlugin plugin, String permission) {
try {
Method hasPermission = CordovaInterface.class.getDeclaredMethod("hasPermission", String.class);
// If there is no exception, then this is cordova-android 5.0.0+
return (Boolean) hasPermission.invoke(plugin.cordova, permission);
} catch (NoSuchMethodException noSuchMethodException) {
// cordova-android version is less than 5.0.0, so permission is implicitly granted
LOG.d(LOG_TAG, "No need to check for permission " + permission);
return true;
} catch (IllegalAccessException illegalAccessException) {
// Should never be caught; this is a public method
LOG.e(LOG_TAG, "IllegalAccessException when checking permission " + permission, illegalAccessException);
} catch(InvocationTargetException invocationTargetException) {
// This method does not throw any exceptions, so this should never be caught
LOG.e(LOG_TAG, "invocationTargetException when checking permission " + permission, invocationTargetException);
}
return false;
}
private static void deliverPermissionResult(CordovaPlugin plugin, int requestCode, String[] permissions) {
// Generate the request results
int[] requestResults = new int[permissions.length];
Arrays.fill(requestResults, PackageManager.PERMISSION_GRANTED);
try {
Method onRequestPermissionResult = CordovaPlugin.class.getDeclaredMethod(
"onRequestPermissionResult", int.class, String[].class, int[].class);
onRequestPermissionResult.invoke(plugin, requestCode, permissions, requestResults);
} catch (NoSuchMethodException noSuchMethodException) {
// Should never be caught since the plugin must be written for cordova-android 5.0.0+ if it
// made it to this point
LOG.e(LOG_TAG, "NoSuchMethodException when delivering permissions results", noSuchMethodException);
} catch (IllegalAccessException illegalAccessException) {
// Should never be caught; this is a public method
LOG.e(LOG_TAG, "IllegalAccessException when delivering permissions results", illegalAccessException);
} catch(InvocationTargetException invocationTargetException) {
// This method may throw a JSONException. We are just duplicating cordova-android's
// exception handling behavior here; all it does is log the exception in CordovaActivity,
// print the stacktrace, and ignore it
LOG.e(LOG_TAG, "InvocationTargetException when delivering permissions results", invocationTargetException);
}
}
}

View File

@@ -18,6 +18,9 @@
* under the License.
*
*/
/* globals qnx, FileError, PluginResult */
var PictureSourceType = {
PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
CAMERA : 1, // Take picture from camera
@@ -40,7 +43,7 @@ window.qnx.webplatform.getApplication().invocation.queryTargets(
},
function (error, targets) {
invokeAvailable = !error && targets && targets instanceof Array &&
targets.filter(function (t) { return t.default === 'sys.camera.card' }).length > 0;
targets.filter(function (t) { return t.default === 'sys.camera.card'; }).length > 0;
}
);
@@ -85,7 +88,7 @@ function showCameraDialog (done, cancel, fail) {
//create unique name for saved file (same pattern as BB10 camera app)
function imgName() {
var date = new Date(),
pad = function (n) { return n < 10 ? '0' + n : n };
pad = function (n) { return n < 10 ? '0' + n : n; };
return 'IMG_' + date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) + '_' +
pad(date.getHours()) + pad(date.getMinutes()) + pad(date.getSeconds()) + '.png';
}
@@ -145,7 +148,7 @@ function encodeBase64(filePath, callback) {
default:
msg += "Unknown Error";
break;
};
}
// set it back to original value
window.qnx.webplatform.getController().setFileSystemSandbox = sandbox;

View File

@@ -19,17 +19,19 @@
*
*/
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
function takePicture(success, error, opts) {
if (opts && opts[2] === 1) {
capture(success, error);
} else {
var input = document.createElement('input');
input.style.position = 'relative';
input.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
input.type = 'file';
input.name = 'files[]';
input.onchange = function(inputEvent) {
var canvas = document.createElement('canvas');
var reader = new FileReader();
reader.onload = function(readerEvent) {
input.parentNode.removeChild(input);
@@ -37,7 +39,7 @@ function takePicture(success, error, opts) {
var imageData = readerEvent.target.result;
return success(imageData.substr(imageData.indexOf(',') + 1));
}
};
reader.readAsDataURL(inputEvent.target.files[0]);
};
@@ -51,6 +53,11 @@ function capture(success, errorCallback) {
var video = document.createElement('video');
var button = document.createElement('button');
var parent = document.createElement('div');
parent.style.position = 'relative';
parent.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
parent.appendChild(video);
parent.appendChild(button);
video.width = 320;
video.height = 240;
@@ -60,18 +67,24 @@ function capture(success, errorCallback) {
// create a canvas and capture a frame from video stream
var canvas = document.createElement('canvas');
canvas.getContext('2d').drawImage(video, 0, 0, 320, 240);
// convert image stored in canvas to base64 encoded image
var imageData = canvas.toDataURL('img/png');
imageData = imageData.replace('data:image/png;base64,', '');
// stop video stream, remove video and button
localMediaStream.stop();
video.parentNode.removeChild(video);
button.parentNode.removeChild(button);
// stop video stream, remove video and button.
// Note that MediaStream.stop() is deprecated as of Chrome 47.
if (localMediaStream.stop) {
localMediaStream.stop();
} else {
localMediaStream.getTracks().forEach(function (track) {
track.stop();
});
}
parent.parentNode.removeChild(parent);
return success(imageData);
}
};
navigator.getUserMedia = navigator.getUserMedia ||
navigator.webkitGetUserMedia ||
@@ -83,9 +96,8 @@ function capture(success, errorCallback) {
video.src = window.URL.createObjectURL(localMediaStream);
video.play();
document.body.appendChild(video);
document.body.appendChild(button);
}
document.body.appendChild(parent);
};
if (navigator.getUserMedia) {
navigator.getUserMedia({video: true, audio: true}, successCallback, errorCallback);

View File

@@ -19,6 +19,8 @@
*
*/
/* globals MozActivity */
function takePicture(success, error, opts) {
var pick = new MozActivity({
name: "pick",
@@ -32,7 +34,7 @@ function takePicture(success, error, opts) {
pick.onsuccess = function() {
// image is returned as Blob in this.result.blob
// we need to call success with url or base64 encoded image
if (opts && opts.destinationType == 0) {
if (opts && opts.destinationType === 0) {
// TODO: base64
return;
}

View File

@@ -20,7 +20,6 @@
#import "CDVCamera.h"
#import "CDVJpegHeaderWriter.h"
#import "UIImage+CropScaleOrientation.h"
#import <Cordova/NSData+Base64.h>
#import <ImageIO/CGImageProperties.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AssetsLibrary/AssetsLibrary.h>
@@ -31,6 +30,10 @@
#import <MobileCoreServices/UTCoreTypes.h>
#import <objc/message.h>
#ifndef __CORDOVA_4_0_0
#import <Cordova/NSData+Base64.h>
#endif
#define CDV_PHOTO_PREFIX @"cdv_photo_"
static NSSet* org_apache_cordova_validArrowDirections;
@@ -38,9 +41,20 @@ static NSSet* org_apache_cordova_validArrowDirections;
static NSString* toBase64(NSData* data) {
SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
SEL s2 = NSSelectorFromString(@"base64EncodedString");
SEL realSel = [data respondsToSelector:s1] ? s1 : s2;
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:realSel];
return func(data, realSel);
SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
if ([data respondsToSelector:s1]) {
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
return func(data, s1);
} else if ([data respondsToSelector:s2]) {
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
return func(data, s2);
} else if ([data respondsToSelector:s3]) {
NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
return func(data, s3, 0);
} else {
return nil;
}
}
@implementation CDVPictureOptions
@@ -149,16 +163,19 @@ static NSString* toBase64(NSData* data) {
if (authStatus == AVAuthorizationStatusDenied ||
authStatus == AVAuthorizationStatusRestricted) {
// If iOS 8+, offer a link to the Settings app
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
NSString* settingsButton = (&UIApplicationOpenSettingsURLString != NULL)
? NSLocalizedString(@"Settings", nil)
: nil;
#pragma clang diagnostic pop
// Denied; show an alert
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:[[NSBundle mainBundle]
objectForInfoDictionaryKey:@"CFBundleDisplayName"]
message:NSLocalizedString(@"Access to the camera has been prohibited; please enable it in the Settings app to continue.", nil)
delegate:self
delegate:weakSelf
cancelButtonTitle:NSLocalizedString(@"OK", nil)
otherButtonTitles:settingsButton, nil] show];
});
@@ -202,7 +219,12 @@ static NSString* toBase64(NSData* data) {
{
// If Settings button (on iOS 8), open the settings app
if (buttonIndex == 1) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wtautological-pointer-compare"
if (&UIApplicationOpenSettingsURLString != NULL) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
#pragma clang diagnostic pop
}
// Dismiss the view
@@ -332,7 +354,7 @@ static NSString* toBase64(NSData* data) {
break;
case EncodingTypeJPEG:
{
if ((options.allowsEditing == NO) && (options.targetSize.width <= 0) && (options.targetSize.height <= 0) && (options.correctOrientation == NO)){
if ((options.allowsEditing == NO) && (options.targetSize.width <= 0) && (options.targetSize.height <= 0) && (options.correctOrientation == NO) && (([options.quality integerValue] == 100) || (options.sourceType != UIImagePickerControllerSourceTypeCamera))){
// use image unedited as requested , don't resize
data = UIImageJPEGRepresentation(image, 1.0);
} else {
@@ -408,7 +430,7 @@ static NSString* toBase64(NSData* data) {
return (scaledImage == nil ? image : scaledImage);
}
- (CDVPluginResult*)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info
- (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info completion:(void (^)(CDVPluginResult* res))completion
{
CDVPluginResult* result = nil;
BOOL saveToPhotoAlbum = options.saveToPhotoAlbum;
@@ -417,10 +439,28 @@ static NSString* toBase64(NSData* data) {
switch (options.destinationType) {
case DestinationTypeNativeUri:
{
NSURL* url = (NSURL*)[info objectForKey:UIImagePickerControllerReferenceURL];
NSString* nativeUri = [[self urlTransformer:url] absoluteString];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
NSURL* url = [info objectForKey:UIImagePickerControllerReferenceURL];
saveToPhotoAlbum = NO;
// If, for example, we use sourceType = Camera, URL might be nil because image is stored in memory.
// In this case we must save image to device before obtaining an URI.
if (url == nil) {
image = [self retrieveImage:info options:options];
ALAssetsLibrary* library = [ALAssetsLibrary new];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:^(NSURL *assetURL, NSError *error) {
CDVPluginResult* resultToReturn = nil;
if (error) {
resultToReturn = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
} else {
NSString* nativeUri = [[self urlTransformer:assetURL] absoluteString];
resultToReturn = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
}
completion(resultToReturn);
}];
return;
} else {
NSString* nativeUri = [[self urlTransformer:url] absoluteString];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
}
}
break;
case DestinationTypeFileUri:
@@ -446,7 +486,6 @@ static NSString* toBase64(NSData* data) {
{
image = [self retrieveImage:info options:options];
NSData* data = [self processImage:image info:info options:options];
if (data) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(data)];
}
@@ -460,8 +499,8 @@ static NSString* toBase64(NSData* data) {
ALAssetsLibrary* library = [ALAssetsLibrary new];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil];
}
return result;
completion(result);
}
- (CDVPluginResult*)resultForVideo:(NSDictionary*)info
@@ -480,13 +519,14 @@ static NSString* toBase64(NSData* data) {
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
result = [self resultForImage:cameraPicker.pictureOptions info:info];
[weakSelf resultForImage:cameraPicker.pictureOptions info:info completion:^(CDVPluginResult* res) {
[weakSelf.commandDelegate sendPluginResult:res callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
}];
}
else {
result = [self resultForVideo:info];
}
if (result) {
result = [weakSelf resultForVideo:info];
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
@@ -518,11 +558,14 @@ static NSString* toBase64(NSData* data) {
dispatch_block_t invoke = ^ (void) {
CDVPluginResult* result;
if ([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"];
} else {
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to camera"];
} else if (picker.sourceType != UIImagePickerControllerSourceTypeCamera && [ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"];
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"];
}
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
@@ -719,4 +762,4 @@ static NSString* toBase64(NSData* data) {
return cameraPicker;
}
@end
@end

View File

@@ -20,7 +20,7 @@
*/
/*jshint unused:true, undef:true, browser:true */
/*global Windows:true, URL:true, module:true, require:true */
/*global Windows:true, URL:true, module:true, require:true, WinJS:true */
var Camera = require('./Camera');
@@ -73,6 +73,9 @@ var windowsPhoneVideoContainers = [".avi", ".3gp", ".3g2", ".wmv", ".3gp", ".3g
// Default aspect ratio 1.78 (16:9 hd video standard)
var DEFAULT_ASPECT_RATIO = '1.8';
// Highest possible z-index supported across browsers. Anything used above is converted to this value.
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
// Resize method
function resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
var tempPhotoFileName = "";
@@ -93,8 +96,10 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
var image = new Image();
image.src = imageData;
image.onload = function() {
var imageWidth = targetWidth,
imageHeight = targetHeight;
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
var imageWidth = ratio * this.width;
var imageHeight = ratio * this.height;
var canvas = document.createElement('canvas');
var storageFileName;
@@ -134,8 +139,9 @@ function resizeImageBase64(successCallback, errorCallback, file, targetWidth, ta
image.src = imageData;
image.onload = function() {
var imageWidth = targetWidth,
imageHeight = targetHeight;
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
var imageWidth = ratio * this.width;
var imageHeight = ratio * this.height;
var canvas = document.createElement('canvas');
canvas.width = imageWidth;
@@ -267,7 +273,12 @@ function takePictureFromFileWindows(successCallback, errorCallback, args) {
else {
var storageFolder = getAppData().localFolder;
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
successCallback(URL.createObjectURL(storageFile));
if(destinationType == Camera.DestinationType.NATIVE_URI) {
successCallback("ms-appdata:///local/" + storageFile.name);
}
else {
successCallback(URL.createObjectURL(storageFile));
}
}, function () {
errorCallback("Can't access localStorage folder.");
});
@@ -307,37 +318,55 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
saveToPhotoAlbum = args[9],
cameraDirection = args[11],
capturePreview = null,
captureCancelButton = null,
cameraCaptureButton = null,
cameraCancelButton = null,
capture = null,
captureSettings = null,
CaptureNS = Windows.Media.Capture,
sensor = null;
var createCameraUI = function () {
// Create fullscreen preview
capturePreview = document.createElement("video");
function createCameraUI() {
// create style for take and cancel buttons
var buttonStyle = "width:45%;padding: 10px 16px;font-size: 18px;line-height: 1.3333333;color: #333;background-color: #fff;border-color: #ccc; border: 1px solid transparent;border-radius: 6px; display: block; margin: 20px; z-index: 1000;border-color: #adadad;";
// z-order style element for capturePreview and captureCancelButton elts
// Create fullscreen preview
// z-order style element for capturePreview and cameraCancelButton elts
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
capturePreview.style.cssText = "position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: 999";
capturePreview = document.createElement("video");
capturePreview.style.cssText = "position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: " + (HIGHEST_POSSIBLE_Z_INDEX - 1) + ";";
// Create capture button
cameraCaptureButton = document.createElement("button");
cameraCaptureButton.innerText = "Take";
cameraCaptureButton.style.cssText = buttonStyle + "position: fixed; left: 0; bottom: 0; margin: 20px; z-index: " + HIGHEST_POSSIBLE_Z_INDEX + ";";
// Create cancel button
captureCancelButton = document.createElement("button");
captureCancelButton.innerText = "Cancel";
captureCancelButton.style.cssText = "position: fixed; right: 0; bottom: 0; display: block; margin: 20px; z-index: 1000";
cameraCancelButton = document.createElement("button");
cameraCancelButton.innerText = "Cancel";
cameraCancelButton.style.cssText = buttonStyle + "position: fixed; right: 0; bottom: 0; margin: 20px; z-index: " + HIGHEST_POSSIBLE_Z_INDEX + ";";
capture = new CaptureNS.MediaCapture();
captureSettings = new CaptureNS.MediaCaptureInitializationSettings();
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
};
}
var startCameraPreview = function () {
function continueVideoOnFocus() {
// if preview is defined it would be stuck, play it
if (capturePreview) {
capturePreview.play();
}
}
function startCameraPreview() {
// Search for available camera devices
// This is necessary to detect which camera (front or back) we should use
var DeviceEnum = Windows.Devices.Enumeration;
var expectedPanel = cameraDirection === 1 ? DeviceEnum.Panel.front : DeviceEnum.Panel.back;
// Add focus event handler to capture the event when user suspends the app and comes back while the preview is on
window.addEventListener("focus", continueVideoOnFocus);
DeviceEnum.DeviceInformation.findAllAsync(DeviceEnum.DeviceClass.videoCapture).then(function (devices) {
if (devices.length <= 0) {
destroyCameraPreview();
@@ -355,6 +384,28 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
return capture.initializeAsync(captureSettings);
}).then(function () {
// create focus control if available
var VideoDeviceController = capture.videoDeviceController;
var FocusControl = VideoDeviceController.focusControl;
if (FocusControl.supported === true) {
capturePreview.addEventListener('click', function () {
// Make sure function isn't called again before previous focus is completed
if (this.getAttribute('clicked') === '1') {
return false;
} else {
this.setAttribute('clicked', '1');
}
var preset = Windows.Media.Devices.FocusPreset.autoNormal;
var parent = this;
FocusControl.setPresetAsync(preset).done(function () {
// set the clicked attribute back to '0' to allow focus again
parent.setAttribute('clicked', '0');
});
});
}
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
capturePreview.msZoom = true;
capturePreview.src = URL.createObjectURL(capture);
@@ -365,11 +416,10 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
if (sensor !== null) {
sensor.addEventListener("orientationchanged", onOrientationChange);
}
capturePreview.addEventListener('click', captureAction);
captureCancelButton.addEventListener('click', function () {
destroyCameraPreview();
errorCallback('Cancelled');
}, false);
// add click events to capture and cancel buttons
cameraCaptureButton.addEventListener('click', onCameraCaptureButtonClick);
cameraCancelButton.addEventListener('click', onCameraCancelButtonClick);
// Change default orientation
if (sensor) {
@@ -388,9 +438,10 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
return;
}
// Insert preview frame and controls into page
// add elements to body
document.body.appendChild(capturePreview);
document.body.appendChild(captureCancelButton);
document.body.appendChild(cameraCaptureButton);
document.body.appendChild(cameraCancelButton);
if (aspectRatios.indexOf(DEFAULT_ASPECT_RATIO) > -1) {
return setAspectRatio(capture, DEFAULT_ASPECT_RATIO);
@@ -402,26 +453,40 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
destroyCameraPreview();
errorCallback('Camera intitialization error ' + err);
});
};
}
var destroyCameraPreview = function () {
function destroyCameraPreview() {
// If sensor is available, remove event listener
if (sensor !== null) {
sensor.removeEventListener('orientationchanged', onOrientationChange);
}
// Pause and dispose preview element
capturePreview.pause();
capturePreview.src = null;
[capturePreview, captureCancelButton].forEach(function(elem) {
// Remove event listeners from buttons
cameraCaptureButton.removeEventListener('click', onCameraCaptureButtonClick);
cameraCancelButton.removeEventListener('click', onCameraCancelButtonClick);
// Remove the focus event handler
window.removeEventListener("focus", continueVideoOnFocus);
// Remove elements
[capturePreview, cameraCaptureButton, cameraCancelButton].forEach(function (elem) {
if (elem /* && elem in document.body.childNodes */) {
document.body.removeChild(elem);
}
});
// Stop and dispose media capture manager
if (capture) {
capture.stopRecordAsync();
capture = null;
}
};
}
var captureAction = function () {
function captureAction() {
var encodingProperties,
fileName,
@@ -441,33 +506,33 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
var photoStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
var finalStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
capture.capturePhotoToStreamAsync(encodingProperties, photoStream)
.then(function() {
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
})
.then(function(dec) {
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
})
.then(function(enc) {
// We need to rotate the photo wrt sensor orientation
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
return enc.flushAsync();
})
.then(function() {
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
})
.then(function(fileStream) {
return Windows.Storage.Streams.RandomAccessStream.copyAsync(finalStream, fileStream);
})
.done(function() {
photoStream.close();
finalStream.close();
complete(tempCapturedFile);
}, function() {
photoStream.close();
finalStream.close();
throw new Error("An error has occured while capturing the photo.");
});
.then(function() {
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
})
.then(function(dec) {
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
})
.then(function(enc) {
// We need to rotate the photo wrt sensor orientation
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
return enc.flushAsync();
})
.then(function() {
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
})
.then(function(fileStream) {
return Windows.Storage.Streams.RandomAccessStream.copyAndCloseAsync(finalStream, fileStream);
})
.done(function() {
photoStream.close();
finalStream.close();
complete(tempCapturedFile);
}, function() {
photoStream.close();
finalStream.close();
throw new Error("An error has occured while capturing the photo.");
});
});
})
.done(function(capturedFile) {
@@ -480,12 +545,12 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
saveToPhotoAlbum: saveToPhotoAlbum
}, successCallback, errorCallback);
}, function(err) {
destroyCameraPreview();
errorCallback(err);
destroyCameraPreview();
errorCallback(err);
});
};
}
var getAspectRatios = function (capture) {
function getAspectRatios(capture) {
var videoDeviceController = capture.videoDeviceController;
var photoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo).map(function (element) {
return (element.width / element.height).toFixed(1);
@@ -512,9 +577,9 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
return Object.keys(aspectObj).filter(function (k) {
return aspectObj[k] === 3;
});
};
}
var setAspectRatio = function (capture, aspect) {
function setAspectRatio(capture, aspect) {
// Max photo resolution with desired aspect ratio
var videoDeviceController = capture.videoDeviceController;
var photoResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo)
@@ -550,15 +615,42 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
.then(function () {
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoRecord, videoRecordResolution);
});
};
}
/**
* When Capture button is clicked, try to capture a picture and return
*/
function onCameraCaptureButtonClick() {
// Make sure user can't click more than once
if (this.getAttribute('clicked') === '1') {
return false;
} else {
this.setAttribute('clicked', '1');
}
captureAction();
}
/**
* When Cancel button is clicked, destroy camera preview and return with error callback
*/
function onCameraCancelButtonClick() {
// Make sure user can't click more than once
if (this.getAttribute('clicked') === '1') {
return false;
} else {
this.setAttribute('clicked', '1');
}
destroyCameraPreview();
errorCallback('no image selected');
}
/**
* When the phone orientation change, get the event and change camera preview rotation
* @param {Object} e - SimpleOrientationSensorOrientationChangedEventArgs
*/
var onOrientationChange = function (e) {
function onOrientationChange(e) {
setPreviewRotation(e.orientation);
};
}
/**
* Converts SimpleOrientation to a VideoRotation to remove difference between camera sensor orientation
@@ -566,7 +658,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
* @return {number} - Windows.Media.Capture.VideoRotation
*/
var orientationToRotation = function (orientation) {
function orientationToRotation(orientation) {
// VideoRotation enumerable and BitmapRotation enumerable have the same values
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.capture.videorotation.aspx
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaprotation.aspx
@@ -590,15 +682,15 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
// Falling back to portrait default
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
}
};
}
/**
* Rotates the current MediaCapture's video
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
*/
var setPreviewRotation = function(orientation) {
function setPreviewRotation(orientation) {
capture.setPreviewRotation(orientationToRotation(orientation));
};
}
try {
createCameraUI();
@@ -629,43 +721,55 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
// decide which max pixels should be supported by targetWidth or targetHeight.
var maxRes = null;
var UIMaxRes = WMCapture.CameraCaptureUIMaxPhotoResolution;
switch (true) {
case (targetWidth >= 1280 || targetHeight >= 960) :
cameraCaptureUI.photoSettings.maxResolution = UIMaxRes.large3M;
break;
case (targetWidth >= 1024 || targetHeight >= 768) :
maxRes = UIMaxRes.mediumXga;
break;
case (targetWidth >= 800 || targetHeight >= 600) :
maxRes = UIMaxRes.mediumXga;
break;
case (targetWidth >= 640 || targetHeight >= 480) :
maxRes = UIMaxRes.smallVga;
break;
case (targetWidth >= 320 || targetHeight >= 240) :
maxRes = UIMaxRes.verySmallQvga;
break;
default :
maxRes = UIMaxRes.highestAvailable;
var totalPixels = targetWidth * targetHeight;
if (targetWidth == -1 && targetHeight == -1) {
maxRes = UIMaxRes.highestAvailable;
}
// Temp fix for CB-10539
/*else if (totalPixels <= 320 * 240) {
maxRes = UIMaxRes.verySmallQvga;
}*/
else if (totalPixels <= 640 * 480) {
maxRes = UIMaxRes.smallVga;
} else if (totalPixels <= 1024 * 768) {
maxRes = UIMaxRes.mediumXga;
} else if (totalPixels <= 3 * 1000 * 1000) {
maxRes = UIMaxRes.large3M;
} else if (totalPixels <= 5 * 1000 * 1000) {
maxRes = UIMaxRes.veryLarge5M;
} else {
maxRes = UIMaxRes.highestAvailable;
}
cameraCaptureUI.photoSettings.maxResolution = maxRes;
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function(picture) {
if (!picture) {
errorCallback("User didn't capture a photo.");
return;
}
var cameraPicture;
var savePhotoOnFocus = function() {
savePhoto(picture, {
window.removeEventListener("focus", savePhotoOnFocus);
// call only when the app is in focus again
savePhoto(cameraPicture, {
destinationType: destinationType,
targetHeight: targetHeight,
targetWidth: targetWidth,
encodingType: encodingType,
saveToPhotoAlbum: saveToPhotoAlbum
}, successCallback, errorCallback);
};
// add and delete focus eventHandler to capture the focus back from cameraUI to app
window.addEventListener("focus", savePhotoOnFocus);
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function(picture) {
if (!picture) {
errorCallback("User didn't capture a photo.");
window.removeEventListener("focus", savePhotoOnFocus);
return;
}
cameraPicture = picture;
}, function() {
errorCallback("Fail to capture a photo.");
window.removeEventListener("focus", savePhotoOnFocus);
});
}
@@ -685,7 +789,7 @@ function savePhoto(picture, options, successCallback, errorCallback) {
resizeImageBase64(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight);
} else {
fileIO.readBufferAsync(picture).done(function(buffer) {
var strBase64 =encodeToBase64String(buffer);
var strBase64 = encodeToBase64String(buffer);
picture.deleteAsync().done(function() {
successCallback(strBase64);
}, function(err) {

View File

@@ -46,6 +46,11 @@ namespace WPCordovaClassLib.Cordova.Commands
/// </summary>
private const int FILE_URI = 1;
/// <summary>
/// Return native uri
/// </summary>
private const int NATIVE_URI = 2;
/// <summary>
/// Choose image from picture library
/// </summary>
@@ -214,10 +219,17 @@ namespace WPCordovaClassLib.Cordova.Commands
return;
}
if(cameraOptions.DestinationType != Camera.FILE_URI && cameraOptions.DestinationType != Camera.DATA_URL )
// Api supports FILE_URI, DATA_URL, NATIVE_URI destination types.
// Treat all other destination types as an error.
switch (cameraOptions.DestinationType)
{
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
return;
case Camera.FILE_URI:
case Camera.DATA_URL:
case Camera.NATIVE_URI:
break;
default:
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
return;
}
ChooserBase<PhotoResult> chooserTask = null;
@@ -304,7 +316,7 @@ namespace WPCordovaClassLib.Cordova.Commands
{
imagePathOrContent = GetImageContent(rotImageStream);
}
else // FILE_URL
else // FILE_URL or NATIVE_URI (both use the same resultant uri format)
{
imagePathOrContent = SaveImageToLocalStorage(rotImageStream, Path.GetFileName(e.OriginalFileName));
}
@@ -316,7 +328,7 @@ namespace WPCordovaClassLib.Cordova.Commands
{
imagePathOrContent = GetImageContent(e.ChosenPhoto);
}
else // FILE_URL
else // FILE_URL or NATIVE_URI (both use the same resultant uri format)
{
imagePathOrContent = SaveImageToLocalStorage(e.ChosenPhoto, Path.GetFileName(e.OriginalFileName));
}

View File

@@ -476,7 +476,7 @@
pictureOptions.encodingType = EncodingTypePNG;
resultData = [self.plugin processImage:originalImage info:@{} options:pictureOptions];
XCTAssertEqualObjects([resultData base64EncodedString], [originalImageDataPNG base64EncodedString]);
XCTAssertEqualObjects([resultData base64EncodedStringWithOptions:0], [originalImageDataPNG base64EncodedStringWithOptions:0]);
// Original, JPEG, full quality
@@ -487,7 +487,7 @@
pictureOptions.encodingType = EncodingTypeJPEG;
resultData = [self.plugin processImage:originalImage info:@{} options:pictureOptions];
XCTAssertEqualObjects([resultData base64EncodedString], [originalImageDataJPEG base64EncodedString]);
XCTAssertEqualObjects([resultData base64EncodedStringWithOptions:0], [originalImageDataJPEG base64EncodedStringWithOptions:0]);
// Original, JPEG, with quality value
@@ -500,7 +500,7 @@
NSData* originalImageDataJPEGWithQuality = UIImageJPEGRepresentation(originalImage, [pictureOptions.quality floatValue]/ 100.f);
resultData = [self.plugin processImage:originalImage info:@{} options:pictureOptions];
XCTAssertEqualObjects([resultData base64EncodedString], [originalImageDataJPEGWithQuality base64EncodedString]);
XCTAssertEqualObjects([resultData base64EncodedStringWithOptions:0], [originalImageDataJPEGWithQuality base64EncodedStringWithOptions:0]);
// TODO: usesGeolocation is not tested
}

View File

@@ -22,7 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rim="http://www.blackberry.com/ns/widgets"
id="cordova-plugin-camera-tests"
version="1.2.0">
version="2.1.1">
<name>Cordova Camera Plugin Tests</name>
<license>Apache 2.0</license>

View File

@@ -19,6 +19,9 @@
*
*/
/* globals Camera, resolveLocalFileSystemURI, FileEntry, CameraPopoverOptions, FileTransfer, FileUploadOptions, LocalFileSystem, MSApp */
/* jshint jasmine: true */
exports.defineAutoTests = function () {
describe('Camera (navigator.camera)', function () {
it("should exist", function () {
@@ -78,7 +81,6 @@ exports.defineAutoTests = function () {
/******************************************************************************/
exports.defineManualTests = function (contentEl, createActionButton) {
var platformId = cordova.require('cordova/platform').id;
var pictureUrl = null;
var fileObj = null;
var fileEntry = null;
@@ -94,11 +96,6 @@ exports.defineManualTests = function (contentEl, createActionButton) {
var camCorrectOrientationDefault = ['correctOrientation', false];
var camSaveToPhotoAlbumDefault = ['saveToPhotoAlbum', true];
var clearLog = function () {
var log = document.getElementById('info');
log.innerHTML = "";
}
function log(value) {
console.log(value);
document.getElementById('camera_status').textContent += (new Date() - pageStartTime) / 1000 + ': ' + value + '\n';
@@ -121,8 +118,8 @@ exports.defineManualTests = function (contentEl, createActionButton) {
url = "data:image/jpeg;base64," + url;
} catch (e) {
// not DATA_URL
log('URL: ' + url.slice(0, 100));
}
log('URL: "' + url.slice(0, 90) + '"');
pictureUrl = url;
var img = document.getElementById('camera_image');
@@ -130,7 +127,9 @@ exports.defineManualTests = function (contentEl, createActionButton) {
img.src = url;
img.onloadend = function () {
log('Image tag load time: ' + (new Date() - startTime));
callback && callback();
if (callback) {
callback();
}
};
}
@@ -141,12 +140,13 @@ exports.defineManualTests = function (contentEl, createActionButton) {
function getPictureWin(data) {
setPicture(data);
// TODO: Fix resolveLocalFileSystemURI to work with native-uri.
if (pictureUrl.indexOf('file:') == 0 || pictureUrl.indexOf('content:') == 0 || pictureUrl.indexOf('ms-appdata:') === 0) {
if (pictureUrl.indexOf('file:') === 0 || pictureUrl.indexOf('content:') === 0 || pictureUrl.indexOf('ms-appdata:') === 0 || pictureUrl.indexOf('assets-library:') === 0) {
resolveLocalFileSystemURI(data, function (e) {
fileEntry = e;
logCallback('resolveLocalFileSystemURI()', true)(e.toURL());
readFile();
}, logCallback('resolveLocalFileSystemURI()', false));
} else if (pictureUrl.indexOf('data:image/jpeg;base64') == 0) {
} else if (pictureUrl.indexOf('data:image/jpeg;base64') === 0) {
// do nothing
} else {
var path = pictureUrl.replace(/^file:\/\/(localhost)?/, '').replace(/%20/g, ' ');
@@ -164,13 +164,11 @@ exports.defineManualTests = function (contentEl, createActionButton) {
window.onorientationchange = function () {
var newPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, 0);
popoverHandle.setPosition(newPopoverOptions);
}
};
}
function uploadImage() {
var ft = new FileTransfer(),
uploadcomplete = 0,
progress = 0,
options = new FileUploadOptions();
options.fileKey = "photo";
options.fileName = 'test.jpg';
@@ -206,7 +204,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
img.style.display = "block";
img.src = evt.target.result;
log("FileReader.readAsDataURL success");
};
}
function onFileReceived(file) {
log('Got file: ' + JSON.stringify(file));
@@ -217,8 +215,10 @@ exports.defineManualTests = function (contentEl, createActionButton) {
log('FileReader.readAsDataURL() - length = ' + reader.result.length);
};
reader.onerror = logCallback('FileReader.readAsDataURL', false);
reader.onloadend = onFileReadAsDataURL;
reader.readAsDataURL(file);
};
}
// Test out onFileReceived when the file object was set via a native <input> elements.
if (fileObj) {
onFileReceived(fileObj);
@@ -226,13 +226,14 @@ exports.defineManualTests = function (contentEl, createActionButton) {
fileEntry.file(onFileReceived, logCallback('FileEntry.file', false));
}
}
function getFileInfo() {
// Test FileEntry API here.
fileEntry.getMetadata(logCallback('FileEntry.getMetadata', true), logCallback('FileEntry.getMetadata', false));
fileEntry.setMetadata(logCallback('FileEntry.setMetadata', true), logCallback('FileEntry.setMetadata', false), { "com.apple.MobileBackup": 1 });
fileEntry.getParent(logCallback('FileEntry.getParent', true), logCallback('FileEntry.getParent', false));
fileEntry.getParent(logCallback('FileEntry.getParent', true), logCallback('FileEntry.getParent', false));
};
}
/**
* Copy image from library using a NATIVE_URI destination type
@@ -266,7 +267,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
};
window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, onFileSystemReceived, null);
};
}
/**
* Write image to library using a NATIVE_URI destination type
@@ -287,7 +288,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
fileEntry.createWriter(onFileWriterReceived, logCallback('FileEntry.createWriter', false));
fileEntry.createWriter(onFileTruncateWriterReceived, null);
};
}
function displayImageUsingCanvas() {
var canvas = document.getElementById('canvas');
@@ -300,7 +301,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
canvas.height = h;
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, w, h);
};
}
/**
* Remove image from library using a NATIVE_URI destination type
@@ -308,7 +309,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
*/
function removeImage() {
fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
};
}
function testInputTag(inputEl) {
clearStatus();
@@ -317,7 +318,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
window.setTimeout(function () {
testNativeFile2(inputEl);
}, 0);
};
}
function testNativeFile2(inputEl) {
if (!inputEl.value) {
@@ -347,24 +348,28 @@ exports.defineManualTests = function (contentEl, createActionButton) {
function extractOptions() {
var els = document.querySelectorAll('#image-options select');
var ret = {};
/*jshint -W084 */
for (var i = 0, el; el = els[i]; ++i) {
var value = el.value;
if (value === '') continue;
value = +value;
if (el.isBool) {
ret[el.getAttribute("name")] = !!+value;
ret[el.getAttribute("name")] = !!value;
} else {
ret[el.getAttribute("name")] = +value;
ret[el.getAttribute("name")] = value;
}
}
/*jshint +W084 */
return ret;
}
function createOptionsEl(name, values, selectionDefault) {
var openDiv = '<div style="display: inline-block">' + name + ': ';
var select = '<select name=' + name + '>';
var select = '<select name=' + name + ' id="' + name + '">';
var defaultOption = '';
if (selectionDefault == undefined) {
if (selectionDefault === undefined) {
defaultOption = '<option value="">default</option>';
}
@@ -460,7 +465,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
var elements = document.getElementsByClassName("testInputTag");
var listener = function (e) {
testInputTag(e.target);
}
};
for (var i = 0; i < elements.length; ++i) {
var item = elements[i];
item.addEventListener("change", listener, false);

View File

@@ -25,6 +25,13 @@ var argscheck = require('cordova/argscheck'),
// XXX: commented out
//CameraPopoverHandle = require('./CameraPopoverHandle');
/**
* @namespace navigator
*/
/**
* @exports camera
*/
var cameraExport = {};
// Tack on the Camera Constants to the base camera plugin.
@@ -33,14 +40,102 @@ for (var key in Camera) {
}
/**
* Gets a picture from source defined by "options.sourceType", and returns the
* image as defined by the "options.destinationType" option.
* Callback function that provides an error message.
* @callback module:camera.onError
* @param {string} message - The message is provided by the device's native code.
*/
* The defaults are sourceType=CAMERA and destinationType=FILE_URI.
/**
* Callback function that provides the image data.
* @callback module:camera.onSuccess
* @param {string} imageData - Base64 encoding of the image data, _or_ the image file URI, depending on [`cameraOptions`]{@link module:camera.CameraOptions} in effect.
* @example
* // Show image
* //
* function cameraCallback(imageData) {
* var image = document.getElementById('myImage');
* image.src = "data:image/jpeg;base64," + imageData;
* }
*/
/**
* Optional parameters to customize the camera settings.
* * [Quirks](#CameraOptions-quirks)
* @typedef module:camera.CameraOptions
* @type {Object}
* @property {number} [quality=50] - Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. (Note that information about the camera's resolution is unavailable.)
* @property {module:Camera.DestinationType} [destinationType=FILE_URI] - Choose the format of the return value.
* @property {module:Camera.PictureSourceType} [sourceType=CAMERA] - Set the source of the picture.
* @property {Boolean} [allowEdit=true] - Allow simple editing of image before selection.
* @property {module:Camera.EncodingType} [encodingType=JPEG] - Choose the returned image file's encoding.
* @property {number} [targetWidth] - Width in pixels to scale image. Must be used with `targetHeight`. Aspect ratio remains constant.
* @property {number} [targetHeight] - Height in pixels to scale image. Must be used with `targetWidth`. Aspect ratio remains constant.
* @property {module:Camera.MediaType} [mediaType=PICTURE] - Set the type of media to select from. Only works when `PictureSourceType` is `PHOTOLIBRARY` or `SAVEDPHOTOALBUM`.
* @property {Boolean} [correctOrientation] - Rotate the image to correct for the orientation of the device during capture.
* @property {Boolean} [saveToPhotoAlbum] - Save the image to the photo album on the device after capture.
* @property {module:CameraPopoverOptions} [popoverOptions] - iOS-only options that specify popover location in iPad.
* @property {module:Camera.Direction} [cameraDirection=BACK] - Choose the camera to use (front- or back-facing).
*/
/**
* @description Takes a photo using the camera, or retrieves a photo from the device's
* image gallery. The image is passed to the success callback as a
* Base64-encoded `String`, or as the URI for the image file.
*
* @param {Function} successCallback
* @param {Function} errorCallback
* @param {Object} options
* The `camera.getPicture` function opens the device's default camera
* application that allows users to snap pictures by default - this behavior occurs,
* when `Camera.sourceType` equals [`Camera.PictureSourceType.CAMERA`]{@link module:Camera.PictureSourceType}.
* Once the user snaps the photo, the camera application closes and the application is restored.
*
* If `Camera.sourceType` is `Camera.PictureSourceType.PHOTOLIBRARY` or
* `Camera.PictureSourceType.SAVEDPHOTOALBUM`, then a dialog displays
* that allows users to select an existing image. The
* `camera.getPicture` function returns a [`CameraPopoverHandle`]{@link module:CameraPopoverHandle} object,
* which can be used to reposition the image selection dialog, for
* example, when the device orientation changes.
*
* The return value is sent to the [`cameraSuccess`]{@link module:camera.onSuccess} callback function, in
* one of the following formats, depending on the specified
* `cameraOptions`:
*
* - A `String` containing the Base64-encoded photo image.
*
* - A `String` representing the image file location on local storage (default).
*
* You can do whatever you want with the encoded image or URI, for
* example:
*
* - Render the image in an `<img>` tag, as in the example below
*
* - Save the data locally (`LocalStorage`, [Lawnchair](http://brianleroux.github.com/lawnchair/), etc.)
*
* - Post the data to a remote server
*
* __NOTE__: Photo resolution on newer devices is quite good. Photos
* selected from the device's gallery are not downscaled to a lower
* quality, even if a `quality` parameter is specified. To avoid common
* memory problems, set `Camera.destinationType` to `FILE_URI` rather
* than `DATA_URL`.
*
* __Supported Platforms__
*
* - Android
* - BlackBerry
* - Browser
* - Firefox
* - FireOS
* - iOS
* - Windows
* - WP8
* - Ubuntu
*
* More examples [here](#camera-getPicture-examples). Quirks [here](#camera-getPicture-quirks).
*
* @example
* navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
* @param {module:camera.onSuccess} successCallback
* @param {module:camera.onError} errorCallback
* @param {module:camera.CameraOptions} options CameraOptions
*/
cameraExport.getPicture = function(successCallback, errorCallback, options) {
argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
@@ -68,6 +163,27 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
//return new CameraPopoverHandle();
};
/**
* Removes intermediate image files that are kept in temporary storage
* after calling [`camera.getPicture`]{@link module:camera.getPicture}. Applies only when the value of
* `Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the
* `Camera.destinationType` equals `Camera.DestinationType.FILE_URI`.
*
* __Supported Platforms__
*
* - iOS
*
* @example
* navigator.camera.cleanup(onSuccess, onFail);
*
* function onSuccess() {
* console.log("Camera cleanup success.")
* }
*
* function onFail(message) {
* alert('Failed because: ' + message);
* }
*/
cameraExport.cleanup = function(successCallback, errorCallback) {
exec(successCallback, errorCallback, "Camera", "cleanup", []);
};

View File

@@ -19,35 +19,70 @@
*
*/
/**
* @module Camera
*/
module.exports = {
/**
* @enum {number}
*/
DestinationType:{
DATA_URL: 0, // Return base64 encoded string
FILE_URI: 1, // Return file uri (content://media/external/images/media/2 for Android)
NATIVE_URI: 2 // Return native uri (eg. asset-library://... for iOS)
/** Return base64 encoded string */
DATA_URL: 0,
/** Return file uri (content://media/external/images/media/2 for Android) */
FILE_URI: 1,
/** Return native uri (eg. asset-library://... for iOS) */
NATIVE_URI: 2
},
/**
* @enum {number}
*/
EncodingType:{
JPEG: 0, // Return JPEG encoded image
PNG: 1 // Return PNG encoded image
/** Return JPEG encoded image */
JPEG: 0,
/** Return PNG encoded image */
PNG: 1
},
/**
* @enum {number}
*/
MediaType:{
PICTURE: 0, // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
VIDEO: 1, // allow selection of video only, ONLY RETURNS URL
ALLMEDIA : 2 // allow selection from all media types
/** Allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType */
PICTURE: 0,
/** Allow selection of video only, ONLY RETURNS URL */
VIDEO: 1,
/** Allow selection from all media types */
ALLMEDIA : 2
},
/**
* @enum {number}
*/
PictureSourceType:{
PHOTOLIBRARY : 0, // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
CAMERA : 1, // Take picture from camera
SAVEDPHOTOALBUM : 2 // Choose image from picture library (same as PHOTOLIBRARY for Android)
/** Choose image from picture library (same as SAVEDPHOTOALBUM for Android) */
PHOTOLIBRARY : 0,
/** Take picture from camera */
CAMERA : 1,
/** Choose image from picture library (same as PHOTOLIBRARY for Android) */
SAVEDPHOTOALBUM : 2
},
/**
* Matches iOS UIPopoverArrowDirection constants to specify arrow location on popover.
* @enum {number}
*/
PopoverArrowDirection:{
ARROW_UP : 1, // matches iOS UIPopoverArrowDirection constants to specify arrow location on popover
ARROW_UP : 1,
ARROW_DOWN : 2,
ARROW_LEFT : 4,
ARROW_RIGHT : 8,
ARROW_ANY : 15
},
/**
* @enum {number}
*/
Direction:{
/** Use the back-facing camera */
BACK: 0,
/** Use the front-facing camera */
FRONT: 1
}
};

View File

@@ -19,9 +19,8 @@
*
*/
var exec = require('cordova/exec');
/**
* @ignore in favour of iOS' one
* A handle to an image picker popover.
*/
var CameraPopoverHandle = function() {

View File

@@ -21,16 +21,31 @@
var Camera = require('./Camera');
/**
* Encapsulates options for iOS Popover image picker
/**
* @namespace navigator
*/
var CameraPopoverOptions = function(x,y,width,height,arrowDir){
/**
* iOS-only parameters that specify the anchor element location and arrow
* direction of the popover when selecting images from an iPad's library
* or album.
* Note that the size of the popover may change to adjust to the
* direction of the arrow and orientation of the screen. Make sure to
* account for orientation changes when specifying the anchor element
* location.
* @module CameraPopoverOptions
* @param {Number} [x=0] - x pixel coordinate of screen element onto which to anchor the popover.
* @param {Number} [y=32] - y pixel coordinate of screen element onto which to anchor the popover.
* @param {Number} [width=320] - width, in pixels, of the screen element onto which to anchor the popover.
* @param {Number} [height=480] - height, in pixels, of the screen element onto which to anchor the popover.
* @param {module:Camera.PopoverArrowDirection} [arrowDir=ARROW_ANY] - Direction the arrow on the popover should point.
*/
var CameraPopoverOptions = function (x, y, width, height, arrowDir) {
// information of rectangle that popover should be anchored to
this.x = x || 0;
this.y = y || 32;
this.width = width || 320;
this.height = height || 480;
// The direction of the popover arrow
this.arrowDir = arrowDir || Camera.PopoverArrowDirection.ARROW_ANY;
};

View File

@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*
*/
*/
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('back').onclick = function () {

View File

@@ -21,10 +21,36 @@
var exec = require('cordova/exec');
/**
* @namespace navigator
*/
/**
* A handle to an image picker popover.
*
* __Supported Platforms__
*
* - iOS
*
* @example
* var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail,
* {
* destinationType: Camera.DestinationType.FILE_URI,
* sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
* popoverOptions: new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY)
* });
*
* // Reposition the popover if the orientation changes.
* window.onorientationchange = function() {
* var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY);
* cameraPopoverHandle.setPosition(cameraPopoverOptions);
* }
* @module CameraPopoverHandle
*/
var CameraPopoverHandle = function() {
/** Set the position of the popover.
* @param {module:CameraPopoverOptions} popoverOptions
*/
this.setPosition = function(popoverOptions) {
var args = [ popoverOptions ];
exec(null, null, "Camera", "repositionPopover", args);