mirror of
https://github.com/apache/cordova-plugin-camera.git
synced 2026-02-03 00:06:46 +08:00
Compare commits
195 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d16482d292 | ||
|
|
72502444c9 | ||
|
|
4fc25154f3 | ||
|
|
7f616d16f1 | ||
|
|
dfbca19a7a | ||
|
|
86e546f868 | ||
|
|
f2ca5ed79f | ||
|
|
ab7e02f0b8 | ||
|
|
899f6d8059 | ||
|
|
aa8a5945dd | ||
|
|
c06480f4e3 | ||
|
|
5ca4d8b082 | ||
|
|
bba8283d98 | ||
|
|
c27725ce66 | ||
|
|
415412cfef | ||
|
|
5ebda25164 | ||
|
|
d29c767f07 | ||
|
|
0ad5bdd9ff | ||
|
|
d6bd9ae3b3 | ||
|
|
81f9433606 | ||
|
|
05594c4646 | ||
|
|
a364e79482 | ||
|
|
42fc8e0bcd | ||
|
|
ee5537694a | ||
|
|
9eba35e2f6 | ||
|
|
8b83171ee2 | ||
|
|
c9e6a9a38a | ||
|
|
cc48945f37 | ||
|
|
8b3410bcc6 | ||
|
|
485a11e0f4 | ||
|
|
2d47a26271 | ||
|
|
2d2352f695 | ||
|
|
2f003d2b49 | ||
|
|
3a90bb7d55 | ||
|
|
b13cbdeb16 | ||
|
|
ee192d94b4 | ||
|
|
d9eb83bcb9 | ||
|
|
84f96c1067 | ||
|
|
61064ae3ed | ||
|
|
9ec8aea073 | ||
|
|
9db952e161 | ||
|
|
06d609cfa4 | ||
|
|
cc1076d3cb | ||
|
|
48d4213b2d | ||
|
|
9b566d3f0b | ||
|
|
b63a0d83e0 | ||
|
|
9fe94479e2 | ||
|
|
2f89666db7 | ||
|
|
f8682b9162 | ||
|
|
0ed6406864 | ||
|
|
fa58e83fca | ||
|
|
a3af38ad5b | ||
|
|
c98607c613 | ||
|
|
b89645c749 | ||
|
|
1beeafb6e8 | ||
|
|
7813ad9bef | ||
|
|
6e19147b09 | ||
|
|
9f159d757a | ||
|
|
62d1b01e81 | ||
|
|
92d67d990d | ||
|
|
0accbf560b | ||
|
|
e588907ac7 | ||
|
|
3ed3d887ca | ||
|
|
f010394af8 | ||
|
|
00e0a7dc46 | ||
|
|
fee72c7c04 | ||
|
|
5807458a1d | ||
|
|
c13e9f327b | ||
|
|
7e8fe0bae9 | ||
|
|
3f8c53f7f3 | ||
|
|
4e439d85c3 | ||
|
|
b62fdf50f7 | ||
|
|
744d72a33b | ||
|
|
3d26986bfd | ||
|
|
fed798e6c7 | ||
|
|
2027d69606 | ||
|
|
7129fb2c12 | ||
|
|
b695717240 | ||
|
|
acff98058f | ||
|
|
8a7326969f | ||
|
|
a05f169984 | ||
|
|
dcc81bfbe1 | ||
|
|
278b527702 | ||
|
|
d7ca7edf88 | ||
|
|
f283502545 | ||
|
|
a831e15a91 | ||
|
|
3586ea58a2 | ||
|
|
cc840b6cef | ||
|
|
0115458ce8 | ||
|
|
2eef096861 | ||
|
|
cf35b1bb2a | ||
|
|
39bff2f41d | ||
|
|
7551778e13 | ||
|
|
e3a431cbeb | ||
|
|
832d6e3bea | ||
|
|
2a7469e065 | ||
|
|
af98d57417 | ||
|
|
57b177f3fb | ||
|
|
f2b4eeded0 | ||
|
|
d4a55f20ec | ||
|
|
68e18a97d1 | ||
|
|
bfaef0ff25 | ||
|
|
0ba547cd3c | ||
|
|
6d058fe9e7 | ||
|
|
daf5fa48dd | ||
|
|
d124e03cb9 | ||
|
|
4202fff7ac | ||
|
|
def399fe51 | ||
|
|
82c9f4524a | ||
|
|
a9c18710f2 | ||
|
|
624ddd5ced | ||
|
|
fb871d40e2 | ||
|
|
0cd962466d | ||
|
|
c12206ebc8 | ||
|
|
826aca3524 | ||
|
|
76c129c95e | ||
|
|
fac7a53383 | ||
|
|
1348d2e138 | ||
|
|
c5f5a46e3e | ||
|
|
37dd471d0e | ||
|
|
a19c75253a | ||
|
|
c20e031d42 | ||
|
|
68a1150939 | ||
|
|
9c906b2ab7 | ||
|
|
56b9469313 | ||
|
|
d51e23ad7b | ||
|
|
2cd2528d1c | ||
|
|
5f7f4f3e55 | ||
|
|
20dcaf2cb3 | ||
|
|
b16c5234d5 | ||
|
|
c1948fc0d4 | ||
|
|
f792aaacc3 | ||
|
|
16636d18f2 | ||
|
|
019346d188 | ||
|
|
61b77951e1 | ||
|
|
a060fb36f3 | ||
|
|
77653183dd | ||
|
|
df734a522c | ||
|
|
654286d373 | ||
|
|
76ad059c9c | ||
|
|
5b38453262 | ||
|
|
e48a7e5c5c | ||
|
|
06fcbf05a2 | ||
|
|
9a9081b0d4 | ||
|
|
6f7ce333cc | ||
|
|
0f32b78c82 | ||
|
|
eb009471ab | ||
|
|
1d32ea46f0 | ||
|
|
e2193631d5 | ||
|
|
e8fa1695c4 | ||
|
|
e1911a3c78 | ||
|
|
ef5484a2aa | ||
|
|
b3376e2389 | ||
|
|
ee9884e97b | ||
|
|
1cf4feee60 | ||
|
|
847add6870 | ||
|
|
63ac782b32 | ||
|
|
7f3a0a5483 | ||
|
|
036cdfdeb8 | ||
|
|
2bb134bf6b | ||
|
|
88592575fe | ||
|
|
f5217bf02b | ||
|
|
9b444c39ba | ||
|
|
5ff225d8df | ||
|
|
3a8880d0c3 | ||
|
|
d0ad2f5065 | ||
|
|
44475d9df9 | ||
|
|
2714060b09 | ||
|
|
cbe17eec21 | ||
|
|
8024c5de49 | ||
|
|
933b35b0d2 | ||
|
|
dbe3e3d2ca | ||
|
|
a030c52f7f | ||
|
|
b8a68af63f | ||
|
|
837d6721f2 | ||
|
|
f20dfae735 | ||
|
|
454a6f518c | ||
|
|
dca8bd1943 | ||
|
|
cad9ab0419 | ||
|
|
ee5cfe89a5 | ||
|
|
813d143667 | ||
|
|
110b3b3388 | ||
|
|
eeb5880580 | ||
|
|
9dfd37cd66 | ||
|
|
c50757c245 | ||
|
|
0264e56162 | ||
|
|
8ebaaa62bd | ||
|
|
11eaaa8b9f | ||
|
|
899802a202 | ||
|
|
1e607ddcc8 | ||
|
|
63110ea54c | ||
|
|
b683315be6 | ||
|
|
2e147ba9ca | ||
|
|
11498404fc | ||
|
|
363dd02584 |
94
.gitattributes
vendored
Normal file
94
.gitattributes
vendored
Normal 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
|
||||
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<!--
|
||||
Please make sure the checklist boxes are all checked before submitting the PR. The checklist
|
||||
is intended as a quick reference, for complete details please see our Contributor Guidelines:
|
||||
|
||||
http://cordova.apache.org/contribute/contribute_guidelines.html
|
||||
|
||||
Thanks!
|
||||
-->
|
||||
|
||||
### Platforms affected
|
||||
|
||||
|
||||
### What does this PR do?
|
||||
|
||||
|
||||
### What testing has been done on this change?
|
||||
|
||||
|
||||
### Checklist
|
||||
- [ ] [Reported an issue](http://cordova.apache.org/contribute/issues.html) in the JIRA database
|
||||
- [ ] Commit message follows the format: "CB-3232: (android) Fix bug with resolving file paths", where CB-xxxx is the JIRA ID & "android" is the platform affected.
|
||||
- [ ] Added automated test coverage as appropriate for this change.
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -12,12 +12,4 @@ Thumbs.db
|
||||
*.swp
|
||||
*.user
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/node_modules/**
|
||||
|
||||
16
.jshintrc
Normal file
16
.jshintrc
Normal 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
1
.ratignore
Normal file
@@ -0,0 +1 @@
|
||||
TEMPLATE.md
|
||||
17
.travis.yml
17
.travis.yml
@@ -1,13 +1,8 @@
|
||||
language: objective-c
|
||||
git:
|
||||
depth: 2
|
||||
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"
|
||||
env:
|
||||
- TEST_DIR=.
|
||||
- TEST_DIR=./tests/ios
|
||||
script: cd $TEST_DIR && npm install && npm test
|
||||
|
||||
@@ -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).
|
||||
|
||||
782
README.md
782
README.md
@@ -1,3 +1,7 @@
|
||||
---
|
||||
title: Camera
|
||||
description: Take pictures with the device camera.
|
||||
---
|
||||
<!---
|
||||
# license: Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
@@ -17,9 +21,11 @@
|
||||
# under the License.
|
||||
-->
|
||||
|
||||
# cordova-plugin-camera
|
||||
|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-camera/)|[](https://travis-ci.org/apache/cordova-plugin-camera)
|
||||
|
||||
[](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,60 +37,120 @@ 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
|
||||
|
||||
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).
|
||||
|
||||
|
||||
|
||||
## navigator.camera.getPicture
|
||||
### iOS Quirks
|
||||
|
||||
Since iOS 10 it's mandatory to add a `NSCameraUsageDescription` and `NSPhotoLibraryUsageDescription` in the info.plist.
|
||||
|
||||
- `NSCameraUsageDescription` describes the reason that the app accesses the user’s camera.
|
||||
- `NSPhotoLibraryUsageDescription` describes the reason the app accesses the user's photo library.
|
||||
|
||||
When the system prompts the user to allow access, this string is displayed as part of the dialog box.
|
||||
|
||||
To add this entry you can pass the following variables on plugin install.
|
||||
|
||||
- `CAMERA_USAGE_DESCRIPTION` for `NSCameraUsageDescription`
|
||||
- `PHOTOLIBRARY_USAGE_DESCRIPTION` for `NSPhotoLibraryUsageDescription`
|
||||
|
||||
Example:
|
||||
|
||||
cordova plugin add cordova-plugin-camera --variable CAMERA_USAGE_DESCRIPTION="your usage message" --variable PHOTOLIBRARY_USAGE_DESCRIPTION="your usage message"
|
||||
|
||||
If you don't pass the variable, the plugin will add an empty string as value.
|
||||
|
||||
---
|
||||
|
||||
# API Reference <a name="reference"></a>
|
||||
|
||||
|
||||
* [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,
|
||||
which can be used to reposition the image selection dialog, for
|
||||
example, when the device orientation changes.
|
||||
that allows users to select an existing image.
|
||||
|
||||
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).
|
||||
|
||||
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
|
||||
@@ -93,26 +159,257 @@ 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__
|
||||
|
||||
        
|
||||
- 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>
|
||||
|
||||
navigator.camera.getPicture(onSuccess, onFail, { quality: 50,
|
||||
destinationType: Camera.DestinationType.DATA_URL
|
||||
});
|
||||
| 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 |
|
||||
|
||||
function onSuccess(imageData) {
|
||||
var image = document.getElementById('myImage');
|
||||
image.src = "data:image/jpeg;base64," + imageData;
|
||||
}
|
||||
**Example**
|
||||
```js
|
||||
navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
|
||||
```
|
||||
<a name="module_camera.cleanup"></a>
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
### 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>
|
||||
Defines the output format of `Camera.getPicture` call.
|
||||
_Note:_ On iOS passing `DestinationType.NATIVE_URI` along with
|
||||
`PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM` will
|
||||
disable any image modifications (resize, quality change, cropping, etc.) due
|
||||
to implementation specific.
|
||||
|
||||
**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. DATA_URL can be very memory intensive and cause app crashes or out of memory errors. Use FILE_URI or NATIVE_URI if possible |
|
||||
| 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>
|
||||
Defines the output format of `Camera.getPicture` call.
|
||||
_Note:_ On iOS passing `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM`
|
||||
along with `DestinationType.NATIVE_URI` will disable any image modifications (resize, quality
|
||||
change, cropping, etc.) due to implementation specific.
|
||||
|
||||
**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 the device's photo 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 only from the device's Camera Roll album (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
|
||||
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 cameraPopoverHandle = new CameraPopoverHandle();
|
||||
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 the image's file location:
|
||||
|
||||
@@ -128,31 +425,57 @@ Take a photo and retrieve the image's file location:
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
|
||||
Take a photo and retrieve it as a Base64-encoded image:
|
||||
|
||||
/**
|
||||
* Warning: Using DATA_URL is not recommended! The DATA_URL destination
|
||||
* type is very memory intensive, even with a low quality setting. Using it
|
||||
* can result in out of memory errors and application crashes. Use FILE_URI
|
||||
* or NATIVE_URI instead.
|
||||
*/
|
||||
navigator.camera.getPicture(onSuccess, onFail, { quality: 25,
|
||||
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);
|
||||
}
|
||||
|
||||
#### 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
|
||||
#### 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
|
||||
|
||||
@@ -170,77 +493,24 @@ displays:
|
||||
Invoking the native camera application while the device is connected
|
||||
via Zune does not work, and triggers an error callback.
|
||||
|
||||
#### Windows quirks
|
||||
|
||||
On Windows Phone 8.1 using `SAVEDPHOTOALBUM` or `PHOTOLIBRARY` as a source type causes application to suspend until file picker returns the selected image and
|
||||
then restore with start page as defined in app's `config.xml`. In case when `camera.getPicture` was called from different page, this will lead to reloading
|
||||
start page from scratch and success and error callbacks will never be called.
|
||||
|
||||
To avoid this we suggest using SPA pattern or call `camera.getPicture` only from your app's start page.
|
||||
|
||||
More information about Windows Phone 8.1 picker APIs is here: [How to continue your Windows Phone app after calling a file picker](https://msdn.microsoft.com/en-us/library/windows/apps/dn720490.aspx)
|
||||
|
||||
#### Tizen Quirks
|
||||
|
||||
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 +524,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 +564,12 @@ 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.
|
||||
|
||||
- When using `destinationType.NATIVE_URI` and `sourceType.PHOTOLIBRARY` or `sourceType.SAVEDPHOTOALBUM`, all editing options are ignored and link is returned to original picture.
|
||||
|
||||
#### Tizen Quirks
|
||||
|
||||
- options not supported
|
||||
@@ -311,137 +584,210 @@ 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.
|
||||
|
||||
[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
|
||||
|
||||
## CameraError
|
||||
## Sample: Take Pictures, Select Pictures from the Picture Library, and Get Thumbnails <a name="sample"></a>
|
||||
|
||||
onError callback function that provides an error message.
|
||||
The Camera plugin allows you to do things like open the device's Camera app and take a picture, or open the file picker and select one. The code snippets in this section demonstrate different tasks including:
|
||||
|
||||
function(message) {
|
||||
// Show a helpful message
|
||||
* Open the Camera app and [take a Picture](#takePicture)
|
||||
* Take a picture and [return thumbnails](#getThumbnails) (resized picture)
|
||||
* Take a picture and [generate a FileEntry object](#convert)
|
||||
* [Select a file](#selectFile) from the picture library
|
||||
* Select a JPEG image and [return thumbnails](#getFileThumbnails) (resized image)
|
||||
* Select an image and [generate a FileEntry object](#convert)
|
||||
|
||||
## Take a Picture <a name="takePicture"></a>
|
||||
|
||||
Before you can take a picture, you need to set some Camera plugin options to pass into the Camera plugin's `getPicture` function. Here is a common set of recommendations. In this example, you create the object that you will use for the Camera options, and set the `sourceType` dynamically to support both the Camera app and the file picker.
|
||||
|
||||
```js
|
||||
function setOptions(srcType) {
|
||||
var options = {
|
||||
// Some common settings are 20, 50, and 100
|
||||
quality: 50,
|
||||
destinationType: Camera.DestinationType.FILE_URI,
|
||||
// In this app, dynamically set the picture source, Camera or photo gallery
|
||||
sourceType: srcType,
|
||||
encodingType: Camera.EncodingType.JPEG,
|
||||
mediaType: Camera.MediaType.PICTURE,
|
||||
allowEdit: true,
|
||||
correctOrientation: true //Corrects Android orientation quirks
|
||||
}
|
||||
return options;
|
||||
}
|
||||
```
|
||||
|
||||
Typically, you want to use a FILE_URI instead of a DATA_URL to avoid most memory issues. JPEG is the recommended encoding type for Android.
|
||||
|
||||
You take a picture by passing in the options object to `getPicture`, which takes a CameraOptions object as the third argument. When you call `setOptions`, pass `Camera.PictureSourceType.CAMERA` as the picture source.
|
||||
|
||||
```js
|
||||
function openCamera(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.CAMERA;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
displayImage(imageUri);
|
||||
// You may choose to copy the picture, save it somewhere, or upload.
|
||||
func(imageUri);
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
Once you take the picture, you can display it or do something else. In this example, call the app's `displayImage` function from the preceding code.
|
||||
|
||||
```js
|
||||
function displayImage(imgUri) {
|
||||
|
||||
var elem = document.getElementById('imageFile');
|
||||
elem.src = imgUri;
|
||||
}
|
||||
```
|
||||
|
||||
To display the image on some platforms, you might need to include the main part of the URI in the Content-Security-Policy `<meta>` element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your `<meta>` element. Here is an example.
|
||||
|
||||
```html
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
|
||||
```
|
||||
|
||||
## Take a Picture and Return Thumbnails (Resize the Picture) <a name="getThumbnails"></a>
|
||||
|
||||
To get smaller images, you can return a resized image by passing both `targetHeight` and `targetWidth` values with your CameraOptions object. In this example, you resize the returned image to fit in a 100px by 100px box (the aspect ratio is maintained, so 100px is either the height or width, whichever is greater in the source).
|
||||
|
||||
```js
|
||||
function openCamera(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.CAMERA;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
if (selection == "camera-thmb") {
|
||||
options.targetHeight = 100;
|
||||
options.targetWidth = 100;
|
||||
}
|
||||
|
||||
#### Description
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
- __message__: The message is provided by the device's native code. _(String)_
|
||||
// Do something
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
## cameraSuccess
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
onSuccess callback function that provides the image data.
|
||||
## Select a File from the Picture Library <a name="selectFile"></a>
|
||||
|
||||
function(imageData) {
|
||||
// Do something with the image
|
||||
When selecting a file using the file picker, you also need to set the CameraOptions object. In this example, set the `sourceType` to `Camera.PictureSourceType.SAVEDPHOTOALBUM`. To open the file picker, call `getPicture` just as you did in the previous example, passing in the success and error callbacks along with CameraOptions object.
|
||||
|
||||
```js
|
||||
function openFilePicker(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
// Do something
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
## Select an Image and Return Thumbnails (resized images) <a name="getFileThumbnails"></a>
|
||||
|
||||
Resizing a file selected with the file picker works just like resizing using the Camera app; set the `targetHeight` and `targetWidth` options.
|
||||
|
||||
```js
|
||||
function openFilePicker(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
if (selection == "picker-thmb") {
|
||||
// To downscale a selected image,
|
||||
// Camera.EncodingType (e.g., JPEG) must match the selected image type.
|
||||
options.targetHeight = 100;
|
||||
options.targetWidth = 100;
|
||||
}
|
||||
|
||||
#### Description
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
- __imageData__: Base64 encoding of the image data, _or_ the image file URI, depending on `cameraOptions` in effect. _(String)_
|
||||
// Do something with image
|
||||
|
||||
#### Example
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
// Show image
|
||||
//
|
||||
function cameraCallback(imageData) {
|
||||
var image = document.getElementById('myImage');
|
||||
image.src = "data:image/jpeg;base64," + imageData;
|
||||
}
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
## Take a picture and get a FileEntry Object <a name="convert"></a>
|
||||
|
||||
## CameraPopoverHandle
|
||||
If you want to do something like copy the image to another location, or upload it somewhere using the FileTransfer plugin, you need to get a FileEntry object for the returned picture. To do that, call `window.resolveLocalFileSystemURL` on the file URI returned by the Camera app. If you need to use a FileEntry object, set the `destinationType` to `Camera.DestinationType.FILE_URI` in your CameraOptions object (this is also the default value).
|
||||
|
||||
A handle to the popover dialog created by `navigator.camera.getPicture`.
|
||||
>*Note* You need the [File plugin](https://www.npmjs.com/package/cordova-plugin-file) to call `window.resolveLocalFileSystemURL`.
|
||||
|
||||
#### Description
|
||||
Here is the call to `window.resolveLocalFileSystemURL`. The image URI is passed to this function from the success callback of `getPicture`. The success handler of `resolveLocalFileSystemURL` receives the FileEntry object.
|
||||
|
||||
- __setPosition__: Set the position of the popover. Takes the `CameraPopoverOptions` that specify the new position.
|
||||
```js
|
||||
function getFileEntry(imgUri) {
|
||||
window.resolveLocalFileSystemURL(imgUri, function success(fileEntry) {
|
||||
|
||||
#### Supported Platforms
|
||||
// Do something with the FileEntry object, like write to it, upload it, etc.
|
||||
// writeFile(fileEntry, imgUri);
|
||||
console.log("got file: " + fileEntry.fullPath);
|
||||
// displayFileData(fileEntry.nativeURL, "Native URL");
|
||||
|
||||
        
|
||||
}, function () {
|
||||
// If don't get the FileEntry (which may happen when testing
|
||||
// on some emulators), copy to a new FileEntry.
|
||||
createNewFileEntry(imgUri);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
#### Example
|
||||
In the example shown in the preceding code, you call the app's `createNewFileEntry` function if you don't get a valid FileEntry object. The image URI returned from the Camera app should result in a valid FileEntry, but platform behavior on some emulators may be different for files returned from the file picker.
|
||||
|
||||
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)
|
||||
});
|
||||
>*Note* To see an example of writing to a FileEntry, see the [File plugin README](https://www.npmjs.com/package/cordova-plugin-file).
|
||||
|
||||
// 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);
|
||||
}
|
||||
The code shown here creates a file in your app's cache (in sandboxed storage) named `tempFile.jpeg`. With the new FileEntry object, you can copy the image to the file or do something else like upload it.
|
||||
|
||||
## CameraPopoverOptions
|
||||
```js
|
||||
function createNewFileEntry(imgUri) {
|
||||
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function success(dirEntry) {
|
||||
|
||||
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.
|
||||
// JPEG file
|
||||
dirEntry.getFile("tempFile.jpeg", { create: true, exclusive: false }, function (fileEntry) {
|
||||
|
||||
{ x : 0,
|
||||
y : 32,
|
||||
width : 320,
|
||||
height : 480,
|
||||
arrowDir : Camera.PopoverArrowDirection.ARROW_ANY
|
||||
};
|
||||
// Do something with it, like write to it, upload it, etc.
|
||||
// writeFile(fileEntry, imgUri);
|
||||
console.log("got file: " + fileEntry.fullPath);
|
||||
// displayFileData(fileEntry.fullPath, "File copied to");
|
||||
|
||||
#### Description
|
||||
}, onErrorCreateFile);
|
||||
|
||||
- __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
|
||||
|
||||
        
|
||||
|
||||
#### Example
|
||||
|
||||
navigator.camera.cleanup(onSuccess, onFail);
|
||||
|
||||
function onSuccess() {
|
||||
console.log("Camera cleanup success.")
|
||||
}
|
||||
|
||||
function onFail(message) {
|
||||
alert('Failed because: ' + message);
|
||||
}
|
||||
}, onErrorResolveUrl);
|
||||
}
|
||||
```
|
||||
|
||||
514
RELEASENOTES.md
514
RELEASENOTES.md
@@ -20,33 +20,308 @@
|
||||
-->
|
||||
# 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.4.0 (Feb 28, 2017)
|
||||
* [CB-12501](https://issues.apache.org/jira/browse/CB-12501) **Android**: Appium tests don't use `XPath` selectors anymore
|
||||
* [CB-12469](https://issues.apache.org/jira/browse/CB-12469) Appium tests can now run on **iOS 10**
|
||||
* [CB-12005](https://issues.apache.org/jira/browse/CB-12005) Changing the `getOrientation` method to return the defined enumerated `EXIF` instead of orientation in degrees for Consistency
|
||||
* [CB-12368](https://issues.apache.org/jira/browse/CB-12368) Fix permission check on **Android**
|
||||
* [CB-12353](https://issues.apache.org/jira/browse/CB-12353) Corrected merges usage in `plugin.xml`
|
||||
* [CB-12369](https://issues.apache.org/jira/browse/CB-12369) Add plugin typings from `DefinitelyTyped`
|
||||
* [CB-12363](https://issues.apache.org/jira/browse/CB-12363) Added build badges for **iOS 9.3** and **iOS 10.0**
|
||||
* [CB-12312](https://issues.apache.org/jira/browse/CB-12312) [Appium] [Android] A few changes to the tests: - updated comments on how to run the tests. extra comments around functionality at certain points in the automation. - stub of a resolution checker on test startup - still need to figure out acceptable values. - moved session shutdown to an afterAll clause. - changed resolution determiner from using webview-based values to using the native windows dimensions - this helps as the webview values may be scaled down intentionally by manufacturers (via changing devicePixelRatio). furthermore, since the screen dimension automation is used purely for native UI automation, better to use the dimensions reported by the native context rather than the web context. - when finding elements by XPath, use multiple calls to avoid a Windows emulator + Android bug. Made this pattern consistent in the entire test.
|
||||
* [CB-12236](https://issues.apache.org/jira/browse/CB-12236) - Fixed RELEASENOTES for cordova-plugin-camera
|
||||
* [CB-12230](https://issues.apache.org/jira/browse/CB-12230) Removed Windows 8.1 build badges
|
||||
|
||||
### 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.3.1 (Dec 07, 2016)
|
||||
* [CB-12224](https://issues.apache.org/jira/browse/CB-12224) Updated version and RELEASENOTES.md for release 2.3.1
|
||||
* Fix missing license headers.
|
||||
* [CB-12086](https://issues.apache.org/jira/browse/CB-12086) Regenerate README.md from template
|
||||
* Added NSPhotoLibraryUsageDescription parameter to example install command Fixing some usages of NSPhotoLibraryUsageDescriptionentry
|
||||
* Updating compat dependency to 1.1.0 or better
|
||||
* [CB-11625](https://issues.apache.org/jira/browse/CB-11625) Forgot to add CordovaUri.java to plugin.xml
|
||||
* [CB-11625](https://issues.apache.org/jira/browse/CB-11625) Files Provider does not work with Android 4.4.4 or lower, and I have no idea why. Working around with CordovaUri
|
||||
* [CB-11625](https://issues.apache.org/jira/browse/CB-11625) (Android) : Make this work with previous versions of Cordova via cordova-plugin-compat
|
||||
* BuildConfig from test project crept in source code thanks to Android Studio, removing
|
||||
* [CB-11625](https://issues.apache.org/jira/browse/CB-11625) Managed to get Content Providers to work with a weird mix of Content Providers and non-Content Providers
|
||||
* [CB-11625](https://issues.apache.org/jira/browse/CB-11625) Working on fix to API 24 no longer allowing File URIs to be passed across intents
|
||||
* [CB-11917](https://issues.apache.org/jira/browse/CB-11917) - Remove pull request template checklist item: "iCLA has been submitted…"
|
||||
* [CB-11832](https://issues.apache.org/jira/browse/CB-11832) Incremented plugin version.
|
||||
|
||||
### 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.3.0 (Sep 08, 2016)
|
||||
* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies
|
||||
* [CB-11661](https://issues.apache.org/jira/browse/CB-11661) Add mandatory **iOS 10** privacy description
|
||||
* [CB-11714](https://issues.apache.org/jira/browse/CB-11714) **windows** added more explicit content-type when converting to target data on canvas
|
||||
* [CB-11295](https://issues.apache.org/jira/browse/CB-11295) Add **WP8.1** quirk when choosing image from `photoalbum`
|
||||
* [CB-10067](https://issues.apache.org/jira/browse/CB-10067) Update `PictureSourceType` JSDoc to reflect `README` update
|
||||
* [CB-9070](https://issues.apache.org/jira/browse/CB-9070) Update `CameraPopoverHandle` docs to reflect `README` update
|
||||
* Plugin uses `Android Log class` and not `Cordova LOG class`
|
||||
* [CB-11631](https://issues.apache.org/jira/browse/CB-11631) Appium tests: A working fix for a flaky `selection canceled` failure
|
||||
* [CB-11709](https://issues.apache.org/jira/browse/CB-11709) Tests should use `resolveLocalFileSystemURL()` instead of deprecated `resolveFileSystemURI()`
|
||||
* [CB-11695](https://issues.apache.org/jira/browse/CB-11695) Increased session creation timeout for Appium tests
|
||||
* [CB-11656](https://issues.apache.org/jira/browse/CB-11656) (**Android**) Appium tests: Fixed side menu opening on some more resolutions
|
||||
* [CB-11376](https://issues.apache.org/jira/browse/CB-11376) (**ios**): fix `CameraUsesGeolocation` error
|
||||
* [CB-10067](https://issues.apache.org/jira/browse/CB-10067) (**ios**) clarifications on `PictureSourceType`
|
||||
* [CB-11410](https://issues.apache.org/jira/browse/CB-11410) (**ios**) fix `cameraPopoverHandle.setPosition`
|
||||
* [CB-9070](https://issues.apache.org/jira/browse/CB-9070) (**ios**) Fixed `CameraPopoverHandle` documentation
|
||||
* [CB-11447](https://issues.apache.org/jira/browse/CB-11447) Respect output format when retrieving images from gallery
|
||||
* [CB-11447](https://issues.apache.org/jira/browse/CB-11447) Resolve **iOS** tests failures due to **iOS** quirks
|
||||
* [CB-11553](https://issues.apache.org/jira/browse/CB-11553) Pend failing Appium tests on Sauce Labs for the time being (reverted from commit b69571724035f41642f3ee612c5b66e1f0c4386c)
|
||||
* [CB-11553](https://issues.apache.org/jira/browse/CB-11553) Pend failing Appium tests on Sauce Labs for the time being
|
||||
* [CB-11498](https://issues.apache.org/jira/browse/CB-11498) [**Android**] Appium tests should not fail when there is no camera
|
||||
* Add badges for paramedic builds on Jenkins
|
||||
* [CB-11296](https://issues.apache.org/jira/browse/CB-11296) Appium: Better element clicking and session error handling
|
||||
* [CB-11232](https://issues.apache.org/jira/browse/CB-11232) Appium tests: fixed element tapping on **iOS 9**
|
||||
* [CB-11183](https://issues.apache.org/jira/browse/CB-11183) Appium tests: Added image verification
|
||||
* fixed some bad formatting that hid `HTML` tags and added link to sample
|
||||
* Set **android** quality default value to 50 on the java code
|
||||
* Moving message in PR template to a comment
|
||||
* Add pull request template. This closes #213
|
||||
* [CB-11228](https://issues.apache.org/jira/browse/CB-11228) **browser**: Add classes for styling purposes
|
||||
* [CB-10139](https://issues.apache.org/jira/browse/CB-10139) **browser**: Respect target width and height
|
||||
* [CB-11227](https://issues.apache.org/jira/browse/CB-11227) **browser**: Fix incorrect `mime type`
|
||||
* [CB-11162](https://issues.apache.org/jira/browse/CB-11162) Appium tests: retry spec on failure
|
||||
* [CB-4078](https://issues.apache.org/jira/browse/CB-4078) Fix for `orientation/scaling` on **Android 4.4+** devices
|
||||
* [CB-11165](https://issues.apache.org/jira/browse/CB-11165) removed peer dependency
|
||||
* [CB-11147](https://issues.apache.org/jira/browse/CB-11147) Appium tests: generate descriptive spec names
|
||||
* [CB-10996](https://issues.apache.org/jira/browse/CB-10996) Adding front matter to `README.md`
|
||||
* [CB-11128](https://issues.apache.org/jira/browse/CB-11128) Appum tests: Fixed some of the flaky failures
|
||||
* [CB-11003](https://issues.apache.org/jira/browse/CB-11003) Added Sample section to the Camera plugin README
|
||||
|
||||
### 2.2.0 (Apr 15, 2016)
|
||||
* [CB-10873](https://issues.apache.org/jira/browse/CB-10873) Avoid crash due to usage of uninitialized variable when writing geolocation data to image destination. Properly handle 'CameraUsesGeolocation' option by properly setting geolocation data in EXIF header in all cases
|
||||
* [CB-11073](https://issues.apache.org/jira/browse/CB-11073) Appium tests stability improvements
|
||||
* Replace `PermissionHelper.java` with `cordova-plugin-compat`
|
||||
* Making focus handler work only for **windows 10** phone
|
||||
* [CB-10865](https://issues.apache.org/jira/browse/CB-10865) Run **ios** native tests on **Travis**
|
||||
* [CB-10120](https://issues.apache.org/jira/browse/CB-10120) Fixing use of constants and `PermissionHelper`
|
||||
* [CB-10120](https://issues.apache.org/jira/browse/CB-10120) Fix missing CAMERA permission for **Android M**
|
||||
* [CB-10756](https://issues.apache.org/jira/browse/CB-10756) Adding sterner warnings about `DATA_URL`
|
||||
* [CB-10460](https://issues.apache.org/jira/browse/CB-10460) `getRealPath` return null in some cases
|
||||
|
||||
### 2.1.1 (Mar 09, 2016)
|
||||
* [CB-10825](https://issues.apache.org/jira/browse/CB-10825) **Android** should 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) **Browse** - 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**
|
||||
|
||||
### 2.1.0 (Jan 15, 2016)
|
||||
* added `.ratignore`
|
||||
* [CB-10319](https://issues.apache.org/jira/browse/CB-10319) **Android** Adding reflective helper methods for permission requests
|
||||
* [CB-9189](https://issues.apache.org/jira/browse/CB-9189) **Android** Implementing `save/restore` API to handle Activity destruction
|
||||
* [CB-10241](https://issues.apache.org/jira/browse/CB-10241) App Crash cause by Camera Plugin **iOS 7**
|
||||
* [CB-8940](https://issues.apache.org/jira/browse/CB-8940) Setting `z-index` values to maximum for UI buttons.
|
||||
|
||||
### 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 +331,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
|
||||
|
||||
628
appium-tests/android/android.spec.js
Normal file
628
appium-tests/android/android.spec.js
Normal file
@@ -0,0 +1,628 @@
|
||||
/*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 ParaMedic Appium runner
|
||||
// you can find it here: https://github.com/apache/cordova-paramedic/
|
||||
// it is not necessary to do a full CI setup to run these tests
|
||||
// Run:
|
||||
// node cordova-paramedic/main.js --platform android --plugin cordova-plugin-camera --skipMainTests --target <emulator name>
|
||||
// Please note only Android 5.1 and 4.4 are supported at this point.
|
||||
|
||||
'use strict';
|
||||
|
||||
var wdHelper = global.WD_HELPER;
|
||||
var screenshotHelper = global.SCREENSHOT_HELPER;
|
||||
var wd = wdHelper.getWD();
|
||||
var cameraConstants = require('../../www/CameraConstants');
|
||||
var cameraHelper = require('../helpers/cameraHelper');
|
||||
|
||||
var MINUTE = 60 * 1000;
|
||||
var BACK_BUTTON = 4;
|
||||
var DEFAULT_SCREEN_WIDTH = 360;
|
||||
var DEFAULT_SCREEN_HEIGHT = 567;
|
||||
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW';
|
||||
var PROMISE_PREFIX = 'appium_camera_promise_';
|
||||
var CONTEXT_NATIVE_APP = 'NATIVE_APP';
|
||||
|
||||
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;
|
||||
// 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;
|
||||
// promise count to use in promise ID
|
||||
var promiseCount = 0;
|
||||
// determine if Appium session is created successfully
|
||||
var appiumSessionStarted = false;
|
||||
// determine if camera is present on the device/emulator
|
||||
var cameraAvailable = false;
|
||||
// determine if emulator is within a range of acceptable resolutions able to run these tests
|
||||
var isResolutionBad = true;
|
||||
// a path to the image we add to the gallery before test run
|
||||
var fillerImagePath;
|
||||
|
||||
function getNextPromiseId() {
|
||||
promiseCount += 1;
|
||||
return getCurrentPromiseId();
|
||||
}
|
||||
|
||||
function getCurrentPromiseId() {
|
||||
return PROMISE_PREFIX + promiseCount;
|
||||
}
|
||||
|
||||
function gracefullyFail(error) {
|
||||
fail(error);
|
||||
return driver
|
||||
.quit()
|
||||
.then(function () {
|
||||
return getDriver();
|
||||
});
|
||||
}
|
||||
|
||||
// combinines specified options in all possible variations
|
||||
// you can add more options to test more scenarios
|
||||
function generateOptions() {
|
||||
var sourceTypes = [
|
||||
cameraConstants.PictureSourceType.CAMERA,
|
||||
cameraConstants.PictureSourceType.PHOTOLIBRARY
|
||||
];
|
||||
var destinationTypes = cameraConstants.DestinationType;
|
||||
var encodingTypes = cameraConstants.EncodingType;
|
||||
var allowEditOptions = [ true, false ];
|
||||
var correctOrientationOptions = [ true, false ];
|
||||
|
||||
return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions);
|
||||
}
|
||||
|
||||
// invokes Camera.getPicture() with the specified options
|
||||
// and goes through all UI interactions unless 'skipUiInteractions' is true
|
||||
function getPicture(options, skipUiInteractions) {
|
||||
var promiseId = getNextPromiseId();
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
return driver
|
||||
.context(webviewContext)
|
||||
.execute(cameraHelper.getPicture, [options, promiseId])
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.then(function () {
|
||||
if (skipUiInteractions) {
|
||||
return;
|
||||
}
|
||||
// selecting a picture from gallery
|
||||
if (options.hasOwnProperty('sourceType') &&
|
||||
(options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY ||
|
||||
options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM)) {
|
||||
var tapTile = new wd.TouchAction();
|
||||
var swipeRight = new wd.TouchAction();
|
||||
tapTile
|
||||
.tap({
|
||||
x: Math.round(screenWidth / 4),
|
||||
y: Math.round(screenHeight / 4)
|
||||
});
|
||||
swipeRight
|
||||
.press({x: 10, y: Math.round(screenHeight / 4)})
|
||||
.wait(300)
|
||||
.moveTo({x: Math.round(screenWidth - (screenWidth / 8)), y: 0})
|
||||
.wait(1500)
|
||||
.release()
|
||||
.wait(1000);
|
||||
if (options.allowEdit) {
|
||||
return driver
|
||||
// always wait before performing touchAction
|
||||
.sleep(7000)
|
||||
.performTouchAction(tapTile);
|
||||
}
|
||||
return driver
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery");', 20000)
|
||||
.fail(function () {
|
||||
// If the Gallery button is not present, swipe right to reveal the Gallery button!
|
||||
return driver
|
||||
.performTouchAction(swipeRight)
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery");', 20000)
|
||||
})
|
||||
.click()
|
||||
// always wait before performing touchAction
|
||||
.sleep(7000)
|
||||
.performTouchAction(tapTile);
|
||||
}
|
||||
// taking a picture from camera
|
||||
return driver
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*shutter.*")', MINUTE / 2)
|
||||
.click()
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*done.*")', MINUTE / 2)
|
||||
.click();
|
||||
})
|
||||
.then(function () {
|
||||
if (skipUiInteractions) {
|
||||
return;
|
||||
}
|
||||
if (options.allowEdit) {
|
||||
return driver
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().text("Save")', MINUTE)
|
||||
.click();
|
||||
}
|
||||
})
|
||||
.fail(function (failure) {
|
||||
throw failure;
|
||||
});
|
||||
}
|
||||
|
||||
// checks if the picture was successfully taken
|
||||
// if shouldLoad is falsy, ensures that the error callback was called
|
||||
function checkPicture(shouldLoad, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
return driver
|
||||
.context(webviewContext)
|
||||
.setAsyncScriptTimeout(MINUTE / 2)
|
||||
.executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options])
|
||||
.then(function (result) {
|
||||
if (shouldLoad) {
|
||||
if (result !== 'OK') {
|
||||
fail(result);
|
||||
}
|
||||
} else if (result.indexOf('ERROR') === -1) {
|
||||
throw 'Unexpected success callback with result: ' + result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// deletes the latest image from the gallery
|
||||
function deleteImage() {
|
||||
var holdTile = new wd.TouchAction();
|
||||
holdTile
|
||||
.press({x: Math.round(screenWidth / 4), y: Math.round(screenHeight / 5)})
|
||||
.wait(1000)
|
||||
.release();
|
||||
return driver
|
||||
// always wait before performing touchAction
|
||||
.sleep(7000)
|
||||
.performTouchAction(holdTile)
|
||||
.elementByAndroidUIAutomator('new UiSelector().text("Delete")')
|
||||
.then(function (element) {
|
||||
return element
|
||||
.click()
|
||||
.elementByAndroidUIAutomator('new UiSelector().text("OK")')
|
||||
.click();
|
||||
}, function () {
|
||||
// couldn't find Delete menu item. Possibly there is no image.
|
||||
return driver;
|
||||
});
|
||||
}
|
||||
|
||||
function getDriver() {
|
||||
driver = wdHelper.getDriver('Android');
|
||||
return driver.getWebviewContext()
|
||||
.then(function(context) {
|
||||
webviewContext = context;
|
||||
return driver.context(webviewContext);
|
||||
})
|
||||
.waitForDeviceReady()
|
||||
.injectLibraries()
|
||||
.deleteFillerImage(fillerImagePath)
|
||||
.then(function () {
|
||||
fillerImagePath = null;
|
||||
})
|
||||
.addFillerImage()
|
||||
.then(function (result) {
|
||||
if (result && result.indexOf('ERROR:') === 0) {
|
||||
throw new Error(result);
|
||||
} else {
|
||||
fillerImagePath = result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function recreateSession() {
|
||||
return driver
|
||||
.quit()
|
||||
.finally(function () {
|
||||
return getDriver();
|
||||
});
|
||||
}
|
||||
|
||||
function tryRunSpec(spec) {
|
||||
return driver
|
||||
.then(spec)
|
||||
.fail(function () {
|
||||
return recreateSession()
|
||||
.then(spec)
|
||||
.fail(function() {
|
||||
return recreateSession()
|
||||
.then(spec);
|
||||
});
|
||||
})
|
||||
.fail(gracefullyFail);
|
||||
}
|
||||
|
||||
// produces a generic spec function which
|
||||
// takes a picture with specified options
|
||||
// and then verifies it
|
||||
function generateSpec(options) {
|
||||
return function () {
|
||||
return driver
|
||||
.then(function () {
|
||||
return getPicture(options);
|
||||
})
|
||||
.then(function () {
|
||||
return checkPicture(true, options);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function checkSession(done, skipResolutionCheck) {
|
||||
if (!appiumSessionStarted) {
|
||||
fail('Failed to start a session ' + (lastFailureReason ? lastFailureReason : ''));
|
||||
done();
|
||||
}
|
||||
if (!skipResolutionCheck && isResolutionBad) {
|
||||
fail('The resolution of this target device is not within the appropriate range of width: blah-blah and height: bleh-bleh. The target\'s current resolution is: ' + isResolutionBad);
|
||||
}
|
||||
}
|
||||
|
||||
function checkCamera(pending) {
|
||||
if (!cameraAvailable) {
|
||||
pending('This test requires a functioning camera on the Android device/emulator, and this test suite\'s functional camera test failed on your target environment.');
|
||||
}
|
||||
}
|
||||
afterAll(function (done) {
|
||||
checkSession(done);
|
||||
driver
|
||||
.quit()
|
||||
.done(done);
|
||||
}, MINUTE);
|
||||
|
||||
it('camera.ui.util configuring driver and starting a session', function (done) {
|
||||
getDriver()
|
||||
.then(function () {
|
||||
appiumSessionStarted = true;
|
||||
}, fail)
|
||||
.done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.util determine screen dimensions', function (done) {
|
||||
checkSession(done, /*skipResolutionCheck?*/ true); // skip the resolution check here since we are about to find out in this spec!
|
||||
driver
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.getWindowSize()
|
||||
.then(function (size) {
|
||||
screenWidth = Number(size.width);
|
||||
screenHeight = Number(size.height);
|
||||
isResolutionBad = false;
|
||||
/*
|
||||
TODO: what are acceptable resolution values?
|
||||
need to check what the emulators used in CI return.
|
||||
and also what local device definitions work and dont
|
||||
*/
|
||||
})
|
||||
.done(done);
|
||||
}, MINUTE);
|
||||
|
||||
it('camera.ui.util determine camera availability', function (done) {
|
||||
checkSession(done);
|
||||
var opts = {
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
saveToPhotoAlbum: false
|
||||
};
|
||||
|
||||
return driver
|
||||
.then(function () {
|
||||
return getPicture(opts);
|
||||
})
|
||||
.then(function () {
|
||||
cameraAvailable = true;
|
||||
}, function () {
|
||||
return recreateSession();
|
||||
})
|
||||
.done(done);
|
||||
}, 5 * MINUTE);
|
||||
|
||||
describe('Specs.', function () {
|
||||
// getPicture() with saveToPhotoLibrary = true
|
||||
it('camera.ui.spec.1 Saving a picture to the photo library', function (done) {
|
||||
checkSession(done);
|
||||
checkCamera(pending);
|
||||
var spec = generateSpec({
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
saveToPhotoAlbum: true
|
||||
});
|
||||
|
||||
tryRunSpec(spec)
|
||||
.then(function () {
|
||||
isTestPictureSaved = true;
|
||||
})
|
||||
.done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
|
||||
it('camera.ui.spec.2 Selecting only videos', function (done) {
|
||||
checkSession(done);
|
||||
var spec = function () {
|
||||
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
mediaType: cameraConstants.MediaType.VIDEO };
|
||||
return driver
|
||||
.then(function () {
|
||||
return getPicture(options, true);
|
||||
})
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.then(function () {
|
||||
// try to find "Gallery" menu item
|
||||
// if there's none, the gallery should be already opened
|
||||
return driver
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery")', 20000)
|
||||
.then(function (element) {
|
||||
return element.click();
|
||||
}, function () {
|
||||
return driver;
|
||||
});
|
||||
})
|
||||
.then(function () {
|
||||
// if the gallery is opened on the videos page,
|
||||
// there should be a "Choose video" caption
|
||||
return driver
|
||||
.elementByAndroidUIAutomator('new UiSelector().text("Choose video")')
|
||||
.fail(function () {
|
||||
throw 'Couldn\'t find "Choose video" element.';
|
||||
});
|
||||
})
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.elementByAndroidUIAutomator('new UiSelector().text("Gallery")')
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.finally(function () {
|
||||
return driver
|
||||
.elementById('action_bar_title')
|
||||
.then(function () {
|
||||
// success means we're still in native app
|
||||
return driver
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
// give native app some time to close
|
||||
.sleep(2000)
|
||||
// try again! because every ~30th build
|
||||
// on Sauce Labs this backbutton doesn't work
|
||||
.elementById('action_bar_title')
|
||||
.then(function () {
|
||||
// success means we're still in native app
|
||||
return driver
|
||||
.deviceKeyEvent(BACK_BUTTON);
|
||||
}, function () {
|
||||
// error means we're already in webview
|
||||
return driver;
|
||||
});
|
||||
}, function () {
|
||||
// error means we're already in webview
|
||||
return driver;
|
||||
});
|
||||
});
|
||||
};
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
// getPicture(), then dismiss
|
||||
// wait for the error callback to be called
|
||||
it('camera.ui.spec.3 Dismissing the camera', function (done) {
|
||||
checkSession(done);
|
||||
checkCamera(pending);
|
||||
var spec = function () {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: true,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URI
|
||||
};
|
||||
return driver
|
||||
.then(function () {
|
||||
return getPicture(options, true);
|
||||
})
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*cancel.*")', MINUTE / 2)
|
||||
.click()
|
||||
.then(function () {
|
||||
return checkPicture(false);
|
||||
});
|
||||
};
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
// getPicture(), then take picture but dismiss the edit
|
||||
// wait for the error callback to be called
|
||||
it('camera.ui.spec.4 Dismissing the edit', function (done) {
|
||||
checkSession(done);
|
||||
checkCamera(pending);
|
||||
var spec = function () {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: true,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URI
|
||||
};
|
||||
return driver
|
||||
.then(function () {
|
||||
return getPicture(options, true);
|
||||
})
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*shutter.*")', MINUTE / 2)
|
||||
.click()
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*done.*")', MINUTE / 2)
|
||||
.click()
|
||||
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*discard.*")', MINUTE / 2)
|
||||
.click()
|
||||
.then(function () {
|
||||
return checkPicture(false);
|
||||
});
|
||||
};
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.5 Verifying target image size, sourceType=CAMERA', function (done) {
|
||||
checkSession(done);
|
||||
checkCamera(pending);
|
||||
var spec = generateSpec({
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
});
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.6 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
|
||||
checkSession(done);
|
||||
var spec = generateSpec({
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
});
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.7 Verifying target image size, sourceType=CAMERA, DestinationType=NATIVE_URI', function (done) {
|
||||
checkSession(done);
|
||||
checkCamera(pending);
|
||||
var spec = generateSpec({
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
});
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, DestinationType=NATIVE_URI', function (done) {
|
||||
checkSession(done);
|
||||
var spec = generateSpec({
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
});
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, DestinationType=NATIVE_URI, quality=100', function (done) {
|
||||
checkSession(done);
|
||||
checkCamera(pending);
|
||||
var spec = generateSpec({
|
||||
quality: 100,
|
||||
allowEdit: true,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 305,
|
||||
targetHeight: 305
|
||||
});
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.10 Verifying target image size, sourceType=PHOTOLIBRARY, DestinationType=NATIVE_URI, quality=100', function (done) {
|
||||
checkSession(done);
|
||||
var spec = generateSpec({
|
||||
quality: 100,
|
||||
allowEdit: true,
|
||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 305,
|
||||
targetHeight: 305
|
||||
});
|
||||
|
||||
tryRunSpec(spec).done(done);
|
||||
}, 10 * MINUTE);
|
||||
|
||||
// combine various options for getPicture()
|
||||
generateOptions().forEach(function (spec) {
|
||||
it('camera.ui.spec.11.' + spec.id + ' Combining options. ' + spec.description, function (done) {
|
||||
checkSession(done);
|
||||
if (spec.options.sourceType == cameraConstants.PictureSourceType.CAMERA) {
|
||||
checkCamera(pending);
|
||||
}
|
||||
var s = generateSpec(spec.options);
|
||||
tryRunSpec(s).done(done);
|
||||
}, 10 * MINUTE);
|
||||
});
|
||||
|
||||
it('camera.ui.util Delete filler picture from device library', function (done) {
|
||||
driver
|
||||
.context(webviewContext)
|
||||
.deleteFillerImage(fillerImagePath)
|
||||
.done(done);
|
||||
}, MINUTE);
|
||||
|
||||
it('camera.ui.util Delete taken picture from device library', function (done) {
|
||||
checkSession(done);
|
||||
if (!isTestPictureSaved) {
|
||||
// couldn't save test picture earlier, so nothing to delete here
|
||||
done();
|
||||
return;
|
||||
}
|
||||
// delete exactly one latest picture
|
||||
// this should be the picture we've taken in the first spec
|
||||
driver
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.sleep(1000)
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.sleep(1000)
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.elementById('Apps')
|
||||
.click()
|
||||
.elementByAndroidUIAutomator('new UiSelector().text("Gallery")')
|
||||
.click()
|
||||
.elementByAndroidUIAutomator('new UiSelector().textContains("Pictures")')
|
||||
.click()
|
||||
.then(deleteImage)
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.sleep(1000)
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.sleep(1000)
|
||||
.deviceKeyEvent(BACK_BUTTON)
|
||||
.fail(fail)
|
||||
.finally(done);
|
||||
}, 3 * MINUTE);
|
||||
});
|
||||
|
||||
});
|
||||
305
appium-tests/helpers/cameraHelper.js
Normal file
305
appium-tests/helpers/cameraHelper.js
Normal file
@@ -0,0 +1,305 @@
|
||||
/*jshint node: true */
|
||||
/* global Q, resolveLocalFileSystemURL, Camera, cordova */
|
||||
/*
|
||||
*
|
||||
* 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');
|
||||
|
||||
function findKeyByValue(set, value) {
|
||||
for (var k in set) {
|
||||
if (set.hasOwnProperty(k)) {
|
||||
if (set[k] == value) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getDescription(spec) {
|
||||
var desc = '';
|
||||
|
||||
desc += 'sourceType: ' + findKeyByValue(cameraConstants.PictureSourceType, spec.options.sourceType);
|
||||
desc += ', destinationType: ' + findKeyByValue(cameraConstants.DestinationType, spec.options.destinationType);
|
||||
desc += ', encodingType: ' + findKeyByValue(cameraConstants.EncodingType, spec.options.encodingType);
|
||||
desc += ', allowEdit: ' + spec.options.allowEdit.toString();
|
||||
desc += ', correctOrientation: ' + spec.options.correctOrientation.toString();
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
module.exports.generateSpecs = function (sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions) {
|
||||
var destinationType,
|
||||
sourceType,
|
||||
encodingType,
|
||||
allowEdit,
|
||||
correctOrientation,
|
||||
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)) {
|
||||
for (correctOrientation in correctOrientationOptions) {
|
||||
// if taking picture from photolibrary, don't vary 'correctOrientation' option
|
||||
if ((sourceTypes[sourceType] === cameraConstants.PictureSourceType.PHOTOLIBRARY ||
|
||||
sourceTypes[sourceType] === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) &&
|
||||
correctOrientation === true) { continue; }
|
||||
var spec = {
|
||||
'id': id++,
|
||||
'options': {
|
||||
'destinationType': destinationTypes[destinationType],
|
||||
'sourceType': sourceTypes[sourceType],
|
||||
'encodingType': encodingTypes[encodingType],
|
||||
'allowEdit': allowEditOptions[allowEdit],
|
||||
'saveToPhotoAlbum': false,
|
||||
'correctOrientation': correctOrientationOptions[correctOrientation]
|
||||
}
|
||||
};
|
||||
spec.description = getDescription(spec);
|
||||
specs.push(spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return specs;
|
||||
};
|
||||
|
||||
// calls getPicture() and saves the result in promise
|
||||
// note that this function is executed in the context of tested app
|
||||
// and not in the context of tests
|
||||
module.exports.getPicture = function (opts, pid) {
|
||||
if (navigator._appiumPromises[pid - 1]) {
|
||||
navigator._appiumPromises[pid - 1] = null;
|
||||
}
|
||||
navigator._appiumPromises[pid] = Q.defer();
|
||||
navigator.camera.getPicture(function (result) {
|
||||
navigator._appiumPromises[pid].resolve(result);
|
||||
}, function (err) {
|
||||
navigator._appiumPromises[pid].reject(err);
|
||||
}, opts);
|
||||
};
|
||||
|
||||
// verifies taken picture when the promise is resolved,
|
||||
// calls a callback with 'OK' if everything is good,
|
||||
// calls a callback with 'ERROR: <error message>' if something is wrong
|
||||
// note that this function is executed in the context of tested app
|
||||
// and not in the context of tests
|
||||
module.exports.checkPicture = function (pid, options, cb) {
|
||||
var isIos = cordova.platformId === "ios";
|
||||
var isAndroid = cordova.platformId === "android";
|
||||
// skip image type check if it's unmodified on Android:
|
||||
// https://github.com/apache/cordova-plugin-camera/#android-quirks-1
|
||||
var skipFileTypeCheckAndroid = isAndroid && options.quality === 100 &&
|
||||
!options.targetWidth && !options.targetHeight &&
|
||||
!options.correctOrientation;
|
||||
|
||||
// Skip image type check if destination is NATIVE_URI and source - device's photoalbum
|
||||
// https://github.com/apache/cordova-plugin-camera/#ios-quirks-1
|
||||
var skipFileTypeCheckiOS = isIos && options.destinationType === Camera.DestinationType.NATIVE_URI &&
|
||||
(options.sourceType === Camera.PictureSourceType.PHOTOLIBRARY ||
|
||||
options.sourceType === Camera.PictureSourceType.SAVEDPHOTOALBUM);
|
||||
|
||||
var skipFileTypeCheck = skipFileTypeCheckAndroid || skipFileTypeCheckiOS;
|
||||
|
||||
var desiredType = 'JPEG';
|
||||
var mimeType = 'image/jpeg';
|
||||
if (options.encodingType === Camera.EncodingType.PNG) {
|
||||
desiredType = 'PNG';
|
||||
mimeType = 'image/png';
|
||||
}
|
||||
|
||||
function errorCallback(msg) {
|
||||
if (msg.hasOwnProperty('message')) {
|
||||
msg = msg.message;
|
||||
}
|
||||
cb('ERROR: ' + msg);
|
||||
}
|
||||
|
||||
// verifies the image we get from plugin
|
||||
function verifyResult(result) {
|
||||
if (result.length === 0) {
|
||||
errorCallback('The result is empty.');
|
||||
return;
|
||||
} else if (isIos && options.destinationType === Camera.DestinationType.NATIVE_URI && result.indexOf('assets-library:') !== 0) {
|
||||
errorCallback('Expected "' + result.substring(0, 150) + '"to start with "assets-library:"');
|
||||
return;
|
||||
} else if (isIos && options.destinationType === Camera.DestinationType.FILE_URI && result.indexOf('file:') !== 0) {
|
||||
errorCallback('Expected "' + result.substring(0, 150) + '"to start with "file:"');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
window.atob(result);
|
||||
// if we got here it is a base64 string (DATA_URL)
|
||||
result = "data:" + mimeType + ";base64," + result;
|
||||
} catch (e) {
|
||||
// not DATA_URL
|
||||
if (options.destinationType === Camera.DestinationType.DATA_URL) {
|
||||
errorCallback('Expected ' + result.substring(0, 150) + 'not to be DATA_URL');
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (result.indexOf('file:') === 0 ||
|
||||
result.indexOf('content:') === 0 ||
|
||||
result.indexOf('assets-library:') === 0) {
|
||||
|
||||
if (!window.resolveLocalFileSystemURL) {
|
||||
errorCallback('Cannot read file. Please install cordova-plugin-file to fix this.');
|
||||
return;
|
||||
}
|
||||
resolveLocalFileSystemURL(result, function (entry) {
|
||||
if (skipFileTypeCheck) {
|
||||
displayFile(entry);
|
||||
} else {
|
||||
verifyFile(entry);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
displayImage(result);
|
||||
}
|
||||
} catch (e) {
|
||||
errorCallback(e);
|
||||
}
|
||||
}
|
||||
|
||||
// verifies that the file type matches the requested type
|
||||
function verifyFile(entry) {
|
||||
try {
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function(e) {
|
||||
var arr = (new Uint8Array(e.target.result)).subarray(0, 4);
|
||||
var header = '';
|
||||
for(var i = 0; i < arr.length; i++) {
|
||||
header += arr[i].toString(16);
|
||||
}
|
||||
var actualType = 'unknown';
|
||||
|
||||
switch (header) {
|
||||
case "89504e47":
|
||||
actualType = 'PNG';
|
||||
break;
|
||||
case 'ffd8ffe0':
|
||||
case 'ffd8ffe1':
|
||||
case 'ffd8ffe2':
|
||||
actualType = 'JPEG';
|
||||
break;
|
||||
}
|
||||
|
||||
if (actualType === desiredType) {
|
||||
displayFile(entry);
|
||||
} else {
|
||||
errorCallback('File type mismatch. Expected ' + desiredType + ', got ' + actualType);
|
||||
}
|
||||
};
|
||||
reader.onerror = function (e) {
|
||||
errorCallback(e);
|
||||
};
|
||||
entry.file(function (file) {
|
||||
reader.readAsArrayBuffer(file);
|
||||
}, function (e) {
|
||||
errorCallback(e);
|
||||
});
|
||||
} catch (e) {
|
||||
errorCallback(e);
|
||||
}
|
||||
}
|
||||
|
||||
// reads the file, then displays the image
|
||||
function displayFile(entry) {
|
||||
function onFileReceived(file) {
|
||||
var reader = new FileReader();
|
||||
reader.onerror = function (e) {
|
||||
errorCallback(e);
|
||||
};
|
||||
reader.onloadend = function (evt) {
|
||||
displayImage(evt.target.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
entry.file(onFileReceived, function (e) {
|
||||
errorCallback(e);
|
||||
});
|
||||
}
|
||||
|
||||
function displayImage(image) {
|
||||
try {
|
||||
var imgEl = document.getElementById('camera_test_image');
|
||||
if (!imgEl) {
|
||||
imgEl = document.createElement('img');
|
||||
imgEl.id = 'camera_test_image';
|
||||
document.body.appendChild(imgEl);
|
||||
}
|
||||
var timedOut = false;
|
||||
var loadTimeout = setTimeout(function () {
|
||||
timedOut = true;
|
||||
imgEl.src = '';
|
||||
errorCallback('The image did not load: ' + image.substring(0, 150));
|
||||
}, 10000);
|
||||
var done = function (status) {
|
||||
if (!timedOut) {
|
||||
clearTimeout(loadTimeout);
|
||||
imgEl.src = '';
|
||||
cb(status);
|
||||
}
|
||||
};
|
||||
imgEl.onload = function () {
|
||||
try {
|
||||
// aspect ratio is preserved so only one dimension should match
|
||||
if ((typeof options.targetWidth === 'number' && imgEl.naturalWidth !== options.targetWidth) &&
|
||||
(typeof options.targetHeight === 'number' && imgEl.naturalHeight !== options.targetHeight))
|
||||
{
|
||||
done('ERROR: Wrong image size: ' + imgEl.naturalWidth + 'x' + imgEl.naturalHeight +
|
||||
'. Requested size: ' + options.targetWidth + 'x' + options.targetHeight);
|
||||
} else {
|
||||
done('OK');
|
||||
}
|
||||
} catch (e) {
|
||||
errorCallback(e);
|
||||
}
|
||||
};
|
||||
imgEl.src = image;
|
||||
} catch (e) {
|
||||
errorCallback(e);
|
||||
}
|
||||
}
|
||||
|
||||
navigator._appiumPromises[pid].promise
|
||||
.then(function (result) {
|
||||
verifyResult(result);
|
||||
})
|
||||
.fail(function (e) {
|
||||
errorCallback(e);
|
||||
});
|
||||
};
|
||||
489
appium-tests/ios/ios.spec.js
Normal file
489
appium-tests/ios/ios.spec.js
Normal file
@@ -0,0 +1,489 @@
|
||||
/*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 Paramedic test runner
|
||||
// you can find it here: https://github.com/apache/cordova-paramedic/
|
||||
// it is not necessary to do a full CI setup to run these tests
|
||||
// just run "node cordova-paramedic/main.js --platform ios --plugin cordova-plugin-camera"
|
||||
|
||||
'use strict';
|
||||
|
||||
var wdHelper = global.WD_HELPER;
|
||||
var screenshotHelper = global.SCREENSHOT_HELPER;
|
||||
var isDevice = global.DEVICE;
|
||||
var cameraConstants = require('../../www/CameraConstants');
|
||||
var cameraHelper = require('../helpers/cameraHelper');
|
||||
|
||||
var MINUTE = 60 * 1000;
|
||||
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW_1';
|
||||
var PROMISE_PREFIX = 'appium_camera_promise_';
|
||||
var CONTEXT_NATIVE_APP = 'NATIVE_APP';
|
||||
|
||||
describe('Camera tests iOS.', function () {
|
||||
var driver;
|
||||
var webviewContext = DEFAULT_WEBVIEW_CONTEXT;
|
||||
// promise count to use in promise ID
|
||||
var promiseCount = 0;
|
||||
// going to set this to false if session is created successfully
|
||||
var failedToStart = true;
|
||||
// points out which UI automation to use
|
||||
var isXCUI = false;
|
||||
// spec counter to restart the session
|
||||
var specsRun = 0;
|
||||
|
||||
function getNextPromiseId() {
|
||||
promiseCount += 1;
|
||||
return getCurrentPromiseId();
|
||||
}
|
||||
|
||||
function getCurrentPromiseId() {
|
||||
return PROMISE_PREFIX + promiseCount;
|
||||
}
|
||||
|
||||
function gracefullyFail(error) {
|
||||
fail(error);
|
||||
return driver
|
||||
.quit()
|
||||
.then(function () {
|
||||
return getDriver();
|
||||
});
|
||||
}
|
||||
|
||||
// generates test specs by combining all the specified options
|
||||
// you can add more options to test more scenarios
|
||||
function generateOptions() {
|
||||
var sourceTypes = cameraConstants.PictureSourceType;
|
||||
var destinationTypes = cameraConstants.DestinationType;
|
||||
var encodingTypes = cameraConstants.EncodingType;
|
||||
var allowEditOptions = [ true, false ];
|
||||
var correctOrientationOptions = [ true, false ];
|
||||
|
||||
return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions);
|
||||
}
|
||||
|
||||
function usePicture() {
|
||||
return driver
|
||||
.elementByXPath('//*[@label="Use"]')
|
||||
.click()
|
||||
.fail(function () {
|
||||
if (isXCUI) {
|
||||
return driver
|
||||
.waitForElementByAccessibilityId('Choose', MINUTE / 3)
|
||||
.click();
|
||||
}
|
||||
// For some reason "Choose" element is not clickable by standard Appium methods on iOS <= 9
|
||||
return wdHelper.tapElementByXPath('//UIAButton[@label="Choose"]', driver);
|
||||
});
|
||||
}
|
||||
|
||||
function clickPhoto() {
|
||||
if (isXCUI) {
|
||||
// iOS >=10
|
||||
return driver
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.elementsByXPath('//XCUIElementTypeCell')
|
||||
.then(function(photos) {
|
||||
if (photos.length == 0) {
|
||||
return driver
|
||||
.sleep(0) // driver.source is not a function o.O
|
||||
.source()
|
||||
.then(function (src) {
|
||||
console.log(src);
|
||||
gracefullyFail('Couldn\'t find an image to click');
|
||||
});
|
||||
}
|
||||
// intentionally clicking the second photo here
|
||||
// the first one is not clickable for some reason
|
||||
return photos[1].click();
|
||||
});
|
||||
}
|
||||
// iOS <10
|
||||
return driver
|
||||
.elementByXPath('//UIACollectionCell')
|
||||
.click();
|
||||
}
|
||||
|
||||
function getPicture(options, cancelCamera, skipUiInteractions) {
|
||||
var promiseId = getNextPromiseId();
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
return driver
|
||||
.context(webviewContext)
|
||||
.execute(cameraHelper.getPicture, [options, promiseId])
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.then(function () {
|
||||
if (skipUiInteractions) {
|
||||
return;
|
||||
}
|
||||
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
|
||||
return driver
|
||||
.waitForElementByAccessibilityId('Camera Roll', MINUTE / 2)
|
||||
.click()
|
||||
.then(function () {
|
||||
return clickPhoto();
|
||||
})
|
||||
.then(function () {
|
||||
if (!options.allowEdit) {
|
||||
return driver;
|
||||
}
|
||||
return usePicture();
|
||||
});
|
||||
}
|
||||
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) {
|
||||
return clickPhoto()
|
||||
.then(function () {
|
||||
if (!options.allowEdit) {
|
||||
return driver;
|
||||
}
|
||||
return usePicture();
|
||||
});
|
||||
}
|
||||
if (cancelCamera) {
|
||||
return driver
|
||||
.waitForElementByAccessibilityId('Cancel', MINUTE / 2)
|
||||
.click();
|
||||
}
|
||||
return driver
|
||||
.waitForElementByAccessibilityId('Take Picture', MINUTE / 2)
|
||||
.click()
|
||||
.waitForElementByAccessibilityId('Use Photo', MINUTE / 2)
|
||||
.click();
|
||||
})
|
||||
.fail(fail);
|
||||
}
|
||||
|
||||
// checks if the picture was successfully taken
|
||||
// if shouldLoad is falsy, ensures that the error callback was called
|
||||
function checkPicture(shouldLoad, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
return driver
|
||||
.context(webviewContext)
|
||||
.setAsyncScriptTimeout(MINUTE / 2)
|
||||
.executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options])
|
||||
.then(function (result) {
|
||||
if (shouldLoad) {
|
||||
if (result !== 'OK') {
|
||||
fail(result);
|
||||
}
|
||||
} else if (result.indexOf('ERROR') === -1) {
|
||||
throw 'Unexpected success callback with result: ' + result;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// takes a picture with the specified options
|
||||
// and then verifies it
|
||||
function runSpec(options, done, pending) {
|
||||
if (options.sourceType === cameraConstants.PictureSourceType.CAMERA && !isDevice) {
|
||||
pending('Camera is not available on iOS simulator');
|
||||
}
|
||||
checkSession(done);
|
||||
specsRun += 1;
|
||||
return driver
|
||||
.then(function () {
|
||||
return getPicture(options);
|
||||
})
|
||||
.then(function () {
|
||||
return checkPicture(true, options);
|
||||
})
|
||||
.fail(gracefullyFail);
|
||||
}
|
||||
|
||||
function getDriver() {
|
||||
failedToStart = true;
|
||||
driver = wdHelper.getDriver('iOS');
|
||||
return wdHelper.getWebviewContext(driver)
|
||||
.then(function(context) {
|
||||
webviewContext = context;
|
||||
return driver.context(webviewContext);
|
||||
})
|
||||
.then(function () {
|
||||
return wdHelper.waitForDeviceReady(driver);
|
||||
})
|
||||
.then(function () {
|
||||
return wdHelper.injectLibraries(driver);
|
||||
})
|
||||
.sessionCapabilities()
|
||||
.then(function (caps) {
|
||||
var platformVersion = parseFloat(caps.platformVersion);
|
||||
isXCUI = platformVersion >= 10.0;
|
||||
})
|
||||
.then(function () {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
return driver
|
||||
.then(function () { return getPicture(options, false, true); })
|
||||
.context(CONTEXT_NATIVE_APP)
|
||||
.acceptAlert()
|
||||
.then(function alertDismissed() {
|
||||
// TODO: once we move to only XCUITest-based (which is force on you in either iOS 10+ or Xcode 8+)
|
||||
// UI tests, we will have to:
|
||||
// a) remove use of autoAcceptAlerts appium capability since it no longer functions in XCUITest
|
||||
// b) can remove this entire then() clause, as we do not need to explicitly handle the acceptAlert
|
||||
// failure callback, since we will be guaranteed to hit the permission dialog on startup.
|
||||
}, function noAlert() {
|
||||
// in case the contacts permission alert never showed up: no problem, don't freak out.
|
||||
// This can happen if:
|
||||
// a) The application-under-test already had photos permissions granted to it
|
||||
// b) Appium's autoAcceptAlerts capability is provided (and functioning)
|
||||
})
|
||||
.elementByAccessibilityId('Cancel', 10000)
|
||||
.click();
|
||||
})
|
||||
.then(function () {
|
||||
failedToStart = false;
|
||||
});
|
||||
}
|
||||
|
||||
function checkSession(done) {
|
||||
if (failedToStart) {
|
||||
fail('Failed to start a session');
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
it('camera.ui.util configure driver and start a session', function (done) {
|
||||
getDriver()
|
||||
.fail(fail)
|
||||
.done(done);
|
||||
}, 15 * MINUTE);
|
||||
|
||||
describe('Specs.', function () {
|
||||
afterEach(function (done) {
|
||||
if (specsRun >= 15) {
|
||||
specsRun = 0;
|
||||
// we need to restart the session regularly because for some reason
|
||||
// when running against iOS 10 simulator on SauceLabs,
|
||||
// Appium cannot handle more than ~20 specs at one session
|
||||
// the error would be as follows:
|
||||
// "Could not proxy command to remote server. Original error: Error: connect ECONNREFUSED 127.0.0.1:8100"
|
||||
checkSession(done);
|
||||
return driver
|
||||
.quit()
|
||||
.then(function () {
|
||||
return getDriver();
|
||||
})
|
||||
.done(done);
|
||||
} else {
|
||||
done();
|
||||
}
|
||||
}, 15 * MINUTE);
|
||||
|
||||
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
|
||||
it('camera.ui.spec.1 Selecting only videos', function (done) {
|
||||
checkSession(done);
|
||||
specsRun += 1;
|
||||
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
mediaType: cameraConstants.MediaType.VIDEO };
|
||||
driver
|
||||
// skip ui unteractions
|
||||
.then(function () { return getPicture(options, false, true); })
|
||||
.waitForElementByXPath('//*[contains(@label,"Videos")]', MINUTE / 2)
|
||||
.elementByAccessibilityId('Cancel')
|
||||
.click()
|
||||
.fail(gracefullyFail)
|
||||
.done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
// getPicture(), then dismiss
|
||||
// wait for the error callback to be called
|
||||
it('camera.ui.spec.2 Dismissing the camera', function (done) {
|
||||
checkSession(done);
|
||||
if (!isDevice) {
|
||||
pending('Camera is not available on iOS simulator');
|
||||
}
|
||||
specsRun += 1;
|
||||
var options = { sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
saveToPhotoAlbum: false };
|
||||
driver
|
||||
.then(function () {
|
||||
return getPicture(options, true);
|
||||
})
|
||||
.then(function () {
|
||||
return checkPicture(false);
|
||||
})
|
||||
.fail(gracefullyFail)
|
||||
.done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.3 Verifying target image size, sourceType=CAMERA', function (done) {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.4 Verifying target image size, sourceType=SAVEDPHOTOALBUM', function (done) {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.5 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.6 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL', function (done) {
|
||||
// remove this line if you don't mind the tests leaving a photo saved on device
|
||||
pending('Cannot prevent iOS from saving the picture to photo library');
|
||||
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URL,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.7 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL', function (done) {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URL,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL', function (done) {
|
||||
var options = {
|
||||
quality: 50,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URL,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 210,
|
||||
targetHeight: 210
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL, quality=100', function (done) {
|
||||
// remove this line if you don't mind the tests leaving a photo saved on device
|
||||
pending('Cannot prevent iOS from saving the picture to photo library');
|
||||
|
||||
var options = {
|
||||
quality: 100,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URL,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 305,
|
||||
targetHeight: 305
|
||||
};
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.10 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL, quality=100', function (done) {
|
||||
var options = {
|
||||
quality: 100,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.SAVEDPHOTOALBUM,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URL,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 305,
|
||||
targetHeight: 305
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
it('camera.ui.spec.11 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL, quality=100', function (done) {
|
||||
var options = {
|
||||
quality: 100,
|
||||
allowEdit: false,
|
||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||
destinationType: cameraConstants.DestinationType.FILE_URL,
|
||||
saveToPhotoAlbum: false,
|
||||
targetWidth: 305,
|
||||
targetHeight: 305
|
||||
};
|
||||
|
||||
runSpec(options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
|
||||
// combine various options for getPicture()
|
||||
generateOptions().forEach(function (spec) {
|
||||
it('camera.ui.spec.12.' + spec.id + ' Combining options. ' + spec.description, function (done) {
|
||||
// remove this check if you don't mind the tests leaving a photo saved on device
|
||||
if (spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA &&
|
||||
spec.options.destinationType === cameraConstants.DestinationType.NATIVE_URI) {
|
||||
pending('Skipping: cannot prevent iOS from saving the picture to photo library and cannot delete it. ' +
|
||||
'For more info, see iOS quirks here: https://github.com/apache/cordova-plugin-camera#ios-quirks-1');
|
||||
}
|
||||
|
||||
runSpec(spec.options, done, pending).done(done);
|
||||
}, 7 * MINUTE);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('camera.ui.util Destroy the session', function (done) {
|
||||
checkSession(done);
|
||||
driver
|
||||
.quit()
|
||||
.done(done);
|
||||
}, 5 * MINUTE);
|
||||
});
|
||||
443
jsdoc2md/TEMPLATE.md
Normal file
443
jsdoc2md/TEMPLATE.md
Normal file
@@ -0,0 +1,443 @@
|
||||
---
|
||||
title: Camera
|
||||
description: Take pictures with the device camera.
|
||||
---
|
||||
{{>cdv-license~}}
|
||||
|
||||
|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI |
|
||||
|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=android,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=ios,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-store,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-8.1-phone,PLUGIN=cordova-plugin-camera/)|[](http://cordova-ci.cloudapp.net:8080/job/cordova-periodic-build/PLATFORM=windows-10-store,PLUGIN=cordova-plugin-camera/)|[](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' }}
|
||||
|
||||
|
||||
### iOS Quirks
|
||||
|
||||
Since iOS 10 it's mandatory to add a `NSCameraUsageDescription` and `NSPhotoLibraryUsageDescription` in the info.plist.
|
||||
|
||||
- `NSCameraUsageDescription` describes the reason that the app accesses the user’s camera.
|
||||
- `NSPhotoLibraryUsageDescription` describes the reason the app accesses the user's photo library.
|
||||
|
||||
When the system prompts the user to allow access, this string is displayed as part of the dialog box.
|
||||
|
||||
To add this entry you can pass the following variables on plugin install.
|
||||
|
||||
- `CAMERA_USAGE_DESCRIPTION` for `NSCameraUsageDescription`
|
||||
- `PHOTOLIBRARY_USAGE_DESCRIPTION` for `NSPhotoLibraryUsageDescription`
|
||||
|
||||
Example:
|
||||
|
||||
cordova plugin add cordova-plugin-camera --variable CAMERA_USAGE_DESCRIPTION="your usage message" --variable PHOTOLIBRARY_USAGE_DESCRIPTION="your usage message"
|
||||
|
||||
If you don't pass the variable, the plugin will add an empty string as value.
|
||||
|
||||
---
|
||||
|
||||
# API Reference <a name="reference"></a>
|
||||
|
||||
{{#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 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);
|
||||
}
|
||||
|
||||
Take a photo and retrieve it as a Base64-encoded image:
|
||||
|
||||
/**
|
||||
* Warning: Using DATA_URL is not recommended! The DATA_URL destination
|
||||
* type is very memory intensive, even with a low quality setting. Using it
|
||||
* can result in out of memory errors and application crashes. Use FILE_URI
|
||||
* or NATIVE_URI instead.
|
||||
*/
|
||||
navigator.camera.getPicture(onSuccess, onFail, { quality: 25,
|
||||
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);
|
||||
}
|
||||
|
||||
#### 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.
|
||||
|
||||
#### Windows quirks
|
||||
|
||||
On Windows Phone 8.1 using `SAVEDPHOTOALBUM` or `PHOTOLIBRARY` as a source type causes application to suspend until file picker returns the selected image and
|
||||
then restore with start page as defined in app's `config.xml`. In case when `camera.getPicture` was called from different page, this will lead to reloading
|
||||
start page from scratch and success and error callbacks will never be called.
|
||||
|
||||
To avoid this we suggest using SPA pattern or call `camera.getPicture` only from your app's start page.
|
||||
|
||||
More information about Windows Phone 8.1 picker APIs is here: [How to continue your Windows Phone app after calling a file picker](https://msdn.microsoft.com/en-us/library/windows/apps/dn720490.aspx)
|
||||
|
||||
#### 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.
|
||||
|
||||
- When using `destinationType.NATIVE_URI` and `sourceType.PHOTOLIBRARY` or `sourceType.SAVEDPHOTOALBUM`, all editing options are ignored and link is returned to original picture.
|
||||
|
||||
#### 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
|
||||
|
||||
## Sample: Take Pictures, Select Pictures from the Picture Library, and Get Thumbnails <a name="sample"></a>
|
||||
|
||||
The Camera plugin allows you to do things like open the device's Camera app and take a picture, or open the file picker and select one. The code snippets in this section demonstrate different tasks including:
|
||||
|
||||
* Open the Camera app and [take a Picture](#takePicture)
|
||||
* Take a picture and [return thumbnails](#getThumbnails) (resized picture)
|
||||
* Take a picture and [generate a FileEntry object](#convert)
|
||||
* [Select a file](#selectFile) from the picture library
|
||||
* Select a JPEG image and [return thumbnails](#getFileThumbnails) (resized image)
|
||||
* Select an image and [generate a FileEntry object](#convert)
|
||||
|
||||
## Take a Picture <a name="takePicture"></a>
|
||||
|
||||
Before you can take a picture, you need to set some Camera plugin options to pass into the Camera plugin's `getPicture` function. Here is a common set of recommendations. In this example, you create the object that you will use for the Camera options, and set the `sourceType` dynamically to support both the Camera app and the file picker.
|
||||
|
||||
```js
|
||||
function setOptions(srcType) {
|
||||
var options = {
|
||||
// Some common settings are 20, 50, and 100
|
||||
quality: 50,
|
||||
destinationType: Camera.DestinationType.FILE_URI,
|
||||
// In this app, dynamically set the picture source, Camera or photo gallery
|
||||
sourceType: srcType,
|
||||
encodingType: Camera.EncodingType.JPEG,
|
||||
mediaType: Camera.MediaType.PICTURE,
|
||||
allowEdit: true,
|
||||
correctOrientation: true //Corrects Android orientation quirks
|
||||
}
|
||||
return options;
|
||||
}
|
||||
```
|
||||
|
||||
Typically, you want to use a FILE_URI instead of a DATA_URL to avoid most memory issues. JPEG is the recommended encoding type for Android.
|
||||
|
||||
You take a picture by passing in the options object to `getPicture`, which takes a CameraOptions object as the third argument. When you call `setOptions`, pass `Camera.PictureSourceType.CAMERA` as the picture source.
|
||||
|
||||
```js
|
||||
function openCamera(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.CAMERA;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
displayImage(imageUri);
|
||||
// You may choose to copy the picture, save it somewhere, or upload.
|
||||
func(imageUri);
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
Once you take the picture, you can display it or do something else. In this example, call the app's `displayImage` function from the preceding code.
|
||||
|
||||
```js
|
||||
function displayImage(imgUri) {
|
||||
|
||||
var elem = document.getElementById('imageFile');
|
||||
elem.src = imgUri;
|
||||
}
|
||||
```
|
||||
|
||||
To display the image on some platforms, you might need to include the main part of the URI in the Content-Security-Policy `<meta>` element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your `<meta>` element. Here is an example.
|
||||
|
||||
```html
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
|
||||
```
|
||||
|
||||
## Take a Picture and Return Thumbnails (Resize the Picture) <a name="getThumbnails"></a>
|
||||
|
||||
To get smaller images, you can return a resized image by passing both `targetHeight` and `targetWidth` values with your CameraOptions object. In this example, you resize the returned image to fit in a 100px by 100px box (the aspect ratio is maintained, so 100px is either the height or width, whichever is greater in the source).
|
||||
|
||||
```js
|
||||
function openCamera(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.CAMERA;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
if (selection == "camera-thmb") {
|
||||
options.targetHeight = 100;
|
||||
options.targetWidth = 100;
|
||||
}
|
||||
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
// Do something
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
## Select a File from the Picture Library <a name="selectFile"></a>
|
||||
|
||||
When selecting a file using the file picker, you also need to set the CameraOptions object. In this example, set the `sourceType` to `Camera.PictureSourceType.SAVEDPHOTOALBUM`. To open the file picker, call `getPicture` just as you did in the previous example, passing in the success and error callbacks along with CameraOptions object.
|
||||
|
||||
```js
|
||||
function openFilePicker(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
// Do something
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
## Select an Image and Return Thumbnails (resized images) <a name="getFileThumbnails"></a>
|
||||
|
||||
Resizing a file selected with the file picker works just like resizing using the Camera app; set the `targetHeight` and `targetWidth` options.
|
||||
|
||||
```js
|
||||
function openFilePicker(selection) {
|
||||
|
||||
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
|
||||
var options = setOptions(srcType);
|
||||
var func = createNewFileEntry;
|
||||
|
||||
if (selection == "picker-thmb") {
|
||||
// To downscale a selected image,
|
||||
// Camera.EncodingType (e.g., JPEG) must match the selected image type.
|
||||
options.targetHeight = 100;
|
||||
options.targetWidth = 100;
|
||||
}
|
||||
|
||||
navigator.camera.getPicture(function cameraSuccess(imageUri) {
|
||||
|
||||
// Do something with image
|
||||
|
||||
}, function cameraError(error) {
|
||||
console.debug("Unable to obtain picture: " + error, "app");
|
||||
|
||||
}, options);
|
||||
}
|
||||
```
|
||||
|
||||
## Take a picture and get a FileEntry Object <a name="convert"></a>
|
||||
|
||||
If you want to do something like copy the image to another location, or upload it somewhere using the FileTransfer plugin, you need to get a FileEntry object for the returned picture. To do that, call `window.resolveLocalFileSystemURL` on the file URI returned by the Camera app. If you need to use a FileEntry object, set the `destinationType` to `Camera.DestinationType.FILE_URI` in your CameraOptions object (this is also the default value).
|
||||
|
||||
>*Note* You need the [File plugin](https://www.npmjs.com/package/cordova-plugin-file) to call `window.resolveLocalFileSystemURL`.
|
||||
|
||||
Here is the call to `window.resolveLocalFileSystemURL`. The image URI is passed to this function from the success callback of `getPicture`. The success handler of `resolveLocalFileSystemURL` receives the FileEntry object.
|
||||
|
||||
```js
|
||||
function getFileEntry(imgUri) {
|
||||
window.resolveLocalFileSystemURL(imgUri, function success(fileEntry) {
|
||||
|
||||
// Do something with the FileEntry object, like write to it, upload it, etc.
|
||||
// writeFile(fileEntry, imgUri);
|
||||
console.log("got file: " + fileEntry.fullPath);
|
||||
// displayFileData(fileEntry.nativeURL, "Native URL");
|
||||
|
||||
}, function () {
|
||||
// If don't get the FileEntry (which may happen when testing
|
||||
// on some emulators), copy to a new FileEntry.
|
||||
createNewFileEntry(imgUri);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
In the example shown in the preceding code, you call the app's `createNewFileEntry` function if you don't get a valid FileEntry object. The image URI returned from the Camera app should result in a valid FileEntry, but platform behavior on some emulators may be different for files returned from the file picker.
|
||||
|
||||
>*Note* To see an example of writing to a FileEntry, see the [File plugin README](https://www.npmjs.com/package/cordova-plugin-file).
|
||||
|
||||
The code shown here creates a file in your app's cache (in sandboxed storage) named `tempFile.jpeg`. With the new FileEntry object, you can copy the image to the file or do something else like upload it.
|
||||
|
||||
```js
|
||||
function createNewFileEntry(imgUri) {
|
||||
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function success(dirEntry) {
|
||||
|
||||
// JPEG file
|
||||
dirEntry.getFile("tempFile.jpeg", { create: true, exclusive: false }, function (fileEntry) {
|
||||
|
||||
// Do something with it, like write to it, upload it, etc.
|
||||
// writeFile(fileEntry, imgUri);
|
||||
console.log("got file: " + fileEntry.fullPath);
|
||||
// displayFileData(fileEntry.fullPath, "File copied to");
|
||||
|
||||
}, onErrorCreateFile);
|
||||
|
||||
}, onErrorResolveUrl);
|
||||
}
|
||||
```
|
||||
25
package.json
25
package.json
@@ -1,7 +1,8 @@
|
||||
{
|
||||
"name": "cordova-plugin-camera",
|
||||
"version": "1.2.0",
|
||||
"version": "2.4.0",
|
||||
"description": "Cordova Camera Plugin",
|
||||
"types": "./types/index.d.ts",
|
||||
"cordova": {
|
||||
"id": "cordova-plugin-camera",
|
||||
"platforms": [
|
||||
@@ -38,9 +39,25 @@
|
||||
"cordova-browser",
|
||||
"cordova-windows"
|
||||
],
|
||||
"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",
|
||||
"engines": {
|
||||
"cordovaDependencies": {
|
||||
"3.0.0": {
|
||||
"cordova": ">100"
|
||||
}
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"dmd-plugin-cordova-plugin": "^0.1.0",
|
||||
"husky": "^0.10.1",
|
||||
"jsdoc-to-markdown": "^1.2.0",
|
||||
"jshint": "^2.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
51
plugin.xml
51
plugin.xml
@@ -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.4.0">
|
||||
<name>Camera</name>
|
||||
<description>Cordova Camera Plugin</description>
|
||||
<license>Apache 2.0</license>
|
||||
@@ -30,6 +30,8 @@
|
||||
<repo>https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git</repo>
|
||||
<issue>https://issues.apache.org/jira/browse/CB/component/12320645</issue>
|
||||
|
||||
<dependency id="cordova-plugin-compat" version="^1.1.0" />
|
||||
|
||||
<js-module src="www/CameraConstants.js" name="Camera">
|
||||
<clobbers target="Camera" />
|
||||
</js-module>
|
||||
@@ -50,12 +52,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">
|
||||
@@ -67,16 +69,31 @@
|
||||
<config-file target="AndroidManifest.xml" parent="/*">
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="application">
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true" >
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths"/>
|
||||
</provider>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/CameraLauncher.java" target-dir="src/org/apache/cordova/camera" />
|
||||
<source-file src="src/android/CordovaUri.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/xml/provider_paths.xml" target-dir="res/xml" />
|
||||
|
||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||
<clobbers target="CameraPopoverHandle" />
|
||||
</js-module>
|
||||
</js-module>
|
||||
|
||||
</platform>
|
||||
<framework src="com.android.support:support-v4:24.1.1+" />
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- amazon-fireos -->
|
||||
<platform name="amazon-fireos">
|
||||
@@ -98,7 +115,7 @@
|
||||
</js-module>
|
||||
|
||||
</platform>
|
||||
|
||||
|
||||
<!-- ubuntu -->
|
||||
<platform name="ubuntu">
|
||||
<config-file target="config.xml" parent="/*">
|
||||
@@ -147,11 +164,21 @@
|
||||
<framework src="MobileCoreServices.framework" />
|
||||
<framework src="CoreGraphics.framework" />
|
||||
<framework src="AVFoundation.framework" />
|
||||
|
||||
|
||||
<preference name="CAMERA_USAGE_DESCRIPTION" default=" " />
|
||||
<config-file target="*-Info.plist" parent="NSCameraUsageDescription">
|
||||
<string>$CAMERA_USAGE_DESCRIPTION</string>
|
||||
</config-file>
|
||||
|
||||
<preference name="PHOTOLIBRARY_USAGE_DESCRIPTION" default=" " />
|
||||
<config-file target="*-Info.plist" parent="NSPhotoLibraryUsageDescription">
|
||||
<string>$PHOTOLIBRARY_USAGE_DESCRIPTION</string>
|
||||
</config-file>
|
||||
|
||||
<config-file target="*-Info.plist" parent="NSLocationWhenInUseUsageDescription">
|
||||
<string></string>
|
||||
</config-file>
|
||||
|
||||
|
||||
</platform>
|
||||
|
||||
<!-- blackberry10 -->
|
||||
@@ -222,7 +249,7 @@
|
||||
<clobbers target="CameraPopoverHandle" />
|
||||
</js-module>
|
||||
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
|
||||
<merges target="" />
|
||||
<runs />
|
||||
</js-module>
|
||||
|
||||
</platform>
|
||||
@@ -249,10 +276,8 @@
|
||||
<clobbers target="CameraPopoverHandle" />
|
||||
</js-module>
|
||||
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
|
||||
<merges target="" />
|
||||
<runs />
|
||||
</js-module>
|
||||
</platform>
|
||||
|
||||
|
||||
|
||||
</plugin>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
104
src/android/CordovaUri.java
Normal file
104
src/android/CordovaUri.java
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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 android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.support.v4.content.FileProvider;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/*
|
||||
* This class exists because Andorid FilesProvider doesn't work on Android 4.4.4 and below and throws
|
||||
* weird errors. I'm not sure why writing to shared cache directories is somehow verboten, but it is
|
||||
* and this error is irritating for a Compatibility library to have.
|
||||
*
|
||||
*/
|
||||
|
||||
public class CordovaUri {
|
||||
|
||||
private Uri androidUri;
|
||||
private String fileName;
|
||||
private Uri fileUri;
|
||||
|
||||
/*
|
||||
* We always expect a FileProvider string to be passed in for the file that we create
|
||||
*
|
||||
*/
|
||||
CordovaUri (Uri inputUri)
|
||||
{
|
||||
//Determine whether the file is a content or file URI
|
||||
if(inputUri.getScheme().equals("content"))
|
||||
{
|
||||
androidUri = inputUri;
|
||||
fileName = getFileNameFromUri(androidUri);
|
||||
fileUri = Uri.parse("file://" + fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
fileUri = inputUri;
|
||||
fileName = FileHelper.stripFileProtocol(inputUri.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public Uri getFileUri()
|
||||
{
|
||||
return fileUri;
|
||||
}
|
||||
|
||||
public String getFilePath()
|
||||
{
|
||||
return fileName;
|
||||
}
|
||||
|
||||
/*
|
||||
* This only gets called by takePicture
|
||||
*/
|
||||
|
||||
public Uri getCorrectUri()
|
||||
{
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
|
||||
return androidUri;
|
||||
else
|
||||
return fileUri;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is dirty, but it does the job.
|
||||
*
|
||||
* Since the FilesProvider doesn't really provide you a way of getting a URL from the file,
|
||||
* and since we actually need the Camera to create the file for us most of the time, we don't
|
||||
* actually write the file, just generate the location based on a timestamp, we need to get it
|
||||
* back from the Intent.
|
||||
*
|
||||
* However, the FilesProvider preserves the path, so we can at least write to it from here, since
|
||||
* we own the context in this case.
|
||||
*/
|
||||
|
||||
private String getFileNameFromUri(Uri uri) {
|
||||
String fullUri = uri.toString();
|
||||
String partial_path = fullUri.split("external_files")[1];
|
||||
File external_storage = Environment.getExternalStorageDirectory();
|
||||
String path = external_storage.getAbsolutePath() + partial_path;
|
||||
return path;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -53,13 +55,9 @@ public class FileHelper {
|
||||
if (Build.VERSION.SDK_INT < 11)
|
||||
realPath = FileHelper.getRealPathFromURI_BelowAPI11(cordova.getActivity(), uri);
|
||||
|
||||
// SDK >= 11 && SDK < 19
|
||||
else if (Build.VERSION.SDK_INT < 19)
|
||||
realPath = FileHelper.getRealPathFromURI_API11to18(cordova.getActivity(), uri);
|
||||
|
||||
// SDK > 19 (Android 4.4)
|
||||
// SDK >= 11
|
||||
else
|
||||
realPath = FileHelper.getRealPathFromURI_API19(cordova.getActivity(), uri);
|
||||
realPath = FileHelper.getRealPathFromURI_API11_And_Above(cordova.getActivity(), uri);
|
||||
|
||||
return realPath;
|
||||
}
|
||||
@@ -77,53 +75,71 @@ 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_API11_And_Above(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;
|
||||
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||
// DocumentProvider
|
||||
if (isKitKat && 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
|
||||
}
|
||||
cursor.close();
|
||||
} catch (Exception e) {
|
||||
filePath = "";
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
// DownloadsProvider
|
||||
else if (isDownloadsDocument(uri)) {
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
|
||||
String[] proj = { MediaStore.Images.Media.DATA };
|
||||
String result = null;
|
||||
final String id = DocumentsContract.getDocumentId(uri);
|
||||
final Uri contentUri = ContentUris.withAppendedId(
|
||||
Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||
|
||||
try {
|
||||
CursorLoader cursorLoader = new CursorLoader(context, contentUri, proj, null, null, null);
|
||||
Cursor cursor = cursorLoader.loadInBackground();
|
||||
|
||||
if (cursor != null) {
|
||||
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
|
||||
cursor.moveToFirst();
|
||||
result = cursor.getString(column_index);
|
||||
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);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
// 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;
|
||||
}
|
||||
|
||||
public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri) {
|
||||
@@ -228,4 +244,76 @@ 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);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
} 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());
|
||||
}
|
||||
}
|
||||
|
||||
21
src/android/xml/provider_paths.xml
Normal file
21
src/android/xml/provider_paths.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<external-path name="external_files" path="."/>
|
||||
</paths>
|
||||
@@ -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;
|
||||
|
||||
@@ -19,17 +19,20 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
||||
|
||||
function takePicture(success, error, opts) {
|
||||
if (opts && opts[2] === 1) {
|
||||
capture(success, error);
|
||||
capture(success, error, opts);
|
||||
} else {
|
||||
var input = document.createElement('input');
|
||||
input.style.position = 'relative';
|
||||
input.style.zIndex = HIGHEST_POSSIBLE_Z_INDEX;
|
||||
input.className = 'cordova-camera-select';
|
||||
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 +40,7 @@ function takePicture(success, error, opts) {
|
||||
var imageData = readerEvent.target.result;
|
||||
|
||||
return success(imageData.substr(imageData.indexOf(',') + 1));
|
||||
}
|
||||
};
|
||||
|
||||
reader.readAsDataURL(inputEvent.target.files[0]);
|
||||
};
|
||||
@@ -46,32 +49,51 @@ function takePicture(success, error, opts) {
|
||||
}
|
||||
}
|
||||
|
||||
function capture(success, errorCallback) {
|
||||
function capture(success, errorCallback, opts) {
|
||||
var localMediaStream;
|
||||
var targetWidth = opts[3];
|
||||
var targetHeight = opts[4];
|
||||
|
||||
targetWidth = targetWidth == -1?320:targetWidth;
|
||||
targetHeight = targetHeight == -1?240:targetHeight;
|
||||
|
||||
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.className = 'cordova-camera-capture';
|
||||
parent.appendChild(video);
|
||||
parent.appendChild(button);
|
||||
|
||||
video.width = 320;
|
||||
video.height = 240;
|
||||
video.width = targetWidth;
|
||||
video.height = targetHeight;
|
||||
button.innerHTML = 'Capture!';
|
||||
|
||||
button.onclick = function() {
|
||||
// create a canvas and capture a frame from video stream
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.getContext('2d').drawImage(video, 0, 0, 320, 240);
|
||||
|
||||
canvas.width = targetWidth;
|
||||
canvas.height = targetHeight;
|
||||
canvas.getContext('2d').drawImage(video, 0, 0, targetWidth, targetHeight);
|
||||
|
||||
// convert image stored in canvas to base64 encoded image
|
||||
var imageData = canvas.toDataURL('img/png');
|
||||
var imageData = canvas.toDataURL('image/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 +105,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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -218,9 +240,13 @@ static NSString* toBase64(NSData* data) {
|
||||
|
||||
- (void)repositionPopover:(CDVInvokedUrlCommand*)command
|
||||
{
|
||||
NSDictionary* options = [command argumentAtIndex:0 withDefault:nil];
|
||||
if (([[self pickerController] pickerPopoverController] != nil) && [[[self pickerController] pickerPopoverController] isPopoverVisible]) {
|
||||
|
||||
[self displayPopover:options];
|
||||
[[[self pickerController] pickerPopoverController] dismissPopoverAnimated:NO];
|
||||
|
||||
NSDictionary* options = [command argumentAtIndex:0 withDefault:nil];
|
||||
[self displayPopover:options];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSInteger)integerValueForKey:(NSDictionary*)dict key:(NSString*)key defaultValue:(NSInteger)defaultValue
|
||||
@@ -332,28 +358,28 @@ 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 {
|
||||
if (options.usesGeolocation) {
|
||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||
if (controllerMetadata) {
|
||||
self.data = data;
|
||||
self.metadata = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||
if (EXIFDictionary) {
|
||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||
}
|
||||
|
||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||
}
|
||||
[[self locationManager] startUpdatingLocation];
|
||||
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
|
||||
}
|
||||
|
||||
if (options.usesGeolocation) {
|
||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||
if (controllerMetadata) {
|
||||
self.data = data;
|
||||
self.metadata = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||
if (EXIFDictionary) {
|
||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||
}
|
||||
} else {
|
||||
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
|
||||
|
||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||
}
|
||||
[[self locationManager] startUpdatingLocation];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -408,7 +434,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 +443,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 +490,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 +503,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 +523,16 @@ 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) {
|
||||
if (![self usesGeolocation] || picker.sourceType != UIImagePickerControllerSourceTypeCamera) {
|
||||
[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 +564,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 +768,4 @@ static NSString* toBase64(NSData* data) {
|
||||
return cameraPicker;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
@@ -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,13 +73,20 @@ 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 = "";
|
||||
var targetContentType = "";
|
||||
|
||||
if (encodingType == Camera.EncodingType.PNG) {
|
||||
tempPhotoFileName = "camera_cordova_temp_return.png";
|
||||
targetContentType = "image/png";
|
||||
} else {
|
||||
tempPhotoFileName = "camera_cordova_temp_return.jpg";
|
||||
targetContentType = "image/jpeg";
|
||||
}
|
||||
|
||||
var storageFolder = getAppData().localFolder;
|
||||
@@ -93,8 +100,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;
|
||||
|
||||
@@ -103,7 +112,7 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
|
||||
|
||||
canvas.getContext("2d").drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||
|
||||
var fileContent = canvas.toDataURL(file.contentType).split(',')[1];
|
||||
var fileContent = canvas.toDataURL(targetContentType).split(',')[1];
|
||||
|
||||
var storageFolder = getAppData().localFolder;
|
||||
|
||||
@@ -134,8 +143,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 +277,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 +322,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 +388,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 +420,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 +442,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 +457,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 +510,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 +549,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 +581,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 +619,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 +662,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 +686,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 +725,71 @@ 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;
|
||||
|
||||
savePhoto(picture, {
|
||||
// define focus handler for windows phone 10.0
|
||||
var savePhotoOnFocus = function () {
|
||||
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);
|
||||
}, function() {
|
||||
};
|
||||
|
||||
// if windows phone 10, add and delete focus eventHandler to capture the focus back from cameraUI to app
|
||||
if (navigator.appVersion.indexOf('Windows Phone 10.0') >= 0) {
|
||||
window.addEventListener("focus", savePhotoOnFocus);
|
||||
}
|
||||
|
||||
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function (picture) {
|
||||
if (!picture) {
|
||||
errorCallback("User didn't capture a photo.");
|
||||
// Remove the focus handler if present
|
||||
window.removeEventListener("focus", savePhotoOnFocus);
|
||||
return;
|
||||
}
|
||||
cameraPicture = picture;
|
||||
|
||||
// If not windows 10, call savePhoto() now. If windows 10, wait for the app to be in focus again
|
||||
if (navigator.appVersion.indexOf('Windows Phone 10.0') < 0) {
|
||||
savePhoto(cameraPicture, {
|
||||
destinationType: destinationType,
|
||||
targetHeight: targetHeight,
|
||||
targetWidth: targetWidth,
|
||||
encodingType: encodingType,
|
||||
saveToPhotoAlbum: saveToPhotoAlbum
|
||||
}, successCallback, errorCallback);
|
||||
}
|
||||
}, function () {
|
||||
errorCallback("Fail to capture a photo.");
|
||||
window.removeEventListener("focus", savePhotoOnFocus);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -685,7 +809,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) {
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
#import <XCTest/XCTest.h>
|
||||
#import "CDVCamera.h"
|
||||
#import "UIImage+CropScaleOrientation.h"
|
||||
#import <Cordova/NSArray+Comparisons.h>
|
||||
#import <Cordova/NSData+Base64.h>
|
||||
#import <Cordova/NSDictionary+Extensions.h>
|
||||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
|
||||
|
||||
@@ -291,12 +288,14 @@
|
||||
|
||||
// test 640x480
|
||||
|
||||
targetSize = CGSizeMake(640, 480);
|
||||
targetSize = CGSizeMake(480, 640);
|
||||
|
||||
targetImage = [sourceImagePortrait imageByScalingNotCroppingForSize:targetSize];
|
||||
XCTAssertEqual(targetImage.size.width, targetSize.width);
|
||||
XCTAssertEqual(targetImage.size.height, targetSize.height);
|
||||
|
||||
|
||||
targetSize = CGSizeMake(640, 480);
|
||||
|
||||
targetImage = [sourceImageLandscape imageByScalingNotCroppingForSize:targetSize];
|
||||
XCTAssertEqual(targetImage.size.width, targetSize.width);
|
||||
XCTAssertEqual(targetImage.size.height, targetSize.height);
|
||||
@@ -304,24 +303,28 @@
|
||||
|
||||
// test 800x600
|
||||
|
||||
targetSize = CGSizeMake(800, 600);
|
||||
targetSize = CGSizeMake(600, 800);
|
||||
|
||||
targetImage = [sourceImagePortrait imageByScalingNotCroppingForSize:targetSize];
|
||||
XCTAssertEqual(targetImage.size.width, targetSize.width);
|
||||
XCTAssertEqual(targetImage.size.height, targetSize.height);
|
||||
|
||||
targetSize = CGSizeMake(800, 600);
|
||||
|
||||
targetImage = [sourceImageLandscape imageByScalingNotCroppingForSize:targetSize];
|
||||
XCTAssertEqual(targetImage.size.width, targetSize.width);
|
||||
XCTAssertEqual(targetImage.size.height, targetSize.height);
|
||||
|
||||
// test 1024x768
|
||||
|
||||
targetSize = CGSizeMake(1024, 768);
|
||||
targetSize = CGSizeMake(768, 1024);
|
||||
|
||||
targetImage = [sourceImagePortrait imageByScalingNotCroppingForSize:targetSize];
|
||||
XCTAssertEqual(targetImage.size.width, targetSize.width);
|
||||
XCTAssertEqual(targetImage.size.height, targetSize.height);
|
||||
|
||||
targetSize = CGSizeMake(1024, 768);
|
||||
|
||||
targetImage = [sourceImageLandscape imageByScalingNotCroppingForSize:targetSize];
|
||||
XCTAssertEqual(targetImage.size.width, targetSize.width);
|
||||
XCTAssertEqual(targetImage.size.height, targetSize.height);
|
||||
@@ -476,7 +479,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 +490,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 +503,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
|
||||
}
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
"author": "Apache Software Foundation",
|
||||
"license": "Apache Version 2.0",
|
||||
"dependencies": {
|
||||
"cordova-ios": "^3.7.0"
|
||||
"cordova-ios": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xcodebuild -scheme CordovaLib && xcodebuild test -scheme CDVCameraLibTests -destination 'platform=iOS Simulator,name=iPhone 5'"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
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.4.0">
|
||||
<name>Cordova Camera Plugin Tests</name>
|
||||
<license>Apache 2.0</license>
|
||||
|
||||
|
||||
<dependency id="cordova-plugin-file" version=">=2.0.0" />
|
||||
|
||||
<js-module src="tests.js" name="tests">
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/* globals Camera, resolveLocalFileSystemURL, 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,16 +118,19 @@ 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');
|
||||
var startTime = new Date();
|
||||
img.src = url;
|
||||
img.onloadend = function () {
|
||||
img.onload = function () {
|
||||
log('Img size: ' + img.naturalWidth + 'x' + img.naturalHeight);
|
||||
log('Image tag load time: ' + (new Date() - startTime));
|
||||
callback && callback();
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -141,12 +141,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) {
|
||||
resolveLocalFileSystemURI(data, function (e) {
|
||||
if (pictureUrl.indexOf('file:') === 0 || pictureUrl.indexOf('content:') === 0 || pictureUrl.indexOf('ms-appdata:') === 0 || pictureUrl.indexOf('assets-library:') === 0) {
|
||||
resolveLocalFileSystemURL(data, function (e) {
|
||||
fileEntry = e;
|
||||
logCallback('resolveLocalFileSystemURI()', true)(e.toURL());
|
||||
}, logCallback('resolveLocalFileSystemURI()', false));
|
||||
} else if (pictureUrl.indexOf('data:image/jpeg;base64') == 0) {
|
||||
logCallback('resolveLocalFileSystemURL()', true)(e.toURL());
|
||||
readFile();
|
||||
}, logCallback('resolveLocalFileSystemURL()', false));
|
||||
} else if (pictureUrl.indexOf('data:image/jpeg;base64') === 0) {
|
||||
// do nothing
|
||||
} else {
|
||||
var path = pictureUrl.replace(/^file:\/\/(localhost)?/, '').replace(/%20/g, ' ');
|
||||
@@ -164,13 +165,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 +205,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 +216,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 +227,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
|
||||
@@ -249,7 +251,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
|
||||
//cleanup
|
||||
//rename moved file back to original name so other tests can reference image
|
||||
resolveLocalFileSystemURI(destDirEntry.nativeURL+'moved_file.png', function(fileEntry) {
|
||||
resolveLocalFileSystemURL(destDirEntry.nativeURL+'moved_file.png', function(fileEntry) {
|
||||
fileEntry.moveTo(destDirEntry, origName, logCallback('FileEntry.moveTo', true), logCallback('FileEntry.moveTo', false));
|
||||
console.log('Cleanup: successfully renamed file back to original name');
|
||||
}, function () {
|
||||
@@ -257,7 +259,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
});
|
||||
|
||||
//remove copied file
|
||||
resolveLocalFileSystemURI(destDirEntry.nativeURL+'copied_file.png', function(fileEntry) {
|
||||
resolveLocalFileSystemURL(destDirEntry.nativeURL+'copied_file.png', function(fileEntry) {
|
||||
fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
||||
console.log('Cleanup: successfully removed copied file');
|
||||
}, function () {
|
||||
@@ -266,7 +268,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 +289,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 +302,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 +310,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
*/
|
||||
function removeImage() {
|
||||
fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
||||
};
|
||||
}
|
||||
|
||||
function testInputTag(inputEl) {
|
||||
clearStatus();
|
||||
@@ -317,7 +319,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
||||
window.setTimeout(function () {
|
||||
testNativeFile2(inputEl);
|
||||
}, 0);
|
||||
};
|
||||
}
|
||||
|
||||
function testNativeFile2(inputEl) {
|
||||
if (!inputEl.value) {
|
||||
@@ -347,24 +349,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 +466,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);
|
||||
|
||||
174
types/index.d.ts
vendored
Normal file
174
types/index.d.ts
vendored
Normal file
@@ -0,0 +1,174 @@
|
||||
// Type definitions for Apache Cordova Camera plugin
|
||||
// Project: https://github.com/apache/cordova-plugin-camera
|
||||
// Definitions by: Microsoft Open Technologies Inc <http://msopentech.com>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
//
|
||||
// Copyright (c) Microsoft Open Technologies Inc
|
||||
// Licensed under the MIT license.
|
||||
|
||||
interface Navigator {
|
||||
/**
|
||||
* This plugin provides an API for taking pictures and for choosing images from the system's image library.
|
||||
*/
|
||||
camera: Camera;
|
||||
}
|
||||
|
||||
/**
|
||||
* This plugin provides an API for taking pictures and for choosing images from the system's image library.
|
||||
*/
|
||||
interface Camera {
|
||||
/**
|
||||
* Removes intermediate photos taken by the camera from temporary storage.
|
||||
* @param onSuccess Success callback, that called when cleanup succeeds.
|
||||
* @param onError Error callback, that get an error message.
|
||||
*/
|
||||
cleanup(
|
||||
onSuccess: () => void,
|
||||
onError: (message: string) => void): void;
|
||||
/**
|
||||
* Takes a photo using the camera, or retrieves a photo from the device's image gallery.
|
||||
* @param cameraSuccess Success callback, that get the image
|
||||
* as a base64-encoded String, or as the URI for the image file.
|
||||
* @param cameraError Error callback, that get an error message.
|
||||
* @param cameraOptions Optional parameters to customize the camera settings.
|
||||
*/
|
||||
getPicture(
|
||||
cameraSuccess: (data: string) => void,
|
||||
cameraError: (message: string) => void,
|
||||
cameraOptions?: CameraOptions): void;
|
||||
// Next will work only on iOS
|
||||
//getPicture(
|
||||
// cameraSuccess: (data: string) => void,
|
||||
// cameraError: (message: string) => void,
|
||||
// cameraOptions?: CameraOptions): CameraPopoverHandle;
|
||||
}
|
||||
|
||||
interface CameraOptions {
|
||||
/** Picture quality in range 0-100. Default is 50 */
|
||||
quality?: number;
|
||||
/**
|
||||
* Choose the format of the return value.
|
||||
* Defined in navigator.camera.DestinationType. Default is FILE_URI.
|
||||
* 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)
|
||||
*/
|
||||
destinationType?: number;
|
||||
/**
|
||||
* Set the source of the picture.
|
||||
* Defined in navigator.camera.PictureSourceType. Default is CAMERA.
|
||||
* PHOTOLIBRARY : 0,
|
||||
* CAMERA : 1,
|
||||
* SAVEDPHOTOALBUM : 2
|
||||
*/
|
||||
sourceType?: number;
|
||||
/** Allow simple editing of image before selection. */
|
||||
allowEdit?: boolean;
|
||||
/**
|
||||
* Choose the returned image file's encoding.
|
||||
* Defined in navigator.camera.EncodingType. Default is JPEG
|
||||
* JPEG : 0 Return JPEG encoded image
|
||||
* PNG : 1 Return PNG encoded image
|
||||
*/
|
||||
encodingType?: number;
|
||||
/**
|
||||
* Width in pixels to scale image. Must be used with targetHeight.
|
||||
* Aspect ratio remains constant.
|
||||
*/
|
||||
targetWidth?: number;
|
||||
/**
|
||||
* Height in pixels to scale image. Must be used with targetWidth.
|
||||
* Aspect ratio remains constant.
|
||||
*/
|
||||
targetHeight?: number;
|
||||
/**
|
||||
* Set the type of media to select from. Only works when PictureSourceType
|
||||
* is PHOTOLIBRARY or SAVEDPHOTOALBUM. Defined in nagivator.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
|
||||
*/
|
||||
mediaType?: number;
|
||||
/** Rotate the image to correct for the orientation of the device during capture. */
|
||||
correctOrientation?: boolean;
|
||||
/** Save the image to the photo album on the device after capture. */
|
||||
saveToPhotoAlbum?: boolean;
|
||||
/**
|
||||
* Choose the camera to use (front- or back-facing).
|
||||
* Defined in navigator.camera.Direction. Default is BACK.
|
||||
* FRONT: 0
|
||||
* BACK: 1
|
||||
*/
|
||||
cameraDirection?: number;
|
||||
/** iOS-only options that specify popover location in iPad. Defined in CameraPopoverOptions. */
|
||||
popoverOptions?: CameraPopoverOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* A handle to the popover dialog created by navigator.camera.getPicture. Used on iOS only.
|
||||
*/
|
||||
interface CameraPopoverHandle {
|
||||
/**
|
||||
* Set the position of the popover.
|
||||
* @param popoverOptions the CameraPopoverOptions that specify the new position.
|
||||
*/
|
||||
setPosition(popoverOptions: CameraPopoverOptions): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
interface CameraPopoverOptions {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
/**
|
||||
* Direction the arrow on the popover should point. Defined in Camera.PopoverArrowDirection
|
||||
* Matches iOS UIPopoverArrowDirection constants.
|
||||
* ARROW_UP : 1,
|
||||
* ARROW_DOWN : 2,
|
||||
* ARROW_LEFT : 4,
|
||||
* ARROW_RIGHT : 8,
|
||||
* ARROW_ANY : 15
|
||||
*/
|
||||
arrowDir : number;
|
||||
}
|
||||
|
||||
declare var Camera: {
|
||||
// Camera constants, defined in Camera plugin
|
||||
DestinationType: {
|
||||
DATA_URL: number;
|
||||
FILE_URI: number;
|
||||
NATIVE_URI: number
|
||||
}
|
||||
Direction: {
|
||||
BACK: number;
|
||||
FRONT: number;
|
||||
}
|
||||
EncodingType: {
|
||||
JPEG: number;
|
||||
PNG: number;
|
||||
}
|
||||
MediaType: {
|
||||
PICTURE: number;
|
||||
VIDEO: number;
|
||||
ALLMEDIA: number;
|
||||
}
|
||||
PictureSourceType: {
|
||||
PHOTOLIBRARY: number;
|
||||
CAMERA: number;
|
||||
SAVEDPHOTOALBUM: number;
|
||||
}
|
||||
// Used only on iOS
|
||||
PopoverArrowDirection: {
|
||||
ARROW_UP: number;
|
||||
ARROW_DOWN: number;
|
||||
ARROW_LEFT: number;
|
||||
ARROW_RIGHT: number;
|
||||
ARROW_ANY: number;
|
||||
}
|
||||
};
|
||||
122
www/Camera.js
122
www/Camera.js
@@ -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,96 @@ 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 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 +157,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", []);
|
||||
};
|
||||
|
||||
@@ -19,35 +19,83 @@
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module Camera
|
||||
*/
|
||||
module.exports = {
|
||||
/**
|
||||
* @description
|
||||
* Defines the output format of `Camera.getPicture` call.
|
||||
* _Note:_ On iOS passing `DestinationType.NATIVE_URI` along with
|
||||
* `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM` will
|
||||
* disable any image modifications (resize, quality change, cropping, etc.) due
|
||||
* to implementation specific.
|
||||
*
|
||||
* @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 can be very memory intensive and cause app crashes or out of memory errors. Use FILE_URI or NATIVE_URI if possible */
|
||||
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
|
||||
},
|
||||
/**
|
||||
* @description
|
||||
* Defines the output format of `Camera.getPicture` call.
|
||||
* _Note:_ On iOS passing `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM`
|
||||
* along with `DestinationType.NATIVE_URI` will disable any image modifications (resize, quality
|
||||
* change, cropping, etc.) due to implementation specific.
|
||||
*
|
||||
* @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 the device's photo library (same as SAVEDPHOTOALBUM for Android) */
|
||||
PHOTOLIBRARY : 0,
|
||||
/** Take picture from camera */
|
||||
CAMERA : 1,
|
||||
/** Choose image only from the device's Camera Roll album (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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
*/
|
||||
*/
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.getElementById('back').onclick = function () {
|
||||
|
||||
@@ -21,10 +21,42 @@
|
||||
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
/**
|
||||
* @namespace navigator
|
||||
*/
|
||||
|
||||
/**
|
||||
* A handle to an image picker popover.
|
||||
*
|
||||
* __Supported Platforms__
|
||||
*
|
||||
* - iOS
|
||||
*
|
||||
* @example
|
||||
* 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 cameraPopoverHandle = new CameraPopoverHandle();
|
||||
* var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY);
|
||||
* cameraPopoverHandle.setPosition(cameraPopoverOptions);
|
||||
* }
|
||||
* @module CameraPopoverHandle
|
||||
*/
|
||||
var CameraPopoverHandle = function() {
|
||||
/**
|
||||
* Can be used to reposition the image selection dialog,
|
||||
* for example, when the device orientation changes.
|
||||
* @memberof CameraPopoverHandle
|
||||
* @instance
|
||||
* @method setPosition
|
||||
* @param {module:CameraPopoverOptions} popoverOptions
|
||||
*/
|
||||
this.setPosition = function(popoverOptions) {
|
||||
var args = [ popoverOptions ];
|
||||
exec(null, null, "Camera", "repositionPopover", args);
|
||||
|
||||
Reference in New Issue
Block a user