forked from public/cordova-plugin-camera
Compare commits
114 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1f1abdac1 | |||
| 9d05a1e58f | |||
| c8581ba2c9 | |||
| e36482c7ba | |||
| 7c90555bd4 | |||
| e25aed97c5 | |||
| bf935dfbd2 | |||
| ad8788a5e6 | |||
| 970aa74c1e | |||
| 4b276ee534 | |||
| 4b99623eda | |||
| 36ea63a60f | |||
| e6a4738031 | |||
| c46fe3b31d | |||
| 67718544a8 | |||
| b00cf97a04 | |||
| fb02f48745 | |||
| 6bf45f005a | |||
| 96b7f55ec2 | |||
| 82f1c83a76 | |||
| 4ae0de1c5e | |||
| b79c659c92 | |||
| 7fad5d5786 | |||
| 1f86358156 | |||
| d456eeb711 | |||
| f761934814 | |||
| 43b3a911c2 | |||
| 8f033f53db | |||
| 51d665a7a1 | |||
| 9f480b10cb | |||
| a6706aef5f | |||
| fb213b2b38 | |||
| 3e24b15934 | |||
| 2acda2d2f9 | |||
| 97d2014d62 | |||
| b2107e0818 | |||
| 16c4325fef | |||
| 80342b0ddd | |||
| 55d419a36e | |||
| 5574490bc3 | |||
| f1b0ccec56 | |||
| e69240f251 | |||
| 5b3a99a6db | |||
| 3b8f64e330 | |||
| f667ba6179 | |||
| cfefa53272 | |||
| 77539558e6 | |||
| cba99940a7 | |||
| 7155b636d5 | |||
| 160ed2d03f | |||
| c3d7e55ad4 | |||
| 1b218cd8a0 | |||
| 51e5c02dcb | |||
| 26d13b0245 | |||
| ef617cc943 | |||
| 1422b0a4f2 | |||
| ba9a803b69 | |||
| 9615620843 | |||
| a33c35152e | |||
| eb98015e8a | |||
| 3c48ea9c9c | |||
| 2286bb3bb2 | |||
| 9badea4c95 | |||
| 180f7b5510 | |||
| d0b381aad8 | |||
| 926fbf0e8c | |||
| e4ff41c07c | |||
| 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 | |||
| b63a0d83e0 | |||
| 3ed3d887ca | |||
| f010394af8 | |||
| 00e0a7dc46 | |||
| b62fdf50f7 | |||
| 744d72a33b | |||
| 3d26986bfd |
@@ -0,0 +1,28 @@
|
|||||||
|
# appveyor file
|
||||||
|
# http://www.appveyor.com/docs/appveyor-yml
|
||||||
|
|
||||||
|
max_jobs: 1
|
||||||
|
|
||||||
|
shallow_clone: true
|
||||||
|
|
||||||
|
init:
|
||||||
|
- git config --global core.autocrlf true
|
||||||
|
|
||||||
|
image:
|
||||||
|
- Visual Studio 2017
|
||||||
|
|
||||||
|
environment:
|
||||||
|
nodejs_version: "4"
|
||||||
|
matrix:
|
||||||
|
- PLATFORM: windows-10-store
|
||||||
|
JUST_BUILD: --justBuild
|
||||||
|
install:
|
||||||
|
- npm cache clean -f
|
||||||
|
- node --version
|
||||||
|
- npm install -g cordova-paramedic@https://github.com/apache/cordova-paramedic.git
|
||||||
|
- npm install -g cordova
|
||||||
|
|
||||||
|
build: off
|
||||||
|
|
||||||
|
test_script:
|
||||||
|
- cordova-paramedic --config pr\%PLATFORM% --plugin . %JUST_BUILD%
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
root: true
|
||||||
|
extends: semistandard
|
||||||
|
rules:
|
||||||
|
indent:
|
||||||
|
- error
|
||||||
|
- 4
|
||||||
|
camelcase: off
|
||||||
|
padded-blocks: off
|
||||||
|
operator-linebreak: off
|
||||||
|
no-throw-literal: off
|
||||||
@@ -17,7 +17,6 @@ Thanks!
|
|||||||
|
|
||||||
|
|
||||||
### Checklist
|
### Checklist
|
||||||
- [ ] [ICLA](http://www.apache.org/licenses/icla.txt) has been signed and submitted to secretary@apache.org.
|
|
||||||
- [ ] [Reported an issue](http://cordova.apache.org/contribute/issues.html) in the JIRA database
|
- [ ] [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.
|
- [ ] 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.
|
- [ ] Added automated test coverage as appropriate for this change.
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+94
-6
@@ -1,8 +1,96 @@
|
|||||||
language: objective-c
|
|
||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
addons:
|
||||||
- "4.2"
|
jwt:
|
||||||
|
secure: QivPLlqTVvOo3TJeHxuBOfxU6lho1I0IxQ3b68yntkEQQJko6kzleXHfgjf0a8aw8m38E3+fxaBWF1bGyucGwOLDWY8Ddt2P2xg44zdXH5EXHd9oIqAgngIdzLvUtH3Db2TbQEtIGOkrnNR2STovjqB7vHGLASQrgs4oL7r32/s=
|
||||||
env:
|
env:
|
||||||
- TEST_DIR=.
|
global:
|
||||||
- TEST_DIR=./tests/ios
|
- SAUCE_USERNAME=snay
|
||||||
script: cd $TEST_DIR && npm install && npm test
|
- TRAVIS_NODE_VERSION="4.2"
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- env: TEST_DIR=.
|
||||||
|
language: objective-c
|
||||||
|
- env: TEST_DIR=./tests/ios
|
||||||
|
language: objective-c
|
||||||
|
- env: PLATFORM=browser-chrome
|
||||||
|
os: linux
|
||||||
|
language: node_js
|
||||||
|
node_js: '4.2'
|
||||||
|
- env: PLATFORM=browser-firefox
|
||||||
|
os: linux
|
||||||
|
language: node_js
|
||||||
|
node_js: '4.2'
|
||||||
|
- env: PLATFORM=browser-safari
|
||||||
|
os: linux
|
||||||
|
language: node_js
|
||||||
|
node_js: '4.2'
|
||||||
|
- env: PLATFORM=browser-edge
|
||||||
|
os: linux
|
||||||
|
language: node_js
|
||||||
|
node_js: '4.2'
|
||||||
|
- env: PLATFORM=ios-9.3
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode7.3
|
||||||
|
language: node_js
|
||||||
|
node_js: "4.2"
|
||||||
|
- env: PLATFORM=ios-10.0
|
||||||
|
os: osx
|
||||||
|
osx_image: xcode7.3
|
||||||
|
language: node_js
|
||||||
|
node_js: "4.2"
|
||||||
|
- env: PLATFORM=android-4.4
|
||||||
|
os: linux
|
||||||
|
language: android
|
||||||
|
jdk: oraclejdk8
|
||||||
|
android:
|
||||||
|
components:
|
||||||
|
- tools
|
||||||
|
- extra-android-m2repository
|
||||||
|
- build-tools-26.0.2
|
||||||
|
- env: PLATFORM=android-5.1
|
||||||
|
os: linux
|
||||||
|
language: android
|
||||||
|
jdk: oraclejdk8
|
||||||
|
android:
|
||||||
|
components:
|
||||||
|
- tools
|
||||||
|
- extra-android-m2repository
|
||||||
|
- build-tools-26.0.2
|
||||||
|
- env: PLATFORM=android-6.0
|
||||||
|
os: linux
|
||||||
|
language: android
|
||||||
|
jdk: oraclejdk8
|
||||||
|
android:
|
||||||
|
components:
|
||||||
|
- tools
|
||||||
|
- extra-android-m2repository
|
||||||
|
- build-tools-26.0.2
|
||||||
|
- env: PLATFORM=android-7.0
|
||||||
|
os: linux
|
||||||
|
language: android
|
||||||
|
jdk: oraclejdk8
|
||||||
|
android:
|
||||||
|
components:
|
||||||
|
- tools
|
||||||
|
- extra-android-m2repository
|
||||||
|
- build-tools-26.0.2
|
||||||
|
before_install:
|
||||||
|
- rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install $TRAVIS_NODE_VERSION
|
||||||
|
- node --version
|
||||||
|
- if [[ "$PLATFORM" =~ android ]]; then gradle --version; fi
|
||||||
|
- if [[ "$PLATFORM" =~ ios ]]; then npm install -g ios-deploy; fi
|
||||||
|
- if [[ "$PLATFORM" =~ android ]]; then echo y | android update sdk -u --filter android-22,android-23,android-24,android-25,android-26;
|
||||||
|
fi
|
||||||
|
- git clone https://github.com/apache/cordova-paramedic /tmp/paramedic && pushd /tmp/paramedic
|
||||||
|
&& npm install && popd
|
||||||
|
- npm install -g cordova
|
||||||
|
install:
|
||||||
|
- npm install
|
||||||
|
script:
|
||||||
|
- if [[ "$TEST_DIR" != "" ]];
|
||||||
|
then cd $TEST_DIR && npm install && npm test;
|
||||||
|
else
|
||||||
|
node /tmp/paramedic/main.js --config pr/$PLATFORM --plugin $(pwd) --shouldUseSauce
|
||||||
|
--buildName travis-plugin-camera-$TRAVIS_JOB_NUMBER;
|
||||||
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ description: Take pictures with the device camera.
|
|||||||
# under the License.
|
# under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI |
|
|AppVeyor|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://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-camera)|[](https://travis-ci.org/apache/cordova-plugin-camera)|
|
||||||
|
|
||||||
# cordova-plugin-camera
|
# cordova-plugin-camera
|
||||||
|
|
||||||
@@ -53,11 +53,11 @@ It is also possible to install via repo url directly ( unstable )
|
|||||||
|
|
||||||
## How to Contribute
|
## 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).
|
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%22cordova-plugin-camera%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).
|
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).
|
: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%22cordova-plugin-camera%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).
|
**Have a solution?** Send a [Pull Request](https://github.com/apache/cordova-plugin-camera/pulls).
|
||||||
|
|
||||||
@@ -76,23 +76,40 @@ Documentation consists of template and API docs produced from the plugin JS code
|
|||||||
|
|
||||||
### iOS Quirks
|
### iOS Quirks
|
||||||
|
|
||||||
Since iOS 10 it's mandatory to add a `NSCameraUsageDescription` and `NSPhotoLibraryUsageDescriptionentry` in the info.plist.
|
Since iOS 10 it's mandatory to provide an usage description in the `info.plist` if trying to access privacy-sensitive data. When the system prompts the user to allow access, this usage description string will displayed as part of the permission dialog box, but if you didn't provide the usage description, the app will crash before showing the dialog. Also, Apple will reject apps that access private data but don't provide an usage description.
|
||||||
|
|
||||||
- `NSCameraUsageDescription` describes the reason that the app accesses the user’s camera.
|
This plugins requires the following usage descriptions:
|
||||||
- `NSPhotoLibraryUsageDescriptionentry` 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.
|
- `NSCameraUsageDescription` specifies the reason for your app to access the device's camera.
|
||||||
|
- `NSPhotoLibraryUsageDescription` specifies the reason for your app to access the user's photo library.
|
||||||
|
- `NSLocationWhenInUseUsageDescription` specifies the reason for your app to access the user's location information while your app is in use. (Set it if you have `CameraUsesGeolocation` preference set to `true`)
|
||||||
|
- `NSPhotoLibraryAddUsageDescription` specifies the reason for your app to get write-only access to the user's photo library
|
||||||
|
|
||||||
To add this entry you can pass the following variables on plugin install.
|
To add these entries into the `info.plist`, you can use the `edit-config` tag in the `config.xml` like this:
|
||||||
|
|
||||||
- `CAMERA_USAGE_DESCRIPTION` for `NSCameraUsageDescription`
|
```
|
||||||
- `PHOTOLIBRARY_USAGE_DESCRIPTION` for `NSPhotoLibraryUsageDescriptionentry`
|
<edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
-
|
<string>need camera access to take pictures</string>
|
||||||
Example:
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
cordova plugin add cordova-plugin-camera --variable CAMERA_USAGE_DESCRIPTION="your usage message"
|
```
|
||||||
|
<edit-config target="NSPhotoLibraryUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
|
<string>need photo library access to get pictures from there</string>
|
||||||
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
If you don't pass the variable, the plugin will add an empty string as value.
|
```
|
||||||
|
<edit-config target="NSLocationWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
|
<string>need location access to find things nearby</string>
|
||||||
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
<edit-config target="NSPhotoLibraryAddUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
|
<string>need photo library access to save pictures there</string>
|
||||||
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -255,7 +272,7 @@ Optional parameters to customize the camera settings.
|
|||||||
| 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.) |
|
| 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. |
|
| 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. |
|
| 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. |
|
| allowEdit | <code>Boolean</code> | <code>false</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. |
|
| 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. |
|
| 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. |
|
| targetHeight | <code>number</code> | | Height in pixels to scale image. Must be used with `targetWidth`. Aspect ratio remains constant. |
|
||||||
|
|||||||
+72
-2
@@ -7,9 +7,9 @@
|
|||||||
# to you under the Apache License, Version 2.0 (the
|
# to you under the Apache License, Version 2.0 (the
|
||||||
# "License"); you may not use this file except in compliance
|
# "License"); you may not use this file except in compliance
|
||||||
# with the License. You may obtain a copy of the License at
|
# with the License. You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing,
|
# Unless required by applicable law or agreed to in writing,
|
||||||
# software distributed under the License is distributed on an
|
# software distributed under the License is distributed on an
|
||||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
@@ -20,6 +20,76 @@
|
|||||||
-->
|
-->
|
||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
|
### 4.0.3 (Apr 12, 2018)
|
||||||
|
* [CB-12593](https://issues.apache.org/jira/browse/CB-12593) **Android** Fix potential `FileProvider` conflicts
|
||||||
|
* Fix a mistake in the examples of usage descriptions (#313)
|
||||||
|
* CB-13854(ios): fix Camera opens in portrait orientation on iphones
|
||||||
|
* [CB-13415](https://issues.apache.org/jira/browse/CB-13415) **Android** Importing corrupt images using the Camera plugin crashes the app
|
||||||
|
|
||||||
|
### 4.0.2 (Jan 24, 2018)
|
||||||
|
* [CB-13781](https://issues.apache.org/jira/browse/CB-13781) (android) Fixed permissions request on **Android** 8 to save a photo into the photo album
|
||||||
|
* [CB-13747](https://issues.apache.org/jira/browse/CB-13747) Add build-tools-26.0.2 to travis
|
||||||
|
|
||||||
|
### 4.0.1 (Dec 27, 2017)
|
||||||
|
* CB-13701Fix to allow 4.0.0 version install
|
||||||
|
|
||||||
|
### 4.0.0 (Dec 15, 2017)
|
||||||
|
* [CB-13661](https://issues.apache.org/jira/browse/CB-13661) Remove deprecated platforms
|
||||||
|
|
||||||
|
### 3.0.0 (Nov 06, 2017)
|
||||||
|
* Added `cordova-OSX` support
|
||||||
|
* [CB-13515](https://issues.apache.org/jira/browse/CB-13515) (all): Add 'protective' entry to `cordovaDependencies`
|
||||||
|
* [CB-13332](https://issues.apache.org/jira/browse/CB-13332) (iOS): document `NSPhotoLibraryAddUsageDescription`
|
||||||
|
* [CB-13264](https://issues.apache.org/jira/browse/CB-13264) (iOS): Remove **iOS** usage descriptions
|
||||||
|
* [CB-13473](https://issues.apache.org/jira/browse/CB-13473) (CI) Removed **Browser** builds from AppVeyor
|
||||||
|
* [CB-13446](https://issues.apache.org/jira/browse/CB-13446) Sync template with previous doc changes
|
||||||
|
* [CB-13294](https://issues.apache.org/jira/browse/CB-13294) Removed `cordova-plugin-compat`
|
||||||
|
* [CB-13299](https://issues.apache.org/jira/browse/CB-13299) (CI) Fix **Android** builds
|
||||||
|
* [CB-12985](https://issues.apache.org/jira/browse/CB-12985) setup `eslint` and removed `jshint`
|
||||||
|
* [CB-13028](https://issues.apache.org/jira/browse/CB-13028) (CI) **Browser** builds on Travis and AppVeyor
|
||||||
|
* [CB-13002](https://issues.apache.org/jira/browse/CB-13002) (Android, **iOS**) Fix occasional Appium tests failures
|
||||||
|
* [CB-13000](https://issues.apache.org/jira/browse/CB-13000) (CI) Speed up **Android** builds
|
||||||
|
* [CB-12991](https://issues.apache.org/jira/browse/CB-12991) (CI) Updated CI badges
|
||||||
|
* [CB-12964](https://issues.apache.org/jira/browse/CB-12964) (android) Fix of bug when Pictures folder did not exist.
|
||||||
|
* [CB-12982](https://issues.apache.org/jira/browse/CB-12982) (Android, **iOS**) Appium tests: try to create a session harder
|
||||||
|
* [CB-12682](https://issues.apache.org/jira/browse/CB-12682) (ios, **Android**): changes cancel error message to be consistent for **iOS** **Android**
|
||||||
|
* [CB-12764](https://issues.apache.org/jira/browse/CB-12764) (android) Adapt Appium tests for **Android** 7
|
||||||
|
* [CB-12847](https://issues.apache.org/jira/browse/CB-12847) added `bugs` entry to `package.json`.
|
||||||
|
|
||||||
|
### 2.4.1 (Apr 27, 2017)
|
||||||
|
* [CB-12622](https://issues.apache.org/jira/browse/CB-12622) Updated build badges in `README`
|
||||||
|
* [CB-12650](https://issues.apache.org/jira/browse/CB-12650) Fix manual test for uploading image
|
||||||
|
* [CB-12685](https://issues.apache.org/jira/browse/CB-12685) added `package.json` to tests folder
|
||||||
|
* [CB-12622](https://issues.apache.org/jira/browse/CB-12622) (android) Appium tests: Bust **Android** 6 and 7 permission dialogs
|
||||||
|
* [CB-12618](https://issues.apache.org/jira/browse/CB-12618) (android) Appium tests: Handle native cling
|
||||||
|
|
||||||
|
### 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
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
### 2.3.0 (Sep 08, 2016)
|
### 2.3.0 (Sep 08, 2016)
|
||||||
* [CB-11795](https://issues.apache.org/jira/browse/CB-11795) Add 'protective' entry to cordovaDependencies
|
* [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-11661](https://issues.apache.org/jira/browse/CB-11661) Add mandatory **iOS 10** privacy description
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
/*jshint node: true, jasmine: true */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
@@ -21,10 +19,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// these tests are meant to be executed by Cordova Medic Appium runner
|
// these tests are meant to be executed by Cordova ParaMedic Appium runner
|
||||||
// you can find it here: https://github.com/apache/cordova-medic/
|
// 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
|
// it is not necessary to do a full CI setup to run these tests
|
||||||
// just run "node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-camera"
|
// 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';
|
'use strict';
|
||||||
|
|
||||||
@@ -40,6 +40,7 @@ var DEFAULT_SCREEN_WIDTH = 360;
|
|||||||
var DEFAULT_SCREEN_HEIGHT = 567;
|
var DEFAULT_SCREEN_HEIGHT = 567;
|
||||||
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW';
|
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW';
|
||||||
var PROMISE_PREFIX = 'appium_camera_promise_';
|
var PROMISE_PREFIX = 'appium_camera_promise_';
|
||||||
|
var CONTEXT_NATIVE_APP = 'NATIVE_APP';
|
||||||
|
|
||||||
describe('Camera tests Android.', function () {
|
describe('Camera tests Android.', function () {
|
||||||
var driver;
|
var driver;
|
||||||
@@ -56,8 +57,21 @@ describe('Camera tests Android.', function () {
|
|||||||
var appiumSessionStarted = false;
|
var appiumSessionStarted = false;
|
||||||
// determine if camera is present on the device/emulator
|
// determine if camera is present on the device/emulator
|
||||||
var cameraAvailable = false;
|
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
|
// a path to the image we add to the gallery before test run
|
||||||
var fillerImagePath;
|
var fillerImagePath;
|
||||||
|
var isAndroid7 = getIsAndroid7();
|
||||||
|
|
||||||
|
function getIsAndroid7() {
|
||||||
|
if (global.USE_SAUCE) {
|
||||||
|
return global.SAUCE_CAPS && (parseFloat(global.SAUCE_CAPS.platformVersion) >= 7);
|
||||||
|
} else {
|
||||||
|
// this is most likely null, meaning we cannot determine if it is Android 7 or not
|
||||||
|
// paramedic needs to be modified to receive and pass the platform version when testing locally
|
||||||
|
return global.PLATFORM_VERSION && (parseFloat(global.PLATFORM_VERSION) >= 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getNextPromiseId() {
|
function getNextPromiseId() {
|
||||||
promiseCount += 1;
|
promiseCount += 1;
|
||||||
@@ -68,10 +82,9 @@ describe('Camera tests Android.', function () {
|
|||||||
return PROMISE_PREFIX + promiseCount;
|
return PROMISE_PREFIX + promiseCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveScreenshotAndFail(error) {
|
function gracefullyFail(error) {
|
||||||
fail(error);
|
fail(error);
|
||||||
return screenshotHelper
|
return driver
|
||||||
.saveScreenshot(driver)
|
|
||||||
.quit()
|
.quit()
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return getDriver();
|
return getDriver();
|
||||||
@@ -100,11 +113,21 @@ describe('Camera tests Android.', function () {
|
|||||||
if (!options) {
|
if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
// assign default values
|
||||||
|
if (!options.hasOwnProperty('allowEdit')) {
|
||||||
|
options.allowEdit = true;
|
||||||
|
}
|
||||||
|
if (!options.hasOwnProperty('destinationType')) {
|
||||||
|
options.destinationType = cameraConstants.DestinationType.FILE_URI;
|
||||||
|
}
|
||||||
|
if (!options.hasOwnProperty('sourceType')) {
|
||||||
|
options.destinationType = cameraConstants.PictureSourceType.CAMERA;
|
||||||
|
}
|
||||||
|
|
||||||
return driver
|
return driver
|
||||||
.context(webviewContext)
|
.context(webviewContext)
|
||||||
.execute(cameraHelper.getPicture, [options, promiseId])
|
.execute(cameraHelper.getPicture, [options, promiseId])
|
||||||
.context('NATIVE_APP')
|
.context(CONTEXT_NATIVE_APP)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (skipUiInteractions) {
|
if (skipUiInteractions) {
|
||||||
return;
|
return;
|
||||||
@@ -121,7 +144,7 @@ describe('Camera tests Android.', function () {
|
|||||||
y: Math.round(screenHeight / 4)
|
y: Math.round(screenHeight / 4)
|
||||||
});
|
});
|
||||||
swipeRight
|
swipeRight
|
||||||
.press({x: 10, y: 150})
|
.press({x: 10, y: Math.round(screenHeight / 4)})
|
||||||
.wait(300)
|
.wait(300)
|
||||||
.moveTo({x: Math.round(screenWidth - (screenWidth / 8)), y: 0})
|
.moveTo({x: Math.round(screenWidth - (screenWidth / 8)), y: 0})
|
||||||
.wait(1500)
|
.wait(1500)
|
||||||
@@ -134,19 +157,12 @@ describe('Camera tests Android.', function () {
|
|||||||
.performTouchAction(tapTile);
|
.performTouchAction(tapTile);
|
||||||
}
|
}
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//android.widget.TextView[@text="Gallery"]', 20000)
|
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery");', 20000)
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.fail(function () {
|
.fail(function () {
|
||||||
|
// If the Gallery button is not present, swipe right to reveal the Gallery button!
|
||||||
return driver
|
return driver
|
||||||
.performTouchAction(swipeRight)
|
.performTouchAction(swipeRight)
|
||||||
.waitForElementByXPath('//android.widget.TextView[@text="Gallery"]', 20000)
|
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery");', 20000)
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]');
|
|
||||||
})
|
})
|
||||||
.click()
|
.click()
|
||||||
// always wait before performing touchAction
|
// always wait before performing touchAction
|
||||||
@@ -155,22 +171,37 @@ describe('Camera tests Android.', function () {
|
|||||||
}
|
}
|
||||||
// taking a picture from camera
|
// taking a picture from camera
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]', MINUTE / 2)
|
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*shutter.*")', MINUTE / 2)
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
|
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
|
|
||||||
.click()
|
.click()
|
||||||
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]', MINUTE / 2)
|
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*done.*")', MINUTE / 2)
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
|
.click()
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
|
.then(function () {
|
||||||
.click();
|
if (isAndroid7 && options.allowEdit) {
|
||||||
|
return driver
|
||||||
|
.elementByAndroidUIAutomator('new UiSelector().text("Crop picture");', 20000)
|
||||||
|
.click()
|
||||||
|
.fail(function () {
|
||||||
|
// don't freak out just yet...
|
||||||
|
return driver;
|
||||||
|
})
|
||||||
|
.elementByAndroidUIAutomator('new UiSelector().text("JUST ONCE");', 20000)
|
||||||
|
.click()
|
||||||
|
.fail(function () {
|
||||||
|
// maybe someone's hit that "ALWAYS" button?
|
||||||
|
return driver;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return driver;
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (skipUiInteractions) {
|
if (skipUiInteractions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (options.allowEdit) {
|
if (options.allowEdit) {
|
||||||
|
var saveText = isAndroid7 ? 'SAVE' : 'Save';
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//*[contains(@resource-id,\'save\')]', MINUTE)
|
.waitForElementByAndroidUIAutomator('new UiSelector().text("' + saveText + '")', MINUTE)
|
||||||
.click();
|
.click();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -188,7 +219,7 @@ describe('Camera tests Android.', function () {
|
|||||||
return driver
|
return driver
|
||||||
.context(webviewContext)
|
.context(webviewContext)
|
||||||
.setAsyncScriptTimeout(MINUTE / 2)
|
.setAsyncScriptTimeout(MINUTE / 2)
|
||||||
.executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options])
|
.executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options, isAndroid7])
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
if (shouldLoad) {
|
if (shouldLoad) {
|
||||||
if (result !== 'OK') {
|
if (result !== 'OK') {
|
||||||
@@ -203,16 +234,19 @@ describe('Camera tests Android.', function () {
|
|||||||
// deletes the latest image from the gallery
|
// deletes the latest image from the gallery
|
||||||
function deleteImage() {
|
function deleteImage() {
|
||||||
var holdTile = new wd.TouchAction();
|
var holdTile = new wd.TouchAction();
|
||||||
holdTile.press({x: Math.round(screenWidth / 4), y: Math.round(screenHeight / 5)}).wait(1000).release();
|
holdTile
|
||||||
|
.press({x: Math.round(screenWidth / 4), y: Math.round(screenHeight / 5)})
|
||||||
|
.wait(1000)
|
||||||
|
.release();
|
||||||
return driver
|
return driver
|
||||||
// always wait before performing touchAction
|
// always wait before performing touchAction
|
||||||
.sleep(7000)
|
.sleep(7000)
|
||||||
.performTouchAction(holdTile)
|
.performTouchAction(holdTile)
|
||||||
.elementByXPath('//android.widget.TextView[@text="Delete"]')
|
.elementByAndroidUIAutomator('new UiSelector().text("Delete")')
|
||||||
.then(function (element) {
|
.then(function (element) {
|
||||||
return element
|
return element
|
||||||
.click()
|
.click()
|
||||||
.elementByXPath('//android.widget.Button[@text="OK"]')
|
.elementByAndroidUIAutomator('new UiSelector().text("OK")')
|
||||||
.click();
|
.click();
|
||||||
}, function () {
|
}, function () {
|
||||||
// couldn't find Delete menu item. Possibly there is no image.
|
// couldn't find Delete menu item. Possibly there is no image.
|
||||||
@@ -229,6 +263,40 @@ describe('Camera tests Android.', function () {
|
|||||||
})
|
})
|
||||||
.waitForDeviceReady()
|
.waitForDeviceReady()
|
||||||
.injectLibraries()
|
.injectLibraries()
|
||||||
|
.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, true); })
|
||||||
|
.context(CONTEXT_NATIVE_APP)
|
||||||
|
// case insensitive select, will be handy with Android 7 support
|
||||||
|
.elementByXPath('//android.widget.Button[translate(@text, "alow", "ALOW")="ALLOW"]')
|
||||||
|
.click()
|
||||||
|
.fail(function noAlert() { })
|
||||||
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
|
.sleep(2000)
|
||||||
|
.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;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
// doing it inside a function because otherwise
|
||||||
|
// it would not hook up to the webviewContext var change
|
||||||
|
// in the first methods of this chain
|
||||||
|
return driver.context(webviewContext);
|
||||||
|
})
|
||||||
.deleteFillerImage(fillerImagePath)
|
.deleteFillerImage(fillerImagePath)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
fillerImagePath = null;
|
fillerImagePath = null;
|
||||||
@@ -262,7 +330,7 @@ describe('Camera tests Android.', function () {
|
|||||||
.then(spec);
|
.then(spec);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.fail(saveScreenshotAndFail);
|
.fail(gracefullyFail);
|
||||||
}
|
}
|
||||||
|
|
||||||
// produces a generic spec function which
|
// produces a generic spec function which
|
||||||
@@ -280,40 +348,64 @@ describe('Camera tests Android.', function () {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkSession(done) {
|
function checkSession(done, skipResolutionCheck) {
|
||||||
if (!appiumSessionStarted) {
|
if (!appiumSessionStarted) {
|
||||||
fail('Failed to start a session');
|
fail('Failed to start a session ' + (lastFailureReason ? lastFailureReason : ''));
|
||||||
done();
|
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 camera');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
it('camera.ui.util configuring driver and starting a session', function (done) {
|
function checkCamera(options, pending) {
|
||||||
getDriver()
|
if (!cameraAvailable) {
|
||||||
.then(function () {
|
pending('Skipping because this test requires a functioning camera on the Android device/emulator, and this test suite\'s functional camera test failed on your target environment.');
|
||||||
appiumSessionStarted = true;
|
} else if (isAndroid7 && options.allowEdit) {
|
||||||
}, fail)
|
// TODO: Check if it is fixed some day
|
||||||
.done(done);
|
pending('Skipping because can\'t test with allowEdit=true on Android 7: getting unexpected "Camera cancelled" message.');
|
||||||
}, 10 * MINUTE);
|
} else if (isAndroid7 && (options.sourceType !== cameraConstants.PictureSourceType.CAMERA)) {
|
||||||
|
pending('Skipping because can\'t click on the gallery tile on Android 7.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
it('camera.ui.util determine screen dimensions', function (done) {
|
afterAll(function (done) {
|
||||||
checkSession(done);
|
checkSession(done);
|
||||||
driver
|
driver
|
||||||
.context(webviewContext)
|
.quit()
|
||||||
.execute(function () {
|
.done(done);
|
||||||
return {
|
}, MINUTE);
|
||||||
'width': screen.availWidth,
|
|
||||||
'height': screen.availHeight
|
it('camera.ui.util configuring driver and starting a session', function (done) {
|
||||||
};
|
// retry up to 3 times
|
||||||
}, [])
|
getDriver()
|
||||||
|
.fail(function () {
|
||||||
|
return getDriver()
|
||||||
|
.fail(function () {
|
||||||
|
return getDriver()
|
||||||
|
.fail(fail);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
appiumSessionStarted = true;
|
||||||
|
})
|
||||||
|
.done(done);
|
||||||
|
}, 30 * 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) {
|
.then(function (size) {
|
||||||
screenWidth = Number(size.width);
|
screenWidth = Number(size.width);
|
||||||
screenHeight = Number(size.height);
|
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);
|
.done(done);
|
||||||
}, MINUTE);
|
}, MINUTE);
|
||||||
@@ -340,15 +432,16 @@ describe('Camera tests Android.', function () {
|
|||||||
describe('Specs.', function () {
|
describe('Specs.', function () {
|
||||||
// getPicture() with saveToPhotoLibrary = true
|
// getPicture() with saveToPhotoLibrary = true
|
||||||
it('camera.ui.spec.1 Saving a picture to the photo library', function (done) {
|
it('camera.ui.spec.1 Saving a picture to the photo library', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
checkCamera(pending);
|
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
saveToPhotoAlbum: true
|
saveToPhotoAlbum: true
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
|
||||||
|
var spec = generateSpec(opts);
|
||||||
tryRunSpec(spec)
|
tryRunSpec(spec)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
isTestPictureSaved = true;
|
isTestPictureSaved = true;
|
||||||
@@ -366,14 +459,12 @@ describe('Camera tests Android.', function () {
|
|||||||
.then(function () {
|
.then(function () {
|
||||||
return getPicture(options, true);
|
return getPicture(options, true);
|
||||||
})
|
})
|
||||||
.context('NATIVE_APP')
|
.context(CONTEXT_NATIVE_APP)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
// try to find "Gallery" menu item
|
// try to find "Gallery" menu item
|
||||||
// if there's none, the gallery should be already opened
|
// if there's none, the gallery should be already opened
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//android.widget.TextView[@text="Gallery"]', 20000)
|
.waitForElementByAndroidUIAutomator('new UiSelector().text("Gallery")', 20000)
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
|
||||||
.then(function (element) {
|
.then(function (element) {
|
||||||
return element.click();
|
return element.click();
|
||||||
}, function () {
|
}, function () {
|
||||||
@@ -382,15 +473,16 @@ describe('Camera tests Android.', function () {
|
|||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
// if the gallery is opened on the videos page,
|
// if the gallery is opened on the videos page,
|
||||||
// there should be a "Choose video" caption
|
// there should be a "Choose video" or "Select video" caption
|
||||||
|
var videoSelector = isAndroid7 ? 'new UiSelector().text("Select video")' : 'new UiSelector().text("Choose video")';
|
||||||
return driver
|
return driver
|
||||||
.elementByXPath('//*[@text="Choose video"]')
|
.elementByAndroidUIAutomator(videoSelector)
|
||||||
.fail(function () {
|
.fail(function () {
|
||||||
throw 'Couldn\'t find "Choose video" element.';
|
throw 'Couldn\'t find a "Choose/select video" element.';
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.deviceKeyEvent(BACK_BUTTON)
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
.elementByAndroidUIAutomator('new UiSelector().text("Gallery")')
|
||||||
.deviceKeyEvent(BACK_BUTTON)
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
.finally(function () {
|
.finally(function () {
|
||||||
return driver
|
return driver
|
||||||
@@ -424,23 +516,21 @@ describe('Camera tests Android.', function () {
|
|||||||
// getPicture(), then dismiss
|
// getPicture(), then dismiss
|
||||||
// wait for the error callback to be called
|
// wait for the error callback to be called
|
||||||
it('camera.ui.spec.3 Dismissing the camera', function (done) {
|
it('camera.ui.spec.3 Dismissing the camera', function (done) {
|
||||||
|
var options = {
|
||||||
|
quality: 50,
|
||||||
|
allowEdit: true,
|
||||||
|
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
|
destinationType: cameraConstants.DestinationType.FILE_URI
|
||||||
|
};
|
||||||
checkSession(done);
|
checkSession(done);
|
||||||
checkCamera(pending);
|
checkCamera(options, pending);
|
||||||
var spec = function () {
|
var spec = function () {
|
||||||
var options = {
|
|
||||||
quality: 50,
|
|
||||||
allowEdit: true,
|
|
||||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
|
||||||
destinationType: cameraConstants.DestinationType.FILE_URI
|
|
||||||
};
|
|
||||||
return driver
|
return driver
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return getPicture(options, true);
|
return getPicture(options, true);
|
||||||
})
|
})
|
||||||
.context("NATIVE_APP")
|
.context(CONTEXT_NATIVE_APP)
|
||||||
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]', MINUTE / 2)
|
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*cancel.*")', MINUTE / 2)
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]')
|
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'cancel\')]')
|
|
||||||
.click()
|
.click()
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return checkPicture(false);
|
return checkPicture(false);
|
||||||
@@ -453,32 +543,36 @@ describe('Camera tests Android.', function () {
|
|||||||
// getPicture(), then take picture but dismiss the edit
|
// getPicture(), then take picture but dismiss the edit
|
||||||
// wait for the error callback to be called
|
// wait for the error callback to be called
|
||||||
it('camera.ui.spec.4 Dismissing the edit', function (done) {
|
it('camera.ui.spec.4 Dismissing the edit', function (done) {
|
||||||
|
var options = {
|
||||||
|
quality: 50,
|
||||||
|
allowEdit: true,
|
||||||
|
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
|
destinationType: cameraConstants.DestinationType.FILE_URI
|
||||||
|
};
|
||||||
checkSession(done);
|
checkSession(done);
|
||||||
checkCamera(pending);
|
checkCamera(options, pending);
|
||||||
var spec = function () {
|
var spec = function () {
|
||||||
var options = {
|
|
||||||
quality: 50,
|
|
||||||
allowEdit: true,
|
|
||||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
|
||||||
destinationType: cameraConstants.DestinationType.FILE_URI
|
|
||||||
};
|
|
||||||
return driver
|
return driver
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return getPicture(options, true);
|
return getPicture(options, true);
|
||||||
})
|
})
|
||||||
.context('NATIVE_APP')
|
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*shutter.*")', MINUTE / 2)
|
||||||
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]', MINUTE / 2)
|
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
|
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'shutter\')]')
|
|
||||||
.click()
|
.click()
|
||||||
.waitForElementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]', MINUTE / 2)
|
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*done.*")', MINUTE / 2)
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
|
|
||||||
.elementByXPath('//android.widget.ImageView[contains(@resource-id,\'done\')]')
|
|
||||||
.click()
|
|
||||||
.waitForElementByXPath('//*[contains(@resource-id,\'discard\')]', MINUTE / 2)
|
|
||||||
.elementByXPath('//*[contains(@resource-id,\'discard\')]')
|
|
||||||
.elementByXPath('//*[contains(@resource-id,\'discard\')]')
|
|
||||||
.click()
|
.click()
|
||||||
|
.then(function () {
|
||||||
|
if (isAndroid7 && options.allowEdit) {
|
||||||
|
return driver
|
||||||
|
.waitForElementByAndroidUIAutomator('new UiSelector().text("Crop picture");', 20000)
|
||||||
|
.click()
|
||||||
|
.waitForElementByAndroidUIAutomator('new UiSelector().text("JUST ONCE");', 20000)
|
||||||
|
.click()
|
||||||
|
.deviceKeyEvent(BACK_BUTTON);
|
||||||
|
}
|
||||||
|
return driver
|
||||||
|
.waitForElementByAndroidUIAutomator('new UiSelector().resourceIdMatches(".*discard.*")', MINUTE / 2)
|
||||||
|
.click();
|
||||||
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return checkPicture(false);
|
return checkPicture(false);
|
||||||
});
|
});
|
||||||
@@ -488,53 +582,56 @@ describe('Camera tests Android.', function () {
|
|||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.5 Verifying target image size, sourceType=CAMERA', function (done) {
|
it('camera.ui.spec.5 Verifying target image size, sourceType=CAMERA', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
checkCamera(pending);
|
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
saveToPhotoAlbum: false,
|
saveToPhotoAlbum: false,
|
||||||
targetWidth: 210,
|
targetWidth: 210,
|
||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
var spec = generateSpec(opts);
|
||||||
|
|
||||||
tryRunSpec(spec).done(done);
|
tryRunSpec(spec).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.6 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
|
it('camera.ui.spec.6 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||||
saveToPhotoAlbum: false,
|
saveToPhotoAlbum: false,
|
||||||
targetWidth: 210,
|
targetWidth: 210,
|
||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
var spec = generateSpec(opts);
|
||||||
|
|
||||||
tryRunSpec(spec).done(done);
|
tryRunSpec(spec).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.7 Verifying target image size, sourceType=CAMERA, DestinationType=NATIVE_URI', function (done) {
|
it('camera.ui.spec.7 Verifying target image size, sourceType=CAMERA, DestinationType=NATIVE_URI', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
checkCamera(pending);
|
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: true,
|
||||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
||||||
saveToPhotoAlbum: false,
|
saveToPhotoAlbum: false,
|
||||||
targetWidth: 210,
|
targetWidth: 210,
|
||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
var spec = generateSpec(opts);
|
||||||
|
|
||||||
tryRunSpec(spec).done(done);
|
tryRunSpec(spec).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, DestinationType=NATIVE_URI', function (done) {
|
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, DestinationType=NATIVE_URI', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||||
@@ -542,30 +639,33 @@ describe('Camera tests Android.', function () {
|
|||||||
saveToPhotoAlbum: false,
|
saveToPhotoAlbum: false,
|
||||||
targetWidth: 210,
|
targetWidth: 210,
|
||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
|
||||||
|
var spec = generateSpec(opts);
|
||||||
tryRunSpec(spec).done(done);
|
tryRunSpec(spec).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, DestinationType=NATIVE_URI, quality=100', function (done) {
|
it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, DestinationType=NATIVE_URI, quality=100', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
checkCamera(pending);
|
quality: 50,
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 100,
|
|
||||||
allowEdit: true,
|
allowEdit: true,
|
||||||
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
destinationType: cameraConstants.DestinationType.NATIVE_URI,
|
||||||
saveToPhotoAlbum: false,
|
saveToPhotoAlbum: false,
|
||||||
targetWidth: 305,
|
targetWidth: 305,
|
||||||
targetHeight: 305
|
targetHeight: 305
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
var spec = generateSpec(opts);
|
||||||
|
|
||||||
tryRunSpec(spec).done(done);
|
tryRunSpec(spec).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.10 Verifying target image size, sourceType=PHOTOLIBRARY, DestinationType=NATIVE_URI, quality=100', function (done) {
|
it('camera.ui.spec.10 Verifying target image size, sourceType=PHOTOLIBRARY, DestinationType=NATIVE_URI, quality=100', function (done) {
|
||||||
checkSession(done);
|
var opts = {
|
||||||
var spec = generateSpec({
|
|
||||||
quality: 100,
|
quality: 100,
|
||||||
allowEdit: true,
|
allowEdit: true,
|
||||||
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||||
@@ -573,7 +673,10 @@ describe('Camera tests Android.', function () {
|
|||||||
saveToPhotoAlbum: false,
|
saveToPhotoAlbum: false,
|
||||||
targetWidth: 305,
|
targetWidth: 305,
|
||||||
targetHeight: 305
|
targetHeight: 305
|
||||||
});
|
};
|
||||||
|
checkSession(done);
|
||||||
|
checkCamera(opts, pending);
|
||||||
|
var spec = generateSpec(opts);
|
||||||
|
|
||||||
tryRunSpec(spec).done(done);
|
tryRunSpec(spec).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
@@ -582,15 +685,17 @@ describe('Camera tests Android.', function () {
|
|||||||
generateOptions().forEach(function (spec) {
|
generateOptions().forEach(function (spec) {
|
||||||
it('camera.ui.spec.11.' + spec.id + ' Combining options. ' + spec.description, function (done) {
|
it('camera.ui.spec.11.' + spec.id + ' Combining options. ' + spec.description, function (done) {
|
||||||
checkSession(done);
|
checkSession(done);
|
||||||
if (spec.options.sourceType == cameraConstants.PictureSourceType.CAMERA) {
|
checkCamera(spec.options, pending);
|
||||||
checkCamera(pending);
|
|
||||||
}
|
|
||||||
var s = generateSpec(spec.options);
|
var s = generateSpec(spec.options);
|
||||||
tryRunSpec(s).done(done);
|
tryRunSpec(s).done(done);
|
||||||
}, 10 * MINUTE);
|
}, 10 * MINUTE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('camera.ui.util Delete filler picture from device library', function (done) {
|
it('camera.ui.util Delete filler picture from device library', function (done) {
|
||||||
|
if (isAndroid7 || global.USE_SAUCE) {
|
||||||
|
pending();
|
||||||
|
}
|
||||||
driver
|
driver
|
||||||
.context(webviewContext)
|
.context(webviewContext)
|
||||||
.deleteFillerImage(fillerImagePath)
|
.deleteFillerImage(fillerImagePath)
|
||||||
@@ -598,6 +703,9 @@ describe('Camera tests Android.', function () {
|
|||||||
}, MINUTE);
|
}, MINUTE);
|
||||||
|
|
||||||
it('camera.ui.util Delete taken picture from device library', function (done) {
|
it('camera.ui.util Delete taken picture from device library', function (done) {
|
||||||
|
if (isAndroid7 || global.USE_SAUCE) {
|
||||||
|
pending();
|
||||||
|
}
|
||||||
checkSession(done);
|
checkSession(done);
|
||||||
if (!isTestPictureSaved) {
|
if (!isTestPictureSaved) {
|
||||||
// couldn't save test picture earlier, so nothing to delete here
|
// couldn't save test picture earlier, so nothing to delete here
|
||||||
@@ -607,7 +715,7 @@ describe('Camera tests Android.', function () {
|
|||||||
// delete exactly one latest picture
|
// delete exactly one latest picture
|
||||||
// this should be the picture we've taken in the first spec
|
// this should be the picture we've taken in the first spec
|
||||||
driver
|
driver
|
||||||
.context('NATIVE_APP')
|
.context(CONTEXT_NATIVE_APP)
|
||||||
.deviceKeyEvent(BACK_BUTTON)
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
.sleep(1000)
|
.sleep(1000)
|
||||||
.deviceKeyEvent(BACK_BUTTON)
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
@@ -615,9 +723,18 @@ describe('Camera tests Android.', function () {
|
|||||||
.deviceKeyEvent(BACK_BUTTON)
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
.elementById('Apps')
|
.elementById('Apps')
|
||||||
.click()
|
.click()
|
||||||
.elementByXPath('//android.widget.TextView[@text="Gallery"]')
|
.then(function () {
|
||||||
|
return driver
|
||||||
|
.elementByXPath('//android.widget.Button[@text="OK"]')
|
||||||
|
.click()
|
||||||
|
.fail(function () {
|
||||||
|
// no cling is all right
|
||||||
|
// it is not a brand new emulator, then
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.elementByAndroidUIAutomator('new UiSelector().text("Gallery")')
|
||||||
.click()
|
.click()
|
||||||
.elementByXPath('//android.widget.TextView[contains(@text,"Pictures")]')
|
.elementByAndroidUIAutomator('new UiSelector().textContains("Pictures")')
|
||||||
.click()
|
.click()
|
||||||
.then(deleteImage)
|
.then(deleteImage)
|
||||||
.deviceKeyEvent(BACK_BUTTON)
|
.deviceKeyEvent(BACK_BUTTON)
|
||||||
@@ -630,10 +747,5 @@ describe('Camera tests Android.', function () {
|
|||||||
}, 3 * MINUTE);
|
}, 3 * MINUTE);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('camera.ui.util Destroy the session', function (done) {
|
|
||||||
checkSession(done);
|
|
||||||
driver
|
|
||||||
.quit()
|
|
||||||
.done(done);
|
|
||||||
}, 5 * MINUTE);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
/*jshint node: true */
|
|
||||||
/* global Q, resolveLocalFileSystemURL, Camera, cordova */
|
/* global Q, resolveLocalFileSystemURL, Camera, cordova */
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
@@ -114,7 +113,7 @@ module.exports.getPicture = function (opts, pid) {
|
|||||||
// calls a callback with 'ERROR: <error message>' if something is wrong
|
// calls a callback with 'ERROR: <error message>' if something is wrong
|
||||||
// note that this function is executed in the context of tested app
|
// note that this function is executed in the context of tested app
|
||||||
// and not in the context of tests
|
// and not in the context of tests
|
||||||
module.exports.checkPicture = function (pid, options, cb) {
|
module.exports.checkPicture = function (pid, options, skipContentCheck, cb) {
|
||||||
var isIos = cordova.platformId === "ios";
|
var isIos = cordova.platformId === "ios";
|
||||||
var isAndroid = cordova.platformId === "android";
|
var isAndroid = cordova.platformId === "android";
|
||||||
// skip image type check if it's unmodified on Android:
|
// skip image type check if it's unmodified on Android:
|
||||||
@@ -169,6 +168,7 @@ module.exports.checkPicture = function (pid, options, cb) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (result.indexOf('file:') === 0 ||
|
if (result.indexOf('file:') === 0 ||
|
||||||
result.indexOf('content:') === 0 ||
|
result.indexOf('content:') === 0 ||
|
||||||
@@ -178,12 +178,18 @@ module.exports.checkPicture = function (pid, options, cb) {
|
|||||||
errorCallback('Cannot read file. Please install cordova-plugin-file to fix this.');
|
errorCallback('Cannot read file. Please install cordova-plugin-file to fix this.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (skipContentCheck) {
|
||||||
|
cb('OK');
|
||||||
|
return;
|
||||||
|
}
|
||||||
resolveLocalFileSystemURL(result, function (entry) {
|
resolveLocalFileSystemURL(result, function (entry) {
|
||||||
if (skipFileTypeCheck) {
|
if (skipFileTypeCheck) {
|
||||||
displayFile(entry);
|
displayFile(entry);
|
||||||
} else {
|
} else {
|
||||||
verifyFile(entry);
|
verifyFile(entry);
|
||||||
}
|
}
|
||||||
|
}, function (err) {
|
||||||
|
errorCallback(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
displayImage(result);
|
displayImage(result);
|
||||||
|
|||||||
+178
-83
@@ -1,5 +1,3 @@
|
|||||||
/*jshint node: true, jasmine: true */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
@@ -21,10 +19,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// these tests are meant to be executed by Cordova Medic Appium runner
|
// these tests are meant to be executed by Cordova Paramedic test runner
|
||||||
// you can find it here: https://github.com/apache/cordova-medic/
|
// 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
|
// it is not necessary to do a full CI setup to run these tests
|
||||||
// just run "node cordova-medic/medic/medic.js appium --platform android --plugins cordova-plugin-camera"
|
// just run "node cordova-paramedic/main.js --platform ios --plugin cordova-plugin-camera"
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
@@ -37,6 +35,7 @@ var cameraHelper = require('../helpers/cameraHelper');
|
|||||||
var MINUTE = 60 * 1000;
|
var MINUTE = 60 * 1000;
|
||||||
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW_1';
|
var DEFAULT_WEBVIEW_CONTEXT = 'WEBVIEW_1';
|
||||||
var PROMISE_PREFIX = 'appium_camera_promise_';
|
var PROMISE_PREFIX = 'appium_camera_promise_';
|
||||||
|
var CONTEXT_NATIVE_APP = 'NATIVE_APP';
|
||||||
|
|
||||||
describe('Camera tests iOS.', function () {
|
describe('Camera tests iOS.', function () {
|
||||||
var driver;
|
var driver;
|
||||||
@@ -45,6 +44,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
var promiseCount = 0;
|
var promiseCount = 0;
|
||||||
// going to set this to false if session is created successfully
|
// going to set this to false if session is created successfully
|
||||||
var failedToStart = true;
|
var failedToStart = true;
|
||||||
|
// points out which UI automation to use
|
||||||
|
var isXCUI = false;
|
||||||
|
// spec counter to restart the session
|
||||||
|
var specsRun = 0;
|
||||||
|
|
||||||
function getNextPromiseId() {
|
function getNextPromiseId() {
|
||||||
promiseCount += 1;
|
promiseCount += 1;
|
||||||
@@ -55,10 +58,9 @@ describe('Camera tests iOS.', function () {
|
|||||||
return PROMISE_PREFIX + promiseCount;
|
return PROMISE_PREFIX + promiseCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveScreenshotAndFail(error) {
|
function gracefullyFail(error) {
|
||||||
fail(error);
|
fail(error);
|
||||||
return screenshotHelper
|
return driver
|
||||||
.saveScreenshot(driver)
|
|
||||||
.quit()
|
.quit()
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return getDriver();
|
return getDriver();
|
||||||
@@ -77,65 +79,104 @@ describe('Camera tests iOS.', function () {
|
|||||||
return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions);
|
return cameraHelper.generateSpecs(sourceTypes, destinationTypes, encodingTypes, allowEditOptions, correctOrientationOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
function usePicture() {
|
function usePicture(allowEdit) {
|
||||||
return driver
|
return driver
|
||||||
.elementByXPath('//*[@label="Use"]')
|
.sleep(10)
|
||||||
.click()
|
.then(function () {
|
||||||
.fail(function () {
|
if (isXCUI) {
|
||||||
// For some reason "Choose" element is not clickable by standard Appium methods
|
return driver.waitForElementByAccessibilityId('Choose', MINUTE / 3).click();
|
||||||
return wdHelper.tapElementByXPath('//UIAButton[@label="Choose"]', driver);
|
} else {
|
||||||
|
if (allowEdit) {
|
||||||
|
return wdHelper.tapElementByXPath('//UIAButton[@label="Choose"]', driver);
|
||||||
|
}
|
||||||
|
return driver.elementByXPath('//*[@label="Use"]').click();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
function getPicture(options, cancelCamera, skipUiInteractions) {
|
||||||
var promiseId = getNextPromiseId();
|
var promiseId = getNextPromiseId();
|
||||||
if (!options) {
|
if (!options) {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
// assign defaults
|
||||||
|
if (!options.hasOwnProperty('allowEdit')) {
|
||||||
|
options.allowEdit = true;
|
||||||
|
}
|
||||||
|
if (!options.hasOwnProperty('destinationType')) {
|
||||||
|
options.destinationType = cameraConstants.DestinationType.FILE_URI;
|
||||||
|
}
|
||||||
|
if (!options.hasOwnProperty('sourceType')) {
|
||||||
|
options.destinationType = cameraConstants.PictureSourceType.CAMERA;
|
||||||
|
}
|
||||||
|
|
||||||
return driver
|
return driver
|
||||||
.context(webviewContext)
|
.context(webviewContext)
|
||||||
.execute(cameraHelper.getPicture, [options, promiseId])
|
.execute(cameraHelper.getPicture, [options, promiseId])
|
||||||
.context('NATIVE_APP')
|
.context(CONTEXT_NATIVE_APP)
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (skipUiInteractions) {
|
if (skipUiInteractions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
|
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.PHOTOLIBRARY) {
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//*[@label="Camera Roll"]', MINUTE / 2)
|
.waitForElementByAccessibilityId('Camera Roll', MINUTE / 2)
|
||||||
.click()
|
|
||||||
.elementByXPath('//UIACollectionCell')
|
|
||||||
.click()
|
.click()
|
||||||
|
.then(function () {
|
||||||
|
return clickPhoto();
|
||||||
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (!options.allowEdit) {
|
if (!options.allowEdit) {
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
return usePicture();
|
return usePicture(options.allowEdit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) {
|
if (options.hasOwnProperty('sourceType') && options.sourceType === cameraConstants.PictureSourceType.SAVEDPHOTOALBUM) {
|
||||||
return driver
|
return clickPhoto()
|
||||||
.waitForElementByXPath('//UIACollectionCell', MINUTE / 2)
|
|
||||||
.click()
|
|
||||||
.then(function () {
|
.then(function () {
|
||||||
if (!options.allowEdit) {
|
if (!options.allowEdit) {
|
||||||
return driver;
|
return driver;
|
||||||
}
|
}
|
||||||
return usePicture();
|
return usePicture(options.allowEdit);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (cancelCamera) {
|
if (cancelCamera) {
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//*[@label="Cancel"]', MINUTE / 2)
|
.waitForElementByAccessibilityId('Cancel', MINUTE / 2)
|
||||||
.elementByXPath('//*[@label="Cancel"]')
|
|
||||||
.elementByXPath('//*[@label="Cancel"]')
|
|
||||||
.click();
|
.click();
|
||||||
}
|
}
|
||||||
return driver
|
return driver
|
||||||
.waitForElementByXPath('//*[@label="Take Picture"]', MINUTE / 2)
|
.waitForElementByAccessibilityId('Take Picture', MINUTE / 2)
|
||||||
.click()
|
.click()
|
||||||
.waitForElementByXPath('//*[@label="Use Photo"]', MINUTE / 2)
|
.waitForElementByAccessibilityId('Use Photo', MINUTE / 2)
|
||||||
.click();
|
.click();
|
||||||
})
|
})
|
||||||
.fail(fail);
|
.fail(fail);
|
||||||
@@ -150,7 +191,7 @@ describe('Camera tests iOS.', function () {
|
|||||||
return driver
|
return driver
|
||||||
.context(webviewContext)
|
.context(webviewContext)
|
||||||
.setAsyncScriptTimeout(MINUTE / 2)
|
.setAsyncScriptTimeout(MINUTE / 2)
|
||||||
.executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options])
|
.executeAsync(cameraHelper.checkPicture, [getCurrentPromiseId(), options, false])
|
||||||
.then(function (result) {
|
.then(function (result) {
|
||||||
if (shouldLoad) {
|
if (shouldLoad) {
|
||||||
if (result !== 'OK') {
|
if (result !== 'OK') {
|
||||||
@@ -164,7 +205,12 @@ describe('Camera tests iOS.', function () {
|
|||||||
|
|
||||||
// takes a picture with the specified options
|
// takes a picture with the specified options
|
||||||
// and then verifies it
|
// and then verifies it
|
||||||
function runSpec(options) {
|
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
|
return driver
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return getPicture(options);
|
return getPicture(options);
|
||||||
@@ -172,10 +218,11 @@ describe('Camera tests iOS.', function () {
|
|||||||
.then(function () {
|
.then(function () {
|
||||||
return checkPicture(true, options);
|
return checkPicture(true, options);
|
||||||
})
|
})
|
||||||
.fail(saveScreenshotAndFail);
|
.fail(gracefullyFail);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDriver() {
|
function getDriver() {
|
||||||
|
failedToStart = true;
|
||||||
driver = wdHelper.getDriver('iOS');
|
driver = wdHelper.getDriver('iOS');
|
||||||
return wdHelper.getWebviewContext(driver)
|
return wdHelper.getWebviewContext(driver)
|
||||||
.then(function(context) {
|
.then(function(context) {
|
||||||
@@ -187,6 +234,42 @@ describe('Camera tests iOS.', function () {
|
|||||||
})
|
})
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return wdHelper.injectLibraries(driver);
|
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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,28 +281,62 @@ describe('Camera tests iOS.', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
it('camera.ui.util configure driver and start a session', function (done) {
|
it('camera.ui.util configure driver and start a session', function (done) {
|
||||||
|
// retry up to 3 times
|
||||||
getDriver()
|
getDriver()
|
||||||
.then(function () {
|
.fail(function () {
|
||||||
failedToStart = false;
|
return getDriver()
|
||||||
}, fail)
|
.fail(function () {
|
||||||
|
return getDriver()
|
||||||
|
.fail(fail);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.fail(fail)
|
||||||
.done(done);
|
.done(done);
|
||||||
}, 10 * MINUTE);
|
}, 30 * MINUTE);
|
||||||
|
|
||||||
describe('Specs.', function () {
|
describe('Specs.', function () {
|
||||||
|
afterEach(function (done) {
|
||||||
|
if (specsRun >= 19) {
|
||||||
|
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()
|
||||||
|
.fail(function () {
|
||||||
|
return getDriver()
|
||||||
|
.fail(function () {
|
||||||
|
return getDriver()
|
||||||
|
.fail(fail);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.done(done);
|
||||||
|
} else {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}, 30 * MINUTE);
|
||||||
|
|
||||||
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
|
// getPicture() with mediaType: VIDEO, sourceType: PHOTOLIBRARY
|
||||||
it('camera.ui.spec.1 Selecting only videos', function (done) {
|
it('camera.ui.spec.1 Selecting only videos', function (done) {
|
||||||
checkSession(done);
|
checkSession(done);
|
||||||
|
specsRun += 1;
|
||||||
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
var options = { sourceType: cameraConstants.PictureSourceType.PHOTOLIBRARY,
|
||||||
mediaType: cameraConstants.MediaType.VIDEO };
|
mediaType: cameraConstants.MediaType.VIDEO };
|
||||||
driver
|
driver
|
||||||
// skip ui unteractions
|
// skip ui unteractions
|
||||||
.then(function () { return getPicture(options, false, true); })
|
.then(function () { return getPicture(options, false, true); })
|
||||||
.waitForElementByXPath('//*[contains(@label,"Videos")]', MINUTE / 2)
|
.waitForElementByXPath('//*[contains(@label,"Videos")]', MINUTE / 2)
|
||||||
.elementByXPath('//*[@label="Cancel"]')
|
.elementByAccessibilityId('Cancel')
|
||||||
.click()
|
.click()
|
||||||
.fail(saveScreenshotAndFail)
|
.fail(gracefullyFail)
|
||||||
.done(done);
|
.done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
// getPicture(), then dismiss
|
// getPicture(), then dismiss
|
||||||
// wait for the error callback to be called
|
// wait for the error callback to be called
|
||||||
@@ -228,6 +345,7 @@ describe('Camera tests iOS.', function () {
|
|||||||
if (!isDevice) {
|
if (!isDevice) {
|
||||||
pending('Camera is not available on iOS simulator');
|
pending('Camera is not available on iOS simulator');
|
||||||
}
|
}
|
||||||
|
specsRun += 1;
|
||||||
var options = { sourceType: cameraConstants.PictureSourceType.CAMERA,
|
var options = { sourceType: cameraConstants.PictureSourceType.CAMERA,
|
||||||
saveToPhotoAlbum: false };
|
saveToPhotoAlbum: false };
|
||||||
driver
|
driver
|
||||||
@@ -237,15 +355,11 @@ describe('Camera tests iOS.', function () {
|
|||||||
.then(function () {
|
.then(function () {
|
||||||
return checkPicture(false);
|
return checkPicture(false);
|
||||||
})
|
})
|
||||||
.fail(saveScreenshotAndFail)
|
.fail(gracefullyFail)
|
||||||
.done(done);
|
.done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.3 Verifying target image size, sourceType=CAMERA', function (done) {
|
it('camera.ui.spec.3 Verifying target image size, sourceType=CAMERA', function (done) {
|
||||||
checkSession(done);
|
|
||||||
if (!isDevice) {
|
|
||||||
pending('Camera is not available on iOS simulator');
|
|
||||||
}
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -255,11 +369,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.4 Verifying target image size, sourceType=SAVEDPHOTOALBUM', function (done) {
|
it('camera.ui.spec.4 Verifying target image size, sourceType=SAVEDPHOTOALBUM', function (done) {
|
||||||
checkSession(done);
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -269,11 +382,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.5 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
|
it('camera.ui.spec.5 Verifying target image size, sourceType=PHOTOLIBRARY', function (done) {
|
||||||
checkSession(done);
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -283,17 +395,13 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.6 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL', function (done) {
|
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
|
// 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');
|
pending('Cannot prevent iOS from saving the picture to photo library');
|
||||||
|
|
||||||
checkSession(done);
|
|
||||||
if (!isDevice) {
|
|
||||||
pending('Camera is not available on iOS simulator');
|
|
||||||
}
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -304,11 +412,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.7 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL', function (done) {
|
it('camera.ui.spec.7 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL', function (done) {
|
||||||
checkSession(done);
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -319,11 +426,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL', function (done) {
|
it('camera.ui.spec.8 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL', function (done) {
|
||||||
checkSession(done);
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -334,17 +440,13 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 210
|
targetHeight: 210
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.9 Verifying target image size, sourceType=CAMERA, destinationType=FILE_URL, quality=100', function (done) {
|
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
|
// 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');
|
pending('Cannot prevent iOS from saving the picture to photo library');
|
||||||
|
|
||||||
checkSession(done);
|
|
||||||
if (!isDevice) {
|
|
||||||
pending('Camera is not available on iOS simulator');
|
|
||||||
}
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 100,
|
quality: 100,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -354,11 +456,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetWidth: 305,
|
targetWidth: 305,
|
||||||
targetHeight: 305
|
targetHeight: 305
|
||||||
};
|
};
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.10 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL, quality=100', function (done) {
|
it('camera.ui.spec.10 Verifying target image size, sourceType=SAVEDPHOTOALBUM, destinationType=FILE_URL, quality=100', function (done) {
|
||||||
checkSession(done);
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 100,
|
quality: 100,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -369,11 +470,10 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 305
|
targetHeight: 305
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
it('camera.ui.spec.11 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL, quality=100', function (done) {
|
it('camera.ui.spec.11 Verifying target image size, sourceType=PHOTOLIBRARY, destinationType=FILE_URL, quality=100', function (done) {
|
||||||
checkSession(done);
|
|
||||||
var options = {
|
var options = {
|
||||||
quality: 100,
|
quality: 100,
|
||||||
allowEdit: false,
|
allowEdit: false,
|
||||||
@@ -384,17 +484,12 @@ describe('Camera tests iOS.', function () {
|
|||||||
targetHeight: 305
|
targetHeight: 305
|
||||||
};
|
};
|
||||||
|
|
||||||
runSpec(options).done(done);
|
runSpec(options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
|
|
||||||
// combine various options for getPicture()
|
// combine various options for getPicture()
|
||||||
generateOptions().forEach(function (spec) {
|
generateOptions().forEach(function (spec) {
|
||||||
it('camera.ui.spec.12.' + spec.id + ' Combining options. ' + spec.description, function (done) {
|
it('camera.ui.spec.12.' + spec.id + ' Combining options. ' + spec.description, function (done) {
|
||||||
checkSession(done);
|
|
||||||
if (!isDevice && spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA) {
|
|
||||||
pending('Camera is not available on iOS simulator');
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove this check if you don't mind the tests leaving a photo saved on device
|
// remove this check if you don't mind the tests leaving a photo saved on device
|
||||||
if (spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA &&
|
if (spec.options.sourceType === cameraConstants.PictureSourceType.CAMERA &&
|
||||||
spec.options.destinationType === cameraConstants.DestinationType.NATIVE_URI) {
|
spec.options.destinationType === cameraConstants.DestinationType.NATIVE_URI) {
|
||||||
@@ -402,8 +497,8 @@ describe('Camera tests iOS.', function () {
|
|||||||
'For more info, see iOS quirks here: https://github.com/apache/cordova-plugin-camera#ios-quirks-1');
|
'For more info, see iOS quirks here: https://github.com/apache/cordova-plugin-camera#ios-quirks-1');
|
||||||
}
|
}
|
||||||
|
|
||||||
runSpec(spec.options).done(done);
|
runSpec(spec.options, done, pending).done(done);
|
||||||
}, 3 * MINUTE);
|
}, 7 * MINUTE);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
+32
-15
@@ -4,37 +4,54 @@ description: Take pictures with the device camera.
|
|||||||
---
|
---
|
||||||
{{>cdv-license~}}
|
{{>cdv-license~}}
|
||||||
|
|
||||||
|Android|iOS| Windows 8.1 Store | Windows 8.1 Phone | Windows 10 Store | Travis CI |
|
|AppVeyor|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://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-camera)|[](https://travis-ci.org/apache/cordova-plugin-camera)|
|
||||||
|
|
||||||
# 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
|
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.
|
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' }}
|
{{>cdv-header device-ready-warning-obj='navigator.camera' npmName='cordova-plugin-camera' cprName='org.apache.cordova.camera' pluginName='cordova-plugin-camera' repoUrl='https://github.com/apache/cordova-plugin-camera' }}
|
||||||
|
|
||||||
|
|
||||||
### iOS Quirks
|
### iOS Quirks
|
||||||
|
|
||||||
Since iOS 10 it's mandatory to add a `NSCameraUsageDescription` and `NSPhotoLibraryUsageDescriptionentry` in the info.plist.
|
Since iOS 10 it's mandatory to provide an usage description in the `info.plist` if trying to access privacy-sensitive data. When the system prompts the user to allow access, this usage description string will displayed as part of the permission dialog box, but if you didn't provide the usage description, the app will crash before showing the dialog. Also, Apple will reject apps that access private data but don't provide an usage description.
|
||||||
|
|
||||||
- `NSCameraUsageDescription` describes the reason that the app accesses the user’s camera.
|
This plugins requires the following usage descriptions:
|
||||||
- `NSPhotoLibraryUsageDescriptionentry` 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.
|
- `NSCameraUsageDescription` specifies the reason for your app to access the device's camera.
|
||||||
|
- `NSPhotoLibraryUsageDescription` specifies the reason for your app to access the user's photo library.
|
||||||
|
- `NSLocationWhenInUseUsageDescription` specifies the reason for your app to access the user's location information while your app is in use. (Set it if you have `CameraUsesGeolocation` preference set to `true`)
|
||||||
|
- `NSPhotoLibraryAddUsageDescription` specifies the reason for your app to get write-only access to the user's photo library
|
||||||
|
|
||||||
To add this entry you can pass the following variables on plugin install.
|
To add these entries into the `info.plist`, you can use the `edit-config` tag in the `config.xml` like this:
|
||||||
|
|
||||||
- `CAMERA_USAGE_DESCRIPTION` for `NSCameraUsageDescription`
|
```
|
||||||
- `PHOTOLIBRARY_USAGE_DESCRIPTION` for `NSPhotoLibraryUsageDescriptionentry`
|
<edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
-
|
<string>need camera access to take pictures</string>
|
||||||
Example:
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
cordova plugin add cordova-plugin-camera --variable CAMERA_USAGE_DESCRIPTION="your usage message"
|
```
|
||||||
|
<edit-config target="NSPhotoLibraryUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
|
<string>need photo library access to get pictures from there</string>
|
||||||
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
If you don't pass the variable, the plugin will add an empty string as value.
|
```
|
||||||
|
<edit-config target="NSLocationWhenInUseUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
|
<string>need location access to find things nearby</string>
|
||||||
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
<edit-config target="NSPhotoLibraryAddUsageDescription" file="*-Info.plist" mode="merge">
|
||||||
|
<string>need photo library access to save pictures there</string>
|
||||||
|
</edit-config>
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+22
-21
@@ -1,62 +1,63 @@
|
|||||||
{
|
{
|
||||||
"name": "cordova-plugin-camera",
|
"name": "cordova-plugin-camera",
|
||||||
"version": "2.3.0",
|
"version": "4.0.3",
|
||||||
"description": "Cordova Camera Plugin",
|
"description": "Cordova Camera Plugin",
|
||||||
|
"types": "./types/index.d.ts",
|
||||||
"cordova": {
|
"cordova": {
|
||||||
"id": "cordova-plugin-camera",
|
"id": "cordova-plugin-camera",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
"firefoxos",
|
|
||||||
"android",
|
"android",
|
||||||
"amazon-fireos",
|
|
||||||
"ubuntu",
|
|
||||||
"ios",
|
"ios",
|
||||||
"blackberry10",
|
|
||||||
"wp7",
|
|
||||||
"wp8",
|
|
||||||
"windows8",
|
|
||||||
"browser",
|
"browser",
|
||||||
"windows"
|
"windows",
|
||||||
|
"osx"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/apache/cordova-plugin-camera"
|
"url": "https://github.com/apache/cordova-plugin-camera"
|
||||||
},
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://issues.apache.org/jira/browse/CB"
|
||||||
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"cordova",
|
"cordova",
|
||||||
"camera",
|
"camera",
|
||||||
"ecosystem:cordova",
|
"ecosystem:cordova",
|
||||||
"cordova-firefoxos",
|
|
||||||
"cordova-android",
|
"cordova-android",
|
||||||
"cordova-amazon-fireos",
|
|
||||||
"cordova-ubuntu",
|
|
||||||
"cordova-ios",
|
"cordova-ios",
|
||||||
"cordova-blackberry10",
|
|
||||||
"cordova-wp7",
|
|
||||||
"cordova-wp8",
|
|
||||||
"cordova-windows8",
|
|
||||||
"cordova-browser",
|
"cordova-browser",
|
||||||
"cordova-windows"
|
"cordova-windows",
|
||||||
|
"cordova-osx"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"precommit": "npm run gen-docs && git add README.md",
|
"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",
|
"gen-docs": "jsdoc2md --template \"jsdoc2md/TEMPLATE.md\" \"www/**/*.js\" --plugin \"dmd-plugin-cordova-plugin\" > README.md",
|
||||||
"test": "npm run jshint",
|
"test": "npm run eslint",
|
||||||
"jshint": "node node_modules/jshint/bin/jshint www && node node_modules/jshint/bin/jshint src && node node_modules/jshint/bin/jshint tests"
|
"eslint": "node node_modules/eslint/bin/eslint www && node node_modules/eslint/bin/eslint src && node node_modules/eslint/bin/eslint tests"
|
||||||
},
|
},
|
||||||
"author": "Apache Software Foundation",
|
"author": "Apache Software Foundation",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"engines": {
|
"engines": {
|
||||||
"cordovaDependencies": {
|
"cordovaDependencies": {
|
||||||
"3.0.0": {
|
"3.0.0": {
|
||||||
|
"cordova-android": ">=6.3.0"
|
||||||
|
},
|
||||||
|
"5.0.0": {
|
||||||
"cordova": ">100"
|
"cordova": ">100"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"dmd-plugin-cordova-plugin": "^0.1.0",
|
"dmd-plugin-cordova-plugin": "^0.1.0",
|
||||||
|
"eslint": "^4.3.0",
|
||||||
|
"eslint-config-semistandard": "^11.0.0",
|
||||||
|
"eslint-config-standard": "^10.2.1",
|
||||||
|
"eslint-plugin-import": "^2.3.0",
|
||||||
|
"eslint-plugin-node": "^5.0.0",
|
||||||
|
"eslint-plugin-promise": "^3.5.0",
|
||||||
|
"eslint-plugin-standard": "^3.0.1",
|
||||||
"husky": "^0.10.1",
|
"husky": "^0.10.1",
|
||||||
"jsdoc-to-markdown": "^1.2.0",
|
"jsdoc-to-markdown": "^1.2.0"
|
||||||
"jshint": "^2.6.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+39
-146
@@ -22,7 +22,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
||||||
id="cordova-plugin-camera"
|
id="cordova-plugin-camera"
|
||||||
version="2.3.0">
|
version="4.0.3">
|
||||||
<name>Camera</name>
|
<name>Camera</name>
|
||||||
<description>Cordova Camera Plugin</description>
|
<description>Cordova Camera Plugin</description>
|
||||||
<license>Apache 2.0</license>
|
<license>Apache 2.0</license>
|
||||||
@@ -30,7 +30,9 @@
|
|||||||
<repo>https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git</repo>
|
<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>
|
<issue>https://issues.apache.org/jira/browse/CB/component/12320645</issue>
|
||||||
|
|
||||||
<dependency id="cordova-plugin-compat" version="^1.0.0" />
|
<engines>
|
||||||
|
<engine name="cordova-android" version=">=6.3.0" />
|
||||||
|
</engines>
|
||||||
|
|
||||||
<js-module src="www/CameraConstants.js" name="Camera">
|
<js-module src="www/CameraConstants.js" name="Camera">
|
||||||
<clobbers target="Camera" />
|
<clobbers target="Camera" />
|
||||||
@@ -46,19 +48,6 @@
|
|||||||
<clobbers target="navigator.camera" />
|
<clobbers target="navigator.camera" />
|
||||||
</js-module>
|
</js-module>
|
||||||
|
|
||||||
<!-- firefoxos -->
|
|
||||||
<platform name="firefoxos">
|
|
||||||
<config-file target="config.xml" parent="/*">
|
|
||||||
<feature name="Camera">
|
|
||||||
<param name="firefoxos-package" value="Camera" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
|
|
||||||
<js-module src="src/firefoxos/CameraProxy.js" name="CameraProxy">
|
|
||||||
<runs />
|
|
||||||
</js-module>
|
|
||||||
</platform>
|
|
||||||
|
|
||||||
<!-- android -->
|
<!-- android -->
|
||||||
<platform name="android">
|
<platform name="android">
|
||||||
<config-file target="res/xml/config.xml" parent="/*">
|
<config-file target="res/xml/config.xml" parent="/*">
|
||||||
@@ -69,58 +58,32 @@
|
|||||||
<config-file target="AndroidManifest.xml" parent="/*">
|
<config-file target="AndroidManifest.xml" parent="/*">
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
</config-file>
|
</config-file>
|
||||||
|
<config-file target="AndroidManifest.xml" parent="application">
|
||||||
<source-file src="src/android/CameraLauncher.java" target-dir="src/org/apache/cordova/camera" />
|
<provider
|
||||||
<source-file src="src/android/FileHelper.java" target-dir="src/org/apache/cordova/camera" />
|
android:name="org.apache.cordova.camera.FileProvider"
|
||||||
<source-file src="src/android/ExifHelper.java" target-dir="src/org/apache/cordova/camera" />
|
android:authorities="${applicationId}.provider"
|
||||||
|
android:exported="false"
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
android:grantUriPermissions="true" >
|
||||||
<clobbers target="CameraPopoverHandle" />
|
<meta-data
|
||||||
</js-module>
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/camera_provider_paths"/>
|
||||||
</platform>
|
</provider>
|
||||||
|
|
||||||
<!-- amazon-fireos -->
|
|
||||||
<platform name="amazon-fireos">
|
|
||||||
<config-file target="res/xml/config.xml" parent="/*">
|
|
||||||
<feature name="Camera">
|
|
||||||
<param name="android-package" value="org.apache.cordova.camera.CameraLauncher"/>
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<config-file target="AndroidManifest.xml" parent="/*">
|
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
||||||
</config-file>
|
</config-file>
|
||||||
|
|
||||||
<source-file src="src/android/CameraLauncher.java" target-dir="src/org/apache/cordova/camera" />
|
<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/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/ExifHelper.java" target-dir="src/org/apache/cordova/camera" />
|
||||||
|
<source-file src="src/android/FileProvider.java" target-dir="src/org/apache/cordova/camera" />
|
||||||
|
<source-file src="src/android/xml/camera_provider_paths.xml" target-dir="res/xml" />
|
||||||
|
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||||
<clobbers target="CameraPopoverHandle" />
|
<clobbers target="CameraPopoverHandle" />
|
||||||
</js-module>
|
</js-module>
|
||||||
|
|
||||||
</platform>
|
<framework src="com.android.support:support-v4:24.1.1+" />
|
||||||
|
|
||||||
<!-- ubuntu -->
|
</platform>
|
||||||
<platform name="ubuntu">
|
|
||||||
<config-file target="config.xml" parent="/*">
|
|
||||||
<feature name="Camera">
|
|
||||||
<param policy_group="camera" policy_version="1" />
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
|
||||||
<clobbers target="CameraPopoverHandle" />
|
|
||||||
</js-module>
|
|
||||||
<header-file src="src/ubuntu/camera.h" />
|
|
||||||
<source-file src="src/ubuntu/camera.cpp" />
|
|
||||||
|
|
||||||
<resource-file src="src/ubuntu/back.png" />
|
|
||||||
<resource-file src="src/ubuntu/CaptureWidget.qml" />
|
|
||||||
<resource-file src="src/ubuntu/shoot.png" />
|
|
||||||
<resource-file src="src/ubuntu/toolbar-left.png" />
|
|
||||||
<resource-file src="src/ubuntu/toolbar-middle.png" />
|
|
||||||
<resource-file src="src/ubuntu/toolbar-right.png" />
|
|
||||||
</platform>
|
|
||||||
|
|
||||||
<!-- ios -->
|
<!-- ios -->
|
||||||
<platform name="ios">
|
<platform name="ios">
|
||||||
@@ -150,95 +113,8 @@
|
|||||||
<framework src="CoreGraphics.framework" />
|
<framework src="CoreGraphics.framework" />
|
||||||
<framework src="AVFoundation.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>
|
</platform>
|
||||||
|
|
||||||
<!-- blackberry10 -->
|
|
||||||
<platform name="blackberry10">
|
|
||||||
<source-file src="src/blackberry10/index.js" target-dir="Camera" />
|
|
||||||
<config-file target="www/config.xml" parent="/widget">
|
|
||||||
<feature name="Camera" value="Camera"/>
|
|
||||||
</config-file>
|
|
||||||
<config-file target="www/config.xml" parent="/widget/rim:permissions">
|
|
||||||
<rim:permit>access_shared</rim:permit>
|
|
||||||
<rim:permit>use_camera</rim:permit>
|
|
||||||
</config-file>
|
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
|
||||||
<clobbers target="CameraPopoverHandle" />
|
|
||||||
</js-module>
|
|
||||||
<asset src="www/blackberry10/assets" target="chrome" />
|
|
||||||
</platform>
|
|
||||||
|
|
||||||
<!-- wp7 -->
|
|
||||||
<platform name="wp7">
|
|
||||||
<config-file target="config.xml" parent="/*">
|
|
||||||
<feature name="Camera">
|
|
||||||
<param name="wp-package" value="Camera"/>
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
|
|
||||||
<config-file target="Properties/WMAppManifest.xml" parent="/Deployment/App/Capabilities">
|
|
||||||
<Capability Name="ID_CAP_ISV_CAMERA" />
|
|
||||||
<Capability Name="ID_CAP_MEDIALIB" />
|
|
||||||
</config-file>
|
|
||||||
|
|
||||||
<source-file src="src/wp/Camera.cs" />
|
|
||||||
|
|
||||||
|
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
|
||||||
<clobbers target="CameraPopoverHandle" />
|
|
||||||
</js-module>
|
|
||||||
</platform>
|
|
||||||
|
|
||||||
<!-- wp8 -->
|
|
||||||
<platform name="wp8">
|
|
||||||
<config-file target="config.xml" parent="/*">
|
|
||||||
<feature name="Camera">
|
|
||||||
<param name="wp-package" value="Camera"/>
|
|
||||||
</feature>
|
|
||||||
</config-file>
|
|
||||||
|
|
||||||
<config-file target="Properties/WMAppManifest.xml" parent="/Deployment/App/Capabilities">
|
|
||||||
<Capability Name="ID_CAP_ISV_CAMERA" />
|
|
||||||
<Capability Name="ID_CAP_MEDIALIB_PHOTO"/>
|
|
||||||
</config-file>
|
|
||||||
|
|
||||||
<source-file src="src/wp/Camera.cs" />
|
|
||||||
|
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
|
||||||
<clobbers target="CameraPopoverHandle" />
|
|
||||||
</js-module>
|
|
||||||
</platform>
|
|
||||||
|
|
||||||
<!-- windows8 -->
|
|
||||||
<platform name="windows8">
|
|
||||||
|
|
||||||
<config-file target="package.appxmanifest" parent="/Package/Capabilities">
|
|
||||||
<Capability Name="picturesLibrary" />
|
|
||||||
<DeviceCapability Name="webcam" />
|
|
||||||
</config-file>
|
|
||||||
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
|
||||||
<clobbers target="CameraPopoverHandle" />
|
|
||||||
</js-module>
|
|
||||||
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
|
|
||||||
<merges target="" />
|
|
||||||
</js-module>
|
|
||||||
|
|
||||||
</platform>
|
|
||||||
|
|
||||||
<!-- browser -->
|
<!-- browser -->
|
||||||
<platform name="browser">
|
<platform name="browser">
|
||||||
<config-file target="config.xml" parent="/*">
|
<config-file target="config.xml" parent="/*">
|
||||||
@@ -261,10 +137,27 @@
|
|||||||
<clobbers target="CameraPopoverHandle" />
|
<clobbers target="CameraPopoverHandle" />
|
||||||
</js-module>
|
</js-module>
|
||||||
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
|
<js-module src="src/windows/CameraProxy.js" name="CameraProxy">
|
||||||
<merges target="" />
|
<runs />
|
||||||
</js-module>
|
</js-module>
|
||||||
</platform>
|
</platform>
|
||||||
|
|
||||||
|
<!-- osx -->
|
||||||
|
<platform name="osx">
|
||||||
|
<config-file target="config.xml" parent="/*">
|
||||||
|
<feature name="Camera">
|
||||||
|
<param name="osx-package" value="CDVCamera"/>
|
||||||
|
</feature>
|
||||||
|
</config-file>
|
||||||
|
|
||||||
|
<js-module src="www/CameraPopoverHandle.js" name="CameraPopoverHandle">
|
||||||
|
<clobbers target="CameraPopoverHandle" />
|
||||||
|
</js-module>
|
||||||
|
|
||||||
|
<header-file src="src/osx/CDVCamera.h" />
|
||||||
|
<source-file src="src/osx/CDVCamera.m" />
|
||||||
|
|
||||||
|
<framework src="Quartz.framework" />
|
||||||
|
<framework src="AppKit.framework" />
|
||||||
|
</platform>
|
||||||
|
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|||||||
+147
-90
@@ -29,8 +29,10 @@ import java.io.OutputStream;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
import org.apache.cordova.BuildHelper;
|
||||||
import org.apache.cordova.CallbackContext;
|
import org.apache.cordova.CallbackContext;
|
||||||
import org.apache.cordova.CordovaPlugin;
|
import org.apache.cordova.CordovaPlugin;
|
||||||
|
import org.apache.cordova.CordovaResourceApi;
|
||||||
import org.apache.cordova.LOG;
|
import org.apache.cordova.LOG;
|
||||||
import org.apache.cordova.PermissionHelper;
|
import org.apache.cordova.PermissionHelper;
|
||||||
import org.apache.cordova.PluginResult;
|
import org.apache.cordova.PluginResult;
|
||||||
@@ -58,6 +60,8 @@ import android.os.Bundle;
|
|||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
|
import android.provider.OpenableColumns;
|
||||||
|
import android.support.v4.content.FileProvider;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
@@ -99,7 +103,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||||
private int targetWidth; // desired width of the image
|
private int targetWidth; // desired width of the image
|
||||||
private int targetHeight; // desired height of the image
|
private int targetHeight; // desired height of the image
|
||||||
private Uri imageUri; // Uri of captured image
|
private CordovaUri imageUri; // Uri of captured image
|
||||||
private int encodingType; // Type of encoding to use
|
private int encodingType; // Type of encoding to use
|
||||||
private int mediaType; // What type of media to retrieve
|
private int mediaType; // What type of media to retrieve
|
||||||
private int destType; // Source type (needs to be saved for the permission handling)
|
private int destType; // Source type (needs to be saved for the permission handling)
|
||||||
@@ -109,7 +113,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
private boolean orientationCorrected; // Has the picture's orientation been corrected
|
private boolean orientationCorrected; // Has the picture's orientation been corrected
|
||||||
private boolean allowEdit; // Should we allow the user to crop the image.
|
private boolean allowEdit; // Should we allow the user to crop the image.
|
||||||
|
|
||||||
protected final static String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE };
|
protected final static String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE };
|
||||||
|
|
||||||
public CallbackContext callbackContext;
|
public CallbackContext callbackContext;
|
||||||
private int numPics;
|
private int numPics;
|
||||||
@@ -118,6 +122,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
private Uri scanMe; // Uri of image to be added to content store
|
private Uri scanMe; // Uri of image to be added to content store
|
||||||
private Uri croppedUri;
|
private Uri croppedUri;
|
||||||
private ExifHelper exifData; // Exif data from source
|
private ExifHelper exifData; // Exif data from source
|
||||||
|
private String applicationId;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -130,6 +135,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
*/
|
*/
|
||||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||||
this.callbackContext = callbackContext;
|
this.callbackContext = callbackContext;
|
||||||
|
//Adding an API to CoreAndroid to get the BuildConfigValue
|
||||||
|
//This allows us to not make this a breaking change to embedding
|
||||||
|
this.applicationId = (String) BuildHelper.getBuildConfigValue(cordova.getActivity(), "APPLICATION_ID");
|
||||||
|
this.applicationId = preferences.getString("applicationId", this.applicationId);
|
||||||
|
|
||||||
|
|
||||||
if (action.equals("takePicture")) {
|
if (action.equals("takePicture")) {
|
||||||
this.srcType = CAMERA;
|
this.srcType = CAMERA;
|
||||||
@@ -175,7 +185,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
}
|
}
|
||||||
else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
|
else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
|
||||||
// FIXME: Stop always requesting the permission
|
// FIXME: Stop always requesting the permission
|
||||||
if(!PermissionHelper.hasPermission(this, permissions[0])) {
|
if(!PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
|
||||||
PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
|
PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
|
||||||
} else {
|
} else {
|
||||||
this.getImage(this.srcType, destType, encodingType);
|
this.getImage(this.srcType, destType, encodingType);
|
||||||
@@ -232,10 +242,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
* img.src=result;
|
* img.src=result;
|
||||||
*
|
*
|
||||||
* @param returnType Set the type of image to return.
|
* @param returnType Set the type of image to return.
|
||||||
* @param encodingType JPEG or PNG
|
* @param encodingType Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||||
*/
|
*/
|
||||||
public void callTakePicture(int returnType, int encodingType) {
|
public void callTakePicture(int returnType, int encodingType) {
|
||||||
boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
|
boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
|
&& PermissionHelper.hasPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
|
||||||
boolean takePicturePermission = PermissionHelper.hasPermission(this, Manifest.permission.CAMERA);
|
boolean takePicturePermission = PermissionHelper.hasPermission(this, Manifest.permission.CAMERA);
|
||||||
|
|
||||||
// CB-10120: The CAMERA permission does not need to be requested unless it is declared
|
// CB-10120: The CAMERA permission does not need to be requested unless it is declared
|
||||||
@@ -266,7 +277,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
} else if (saveAlbumPermission && !takePicturePermission) {
|
} else if (saveAlbumPermission && !takePicturePermission) {
|
||||||
PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA);
|
PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA);
|
||||||
} else if (!saveAlbumPermission && takePicturePermission) {
|
} else if (!saveAlbumPermission && takePicturePermission) {
|
||||||
PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
|
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC,
|
||||||
|
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE});
|
||||||
} else {
|
} else {
|
||||||
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, permissions);
|
PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, permissions);
|
||||||
}
|
}
|
||||||
@@ -282,8 +294,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
// Specify file so that large image is captured and returned
|
// Specify file so that large image is captured and returned
|
||||||
File photo = createCaptureFile(encodingType);
|
File photo = createCaptureFile(encodingType);
|
||||||
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
|
this.imageUri = new CordovaUri(FileProvider.getUriForFile(cordova.getActivity(),
|
||||||
this.imageUri = Uri.fromFile(photo);
|
applicationId + ".provider",
|
||||||
|
photo));
|
||||||
|
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, imageUri.getCorrectUri());
|
||||||
|
//We can write to this URI, this will hopefully allow us to write files to get to the next step
|
||||||
|
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
|
||||||
if (this.cordova != null) {
|
if (this.cordova != null) {
|
||||||
// Let's check to make sure the camera is actually installed. (Legacy Nexus 7 code)
|
// Let's check to make sure the camera is actually installed. (Legacy Nexus 7 code)
|
||||||
@@ -399,33 +415,37 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
*/
|
*/
|
||||||
private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
|
private void performCrop(Uri picUri, int destType, Intent cameraIntent) {
|
||||||
try {
|
try {
|
||||||
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
||||||
// indicate image type and Uri
|
// indicate image type and Uri
|
||||||
cropIntent.setDataAndType(picUri, "image/*");
|
cropIntent.setDataAndType(picUri, "image/*");
|
||||||
// set crop properties
|
// set crop properties
|
||||||
cropIntent.putExtra("crop", "true");
|
cropIntent.putExtra("crop", "true");
|
||||||
|
|
||||||
// indicate output X and Y
|
|
||||||
if (targetWidth > 0) {
|
// indicate output X and Y
|
||||||
|
if (targetWidth > 0) {
|
||||||
cropIntent.putExtra("outputX", targetWidth);
|
cropIntent.putExtra("outputX", targetWidth);
|
||||||
}
|
}
|
||||||
if (targetHeight > 0) {
|
if (targetHeight > 0) {
|
||||||
cropIntent.putExtra("outputY", targetHeight);
|
cropIntent.putExtra("outputY", targetHeight);
|
||||||
}
|
}
|
||||||
if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
|
if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
|
||||||
cropIntent.putExtra("aspectX", 1);
|
cropIntent.putExtra("aspectX", 1);
|
||||||
cropIntent.putExtra("aspectY", 1);
|
cropIntent.putExtra("aspectY", 1);
|
||||||
}
|
}
|
||||||
// create new file handle to get full resolution crop
|
// create new file handle to get full resolution crop
|
||||||
croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
||||||
cropIntent.putExtra("output", croppedUri);
|
cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
|
||||||
|
cropIntent.putExtra("output", croppedUri);
|
||||||
|
|
||||||
// start the activity - we handle returning in onActivityResult
|
|
||||||
|
|
||||||
if (this.cordova != null) {
|
// start the activity - we handle returning in onActivityResult
|
||||||
this.cordova.startActivityForResult((CordovaPlugin) this,
|
|
||||||
cropIntent, CROP_CAMERA + destType);
|
if (this.cordova != null) {
|
||||||
}
|
this.cordova.startActivityForResult((CordovaPlugin) this,
|
||||||
|
cropIntent, CROP_CAMERA + destType);
|
||||||
|
}
|
||||||
} catch (ActivityNotFoundException anfe) {
|
} catch (ActivityNotFoundException anfe) {
|
||||||
LOG.e(LOG_TAG, "Crop operation not supported on this device");
|
LOG.e(LOG_TAG, "Crop operation not supported on this device");
|
||||||
try {
|
try {
|
||||||
@@ -450,9 +470,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
// Create an ExifHelper to save the exif data that is lost during compression
|
// Create an ExifHelper to save the exif data that is lost during compression
|
||||||
ExifHelper exif = new ExifHelper();
|
ExifHelper exif = new ExifHelper();
|
||||||
|
|
||||||
String sourcePath = (this.allowEdit && this.croppedUri != null) ?
|
String sourcePath = (this.allowEdit && this.croppedUri != null) ?
|
||||||
FileHelper.stripFileProtocol(this.croppedUri.toString()) :
|
FileHelper.stripFileProtocol(this.croppedUri.toString()) :
|
||||||
FileHelper.stripFileProtocol(this.imageUri.toString());
|
this.imageUri.getFilePath();
|
||||||
|
|
||||||
|
|
||||||
if (this.encodingType == JPEG) {
|
if (this.encodingType == JPEG) {
|
||||||
try {
|
try {
|
||||||
@@ -475,10 +497,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
if (this.saveToPhotoAlbum) {
|
if (this.saveToPhotoAlbum) {
|
||||||
galleryUri = Uri.fromFile(new File(getPicturesPath()));
|
galleryUri = Uri.fromFile(new File(getPicturesPath()));
|
||||||
|
|
||||||
if(this.allowEdit && this.croppedUri != null) {
|
if (this.allowEdit && this.croppedUri != null) {
|
||||||
writeUncompressedImage(this.croppedUri, galleryUri);
|
writeUncompressedImage(croppedUri, galleryUri);
|
||||||
} else {
|
} else {
|
||||||
writeUncompressedImage(this.imageUri, galleryUri);
|
Uri imageUri = this.imageUri.getFileUri();
|
||||||
|
writeUncompressedImage(imageUri, galleryUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshGallery(galleryUri);
|
refreshGallery(galleryUri);
|
||||||
@@ -490,7 +513,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
// Try to get the bitmap from intent.
|
// Try to get the bitmap from intent.
|
||||||
bitmap = (Bitmap)intent.getExtras().get("data");
|
bitmap = (Bitmap) intent.getExtras().get("data");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Double-check the bitmap.
|
// Double-check the bitmap.
|
||||||
@@ -521,10 +544,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
} else {
|
} else {
|
||||||
Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
|
||||||
|
|
||||||
if(this.allowEdit && this.croppedUri != null) {
|
if (this.allowEdit && this.croppedUri != null) {
|
||||||
writeUncompressedImage(this.croppedUri, uri);
|
Uri croppedUri = Uri.fromFile(new File(getFileNameFromUri(this.croppedUri)));
|
||||||
|
writeUncompressedImage(croppedUri, uri);
|
||||||
} else {
|
} else {
|
||||||
writeUncompressedImage(this.imageUri, uri);
|
Uri imageUri = this.imageUri.getFileUri();
|
||||||
|
writeUncompressedImage(imageUri, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.callbackContext.success(uri.toString());
|
this.callbackContext.success(uri.toString());
|
||||||
@@ -554,6 +579,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
if (this.encodingType == JPEG) {
|
if (this.encodingType == JPEG) {
|
||||||
String exifPath;
|
String exifPath;
|
||||||
exifPath = uri.getPath();
|
exifPath = uri.getPath();
|
||||||
|
//We just finished rotating it by an arbitrary orientation, just make sure it's normal
|
||||||
|
if(rotate != ExifInterface.ORIENTATION_NORMAL)
|
||||||
|
exif.resetOrientation();
|
||||||
exif.createOutFile(exifPath);
|
exif.createOutFile(exifPath);
|
||||||
exif.writeExifData();
|
exif.writeExifData();
|
||||||
}
|
}
|
||||||
@@ -566,22 +594,21 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cleanup(FILE_URI, this.imageUri, galleryUri, bitmap);
|
this.cleanup(FILE_URI, this.imageUri.getFileUri(), galleryUri, bitmap);
|
||||||
bitmap = null;
|
bitmap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getPicturesPath()
|
private String getPicturesPath() {
|
||||||
{
|
|
||||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||||
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||||
File storageDir = Environment.getExternalStoragePublicDirectory(
|
File storageDir = Environment.getExternalStoragePublicDirectory(
|
||||||
Environment.DIRECTORY_PICTURES);
|
Environment.DIRECTORY_PICTURES);
|
||||||
|
storageDir.mkdirs();
|
||||||
String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
|
String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
|
||||||
return galleryPath;
|
return galleryPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void refreshGallery(Uri contentUri)
|
private void refreshGallery(Uri contentUri) {
|
||||||
{
|
|
||||||
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
|
||||||
mediaScanIntent.setData(contentUri);
|
mediaScanIntent.setData(contentUri);
|
||||||
this.cordova.getActivity().sendBroadcast(mediaScanIntent);
|
this.cordova.getActivity().sendBroadcast(mediaScanIntent);
|
||||||
@@ -601,9 +628,16 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
|
|
||||||
private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||||
|
// Some content: URIs do not map to file paths (e.g. picasa).
|
||||||
|
String realPath = FileHelper.getRealPath(uri, this.cordova);
|
||||||
|
|
||||||
|
// Get filename from uri
|
||||||
|
String fileName = realPath != null ?
|
||||||
|
realPath.substring(realPath.lastIndexOf('/') + 1) :
|
||||||
|
"modified." + (this.encodingType == JPEG ? "jpg" : "png");
|
||||||
|
|
||||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
|
||||||
String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
//String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
|
||||||
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
|
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
|
||||||
|
|
||||||
OutputStream os = new FileOutputStream(modifiedPath);
|
OutputStream os = new FileOutputStream(modifiedPath);
|
||||||
@@ -630,12 +664,11 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies all needed transformation to the image received from the gallery.
|
* Applies all needed transformation to the image received from the gallery.
|
||||||
*
|
*
|
||||||
* @param destType In which form should we return the image
|
* @param destType In which form should we return the image
|
||||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||||
*/
|
*/
|
||||||
private void processResultFromGallery(int destType, Intent intent) {
|
private void processResultFromGallery(int destType, Intent intent) {
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
@@ -710,8 +743,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
this.failPicture("Error retrieving image.");
|
this.failPicture("Error retrieving image.");
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.callbackContext.success(fileLocation);
|
this.callbackContext.success(fileLocation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -727,10 +759,10 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
/**
|
/**
|
||||||
* Called when the camera view exits.
|
* Called when the camera view exits.
|
||||||
*
|
*
|
||||||
* @param requestCode The request code originally supplied to startActivityForResult(),
|
* @param requestCode The request code originally supplied to startActivityForResult(),
|
||||||
* allowing you to identify who this result came from.
|
* allowing you to identify who this result came from.
|
||||||
* @param resultCode The integer result code returned by the child activity through its setResult().
|
* @param resultCode The integer result code returned by the child activity through its setResult().
|
||||||
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
* @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
|
||||||
*/
|
*/
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
|
||||||
|
|
||||||
@@ -754,7 +786,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
}// If cancelled
|
}// If cancelled
|
||||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
this.failPicture("Camera cancelled.");
|
this.failPicture("No Image Selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If something else
|
// If something else
|
||||||
@@ -767,12 +799,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
// If image available
|
// If image available
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
try {
|
try {
|
||||||
if(this.allowEdit)
|
if (this.allowEdit) {
|
||||||
{
|
Uri tmpFile = FileProvider.getUriForFile(cordova.getActivity(),
|
||||||
Uri tmpFile = Uri.fromFile(createCaptureFile(this.encodingType));
|
applicationId + ".provider",
|
||||||
|
createCaptureFile(this.encodingType));
|
||||||
performCrop(tmpFile, destType, intent);
|
performCrop(tmpFile, destType, intent);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
this.processResultFromCamera(destType, intent);
|
this.processResultFromCamera(destType, intent);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -783,7 +815,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
// If cancelled
|
// If cancelled
|
||||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
this.failPicture("Camera cancelled.");
|
this.failPicture("No Image Selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// If something else
|
// If something else
|
||||||
@@ -801,11 +833,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
processResultFromGallery(finalDestType, i);
|
processResultFromGallery(finalDestType, i);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
else if (resultCode == Activity.RESULT_CANCELED) {
|
this.failPicture("No Image Selected");
|
||||||
this.failPicture("Selection cancelled.");
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.failPicture("Selection did not complete!");
|
this.failPicture("Selection did not complete!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -822,7 +852,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write an inputstream to local disk
|
* Write an inputstream to local disk
|
||||||
*
|
*
|
||||||
@@ -847,14 +877,14 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
try {
|
try {
|
||||||
os.close();
|
os.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.d(LOG_TAG,"Exception while closing output stream.");
|
LOG.d(LOG_TAG, "Exception while closing output stream.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fis != null) {
|
if (fis != null) {
|
||||||
try {
|
try {
|
||||||
fis.close();
|
fis.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
LOG.d(LOG_TAG, "Exception while closing file input stream.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -913,12 +943,17 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
try {
|
try {
|
||||||
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
|
||||||
image = BitmapFactory.decodeStream(fileStream);
|
image = BitmapFactory.decodeStream(fileStream);
|
||||||
} finally {
|
} catch (OutOfMemoryError e) {
|
||||||
|
callbackContext.error(e.getLocalizedMessage());
|
||||||
|
} catch (Exception e){
|
||||||
|
callbackContext.error(e.getLocalizedMessage());
|
||||||
|
}
|
||||||
|
finally {
|
||||||
if (fileStream != null) {
|
if (fileStream != null) {
|
||||||
try {
|
try {
|
||||||
fileStream.close();
|
fileStream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
LOG.d(LOG_TAG,"Exception while closing file input stream.");
|
LOG.d(LOG_TAG, "Exception while closing file input stream.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1126,8 +1161,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
|
public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
|
||||||
final float srcAspect = (float)srcWidth / (float)srcHeight;
|
final float srcAspect = (float) srcWidth / (float) srcHeight;
|
||||||
final float dstAspect = (float)dstWidth / (float)dstHeight;
|
final float dstAspect = (float) dstWidth / (float) dstHeight;
|
||||||
|
|
||||||
if (srcAspect > dstAspect) {
|
if (srcAspect > dstAspect) {
|
||||||
return srcWidth / dstWidth;
|
return srcWidth / dstWidth;
|
||||||
@@ -1144,7 +1179,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
private Cursor queryImgDB(Uri contentStore) {
|
private Cursor queryImgDB(Uri contentStore) {
|
||||||
return this.cordova.getActivity().getContentResolver().query(
|
return this.cordova.getActivity().getContentResolver().query(
|
||||||
contentStore,
|
contentStore,
|
||||||
new String[] { MediaStore.Images.Media._ID },
|
new String[]{MediaStore.Images.Media._ID},
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null);
|
null);
|
||||||
@@ -1152,6 +1187,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
|
* Cleans up after picture taking. Checking for duplicates and that kind of stuff.
|
||||||
|
*
|
||||||
* @param newImage
|
* @param newImage
|
||||||
*/
|
*/
|
||||||
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
|
private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
|
||||||
@@ -1203,6 +1239,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if we are storing the images in internal or external storage
|
* Determine if we are storing the images in internal or external storage
|
||||||
|
*
|
||||||
* @return Uri
|
* @return Uri
|
||||||
*/
|
*/
|
||||||
private Uri whichContentStore() {
|
private Uri whichContentStore() {
|
||||||
@@ -1251,7 +1288,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
private void scanForGallery(Uri newImage) {
|
private void scanForGallery(Uri newImage) {
|
||||||
this.scanMe = newImage;
|
this.scanMe = newImage;
|
||||||
if(this.conn != null) {
|
if (this.conn != null) {
|
||||||
this.conn.disconnect();
|
this.conn.disconnect();
|
||||||
}
|
}
|
||||||
this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
|
this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
|
||||||
@@ -1259,9 +1296,9 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onMediaScannerConnected() {
|
public void onMediaScannerConnected() {
|
||||||
try{
|
try {
|
||||||
this.conn.scanFile(this.scanMe.toString(), "image/*");
|
this.conn.scanFile(this.scanMe.toString(), "image/*");
|
||||||
} catch (java.lang.IllegalStateException e){
|
} catch (java.lang.IllegalStateException e) {
|
||||||
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
|
LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1273,18 +1310,14 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
|
|
||||||
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
public void onRequestPermissionResult(int requestCode, String[] permissions,
|
||||||
int[] grantResults) throws JSONException
|
int[] grantResults) throws JSONException {
|
||||||
{
|
for (int r : grantResults) {
|
||||||
for(int r:grantResults)
|
if (r == PackageManager.PERMISSION_DENIED) {
|
||||||
{
|
|
||||||
if(r == PackageManager.PERMISSION_DENIED)
|
|
||||||
{
|
|
||||||
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
|
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
switch(requestCode)
|
switch (requestCode) {
|
||||||
{
|
|
||||||
case TAKE_PIC_SEC:
|
case TAKE_PIC_SEC:
|
||||||
takePicture(this.destType, this.encodingType);
|
takePicture(this.destType, this.encodingType);
|
||||||
break;
|
break;
|
||||||
@@ -1313,12 +1346,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
state.putBoolean("correctOrientation", this.correctOrientation);
|
state.putBoolean("correctOrientation", this.correctOrientation);
|
||||||
state.putBoolean("saveToPhotoAlbum", this.saveToPhotoAlbum);
|
state.putBoolean("saveToPhotoAlbum", this.saveToPhotoAlbum);
|
||||||
|
|
||||||
if(this.croppedUri != null) {
|
if (this.croppedUri != null) {
|
||||||
state.putString("croppedUri", this.croppedUri.toString());
|
state.putString("croppedUri", this.croppedUri.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.imageUri != null) {
|
if (this.imageUri != null) {
|
||||||
state.putString("imageUri", this.imageUri.toString());
|
state.putString("imageUri", this.imageUri.getFileUri().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
@@ -1337,14 +1370,38 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
this.correctOrientation = state.getBoolean("correctOrientation");
|
this.correctOrientation = state.getBoolean("correctOrientation");
|
||||||
this.saveToPhotoAlbum = state.getBoolean("saveToPhotoAlbum");
|
this.saveToPhotoAlbum = state.getBoolean("saveToPhotoAlbum");
|
||||||
|
|
||||||
if(state.containsKey("croppedUri")) {
|
if (state.containsKey("croppedUri")) {
|
||||||
this.croppedUri = Uri.parse(state.getString("croppedUri"));
|
this.croppedUri = Uri.parse(state.getString("croppedUri"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state.containsKey("imageUri")) {
|
if (state.containsKey("imageUri")) {
|
||||||
this.imageUri = Uri.parse(state.getString("imageUri"));
|
//I have no idea what type of URI is being passed in
|
||||||
|
this.imageUri = new CordovaUri(Uri.parse(state.getString("imageUri")));
|
||||||
}
|
}
|
||||||
|
|
||||||
this.callbackContext = callbackContext;
|
this.callbackContext = callbackContext;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class FileProvider extends android.support.v4.content.FileProvider {}
|
||||||
@@ -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>
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* globals qnx, FileError, PluginResult */
|
|
||||||
|
|
||||||
var 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)
|
|
||||||
},
|
|
||||||
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)
|
|
||||||
},
|
|
||||||
savePath = window.qnx.webplatform.getApplication().getEnv("HOME").replace('/data', '') + '/shared/camera/',
|
|
||||||
invokeAvailable = true;
|
|
||||||
|
|
||||||
//check for camera card - it isn't currently availble in work perimeter
|
|
||||||
window.qnx.webplatform.getApplication().invocation.queryTargets(
|
|
||||||
{
|
|
||||||
type: 'image/jpeg',
|
|
||||||
action: 'bb.action.CAPTURE',
|
|
||||||
target_type: 'CARD'
|
|
||||||
},
|
|
||||||
function (error, targets) {
|
|
||||||
invokeAvailable = !error && targets && targets instanceof Array &&
|
|
||||||
targets.filter(function (t) { return t.default === 'sys.camera.card'; }).length > 0;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
//open a webview with getUserMedia camera card implementation when camera card not available
|
|
||||||
function showCameraDialog (done, cancel, fail) {
|
|
||||||
var wv = qnx.webplatform.createWebView(function () {
|
|
||||||
wv.url = 'local:///chrome/camera.html';
|
|
||||||
wv.allowQnxObject = true;
|
|
||||||
wv.allowRpc = true;
|
|
||||||
wv.zOrder = 1;
|
|
||||||
wv.setGeometry(0, 0, screen.width, screen.height);
|
|
||||||
wv.backgroundColor = 0x00000000;
|
|
||||||
wv.active = true;
|
|
||||||
wv.visible = true;
|
|
||||||
wv.on('UserMediaRequest', function (evt, args) {
|
|
||||||
wv.allowUserMedia(JSON.parse(args).id, 'CAMERA_UNIT_REAR');
|
|
||||||
});
|
|
||||||
wv.on('JavaScriptCallback', function (evt, data) {
|
|
||||||
var args = JSON.parse(data).args;
|
|
||||||
if (args[0] === 'cordova-plugin-camera') {
|
|
||||||
if (args[1] === 'cancel') {
|
|
||||||
cancel('User canceled');
|
|
||||||
} else if (args[1] === 'error') {
|
|
||||||
fail(args[2]);
|
|
||||||
} else {
|
|
||||||
saveImage(args[1], done, fail);
|
|
||||||
}
|
|
||||||
wv.un('JavaScriptCallback', arguments.callee);
|
|
||||||
wv.visible = false;
|
|
||||||
wv.destroy();
|
|
||||||
qnx.webplatform.getApplication().unlockRotation();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
wv.on('Destroyed', function () {
|
|
||||||
wv.delete();
|
|
||||||
});
|
|
||||||
qnx.webplatform.getApplication().lockRotation();
|
|
||||||
qnx.webplatform.getController().dispatchEvent('webview.initialized', [wv]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//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; };
|
|
||||||
return 'IMG_' + date.getFullYear() + pad(date.getMonth() + 1) + pad(date.getDate()) + '_' +
|
|
||||||
pad(date.getHours()) + pad(date.getMinutes()) + pad(date.getSeconds()) + '.png';
|
|
||||||
}
|
|
||||||
|
|
||||||
//convert dataURI to Blob
|
|
||||||
function dataURItoBlob(dataURI) {
|
|
||||||
var byteString = atob(dataURI.split(',')[1]),
|
|
||||||
mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0],
|
|
||||||
arrayBuffer = new ArrayBuffer(byteString.length),
|
|
||||||
ia = new Uint8Array(arrayBuffer),
|
|
||||||
i;
|
|
||||||
for (i = 0; i < byteString.length; i++) {
|
|
||||||
ia[i] = byteString.charCodeAt(i);
|
|
||||||
}
|
|
||||||
return new Blob([new DataView(arrayBuffer)], { type: mimeString });
|
|
||||||
}
|
|
||||||
|
|
||||||
//save dataURI to file system and call success with path
|
|
||||||
function saveImage(data, success, fail) {
|
|
||||||
var name = savePath + imgName();
|
|
||||||
require('lib/webview').setSandbox(false);
|
|
||||||
window.webkitRequestFileSystem(window.PERSISTENT, 0, function (fs) {
|
|
||||||
fs.root.getFile(name, { create: true }, function (entry) {
|
|
||||||
entry.createWriter(function (writer) {
|
|
||||||
writer.onwriteend = function () {
|
|
||||||
success(name);
|
|
||||||
};
|
|
||||||
writer.onerror = fail;
|
|
||||||
writer.write(dataURItoBlob(data));
|
|
||||||
});
|
|
||||||
}, fail);
|
|
||||||
}, fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeBase64(filePath, callback) {
|
|
||||||
var sandbox = window.qnx.webplatform.getController().setFileSystemSandbox, // save original sandbox value
|
|
||||||
errorHandler = function (err) {
|
|
||||||
var msg = "An error occured: ";
|
|
||||||
|
|
||||||
switch (err.code) {
|
|
||||||
case FileError.NOT_FOUND_ERR:
|
|
||||||
msg += "File or directory not found";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FileError.NOT_READABLE_ERR:
|
|
||||||
msg += "File or directory not readable";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FileError.PATH_EXISTS_ERR:
|
|
||||||
msg += "File or directory already exists";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FileError.TYPE_MISMATCH_ERR:
|
|
||||||
msg += "Invalid file type";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
msg += "Unknown Error";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set it back to original value
|
|
||||||
window.qnx.webplatform.getController().setFileSystemSandbox = sandbox;
|
|
||||||
callback(msg);
|
|
||||||
},
|
|
||||||
gotFile = function (fileEntry) {
|
|
||||||
fileEntry.file(function (file) {
|
|
||||||
var reader = new FileReader();
|
|
||||||
|
|
||||||
reader.onloadend = function (e) {
|
|
||||||
// set it back to original value
|
|
||||||
window.qnx.webplatform.getController().setFileSystemSandbox = sandbox;
|
|
||||||
callback(this.result);
|
|
||||||
};
|
|
||||||
|
|
||||||
reader.readAsDataURL(file);
|
|
||||||
}, errorHandler);
|
|
||||||
},
|
|
||||||
onInitFs = function (fs) {
|
|
||||||
window.qnx.webplatform.getController().setFileSystemSandbox = false;
|
|
||||||
fs.root.getFile(filePath, {create: false}, gotFile, errorHandler);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.webkitRequestFileSystem(window.TEMPORARY, 10 * 1024 * 1024, onInitFs, errorHandler); // set size to 10MB max
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
takePicture: function (success, fail, args, env) {
|
|
||||||
var destinationType = JSON.parse(decodeURIComponent(args[1])),
|
|
||||||
sourceType = JSON.parse(decodeURIComponent(args[2])),
|
|
||||||
result = new PluginResult(args, env),
|
|
||||||
done = function (data) {
|
|
||||||
if (destinationType === DestinationType.FILE_URI) {
|
|
||||||
data = "file://" + data;
|
|
||||||
result.callbackOk(data, false);
|
|
||||||
} else {
|
|
||||||
encodeBase64(data, function (data) {
|
|
||||||
if (/^data:/.test(data)) {
|
|
||||||
data = data.slice(data.indexOf(",") + 1);
|
|
||||||
result.callbackOk(data, false);
|
|
||||||
} else {
|
|
||||||
result.callbackError(data, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cancel = function (reason) {
|
|
||||||
result.callbackError(reason, false);
|
|
||||||
},
|
|
||||||
invoked = function (error) {
|
|
||||||
if (error) {
|
|
||||||
result.callbackError(error, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switch(sourceType) {
|
|
||||||
case PictureSourceType.CAMERA:
|
|
||||||
if (invokeAvailable) {
|
|
||||||
window.qnx.webplatform.getApplication().cards.camera.open("photo", done, cancel, invoked);
|
|
||||||
} else {
|
|
||||||
showCameraDialog(done, cancel, fail);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PictureSourceType.PHOTOLIBRARY:
|
|
||||||
case PictureSourceType.SAVEDPHOTOALBUM:
|
|
||||||
window.qnx.webplatform.getApplication().cards.filePicker.open({
|
|
||||||
mode: "Picker",
|
|
||||||
type: ["picture"]
|
|
||||||
}, done, cancel, invoked);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.noResult(true);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
+11
-11
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
||||||
|
|
||||||
function takePicture(success, error, opts) {
|
function takePicture (success, error, opts) {
|
||||||
if (opts && opts[2] === 1) {
|
if (opts && opts[2] === 1) {
|
||||||
capture(success, error, opts);
|
capture(success, error, opts);
|
||||||
} else {
|
} else {
|
||||||
@@ -32,9 +32,9 @@ function takePicture(success, error, opts) {
|
|||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.name = 'files[]';
|
input.name = 'files[]';
|
||||||
|
|
||||||
input.onchange = function(inputEvent) {
|
input.onchange = function (inputEvent) {
|
||||||
var reader = new FileReader();
|
var reader = new FileReader(); /* eslint no-undef : 0 */
|
||||||
reader.onload = function(readerEvent) {
|
reader.onload = function (readerEvent) {
|
||||||
input.parentNode.removeChild(input);
|
input.parentNode.removeChild(input);
|
||||||
|
|
||||||
var imageData = readerEvent.target.result;
|
var imageData = readerEvent.target.result;
|
||||||
@@ -49,13 +49,13 @@ function takePicture(success, error, opts) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function capture(success, errorCallback, opts) {
|
function capture (success, errorCallback, opts) {
|
||||||
var localMediaStream;
|
var localMediaStream;
|
||||||
var targetWidth = opts[3];
|
var targetWidth = opts[3];
|
||||||
var targetHeight = opts[4];
|
var targetHeight = opts[4];
|
||||||
|
|
||||||
targetWidth = targetWidth == -1?320:targetWidth;
|
targetWidth = targetWidth === -1 ? 320 : targetWidth;
|
||||||
targetHeight = targetHeight == -1?240:targetHeight;
|
targetHeight = targetHeight === -1 ? 240 : targetHeight;
|
||||||
|
|
||||||
var video = document.createElement('video');
|
var video = document.createElement('video');
|
||||||
var button = document.createElement('button');
|
var button = document.createElement('button');
|
||||||
@@ -70,7 +70,7 @@ function capture(success, errorCallback, opts) {
|
|||||||
video.height = targetHeight;
|
video.height = targetHeight;
|
||||||
button.innerHTML = 'Capture!';
|
button.innerHTML = 'Capture!';
|
||||||
|
|
||||||
button.onclick = function() {
|
button.onclick = function () {
|
||||||
// create a canvas and capture a frame from video stream
|
// create a canvas and capture a frame from video stream
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
canvas.width = targetWidth;
|
canvas.width = targetWidth;
|
||||||
@@ -100,7 +100,7 @@ function capture(success, errorCallback, opts) {
|
|||||||
navigator.mozGetUserMedia ||
|
navigator.mozGetUserMedia ||
|
||||||
navigator.msGetUserMedia;
|
navigator.msGetUserMedia;
|
||||||
|
|
||||||
var successCallback = function(stream) {
|
var successCallback = function (stream) {
|
||||||
localMediaStream = stream;
|
localMediaStream = stream;
|
||||||
video.src = window.URL.createObjectURL(localMediaStream);
|
video.src = window.URL.createObjectURL(localMediaStream);
|
||||||
video.play();
|
video.play();
|
||||||
@@ -117,7 +117,7 @@ function capture(success, errorCallback, opts) {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
takePicture: takePicture,
|
takePicture: takePicture,
|
||||||
cleanup: function(){}
|
cleanup: function () {}
|
||||||
};
|
};
|
||||||
|
|
||||||
require("cordova/exec/proxy").add("Camera",module.exports);
|
require('cordova/exec/proxy').add('Camera', module.exports);
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* globals MozActivity */
|
|
||||||
|
|
||||||
function takePicture(success, error, opts) {
|
|
||||||
var pick = new MozActivity({
|
|
||||||
name: "pick",
|
|
||||||
data: {
|
|
||||||
type: ["image/*"]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
pick.onerror = error || function() {};
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// TODO: base64
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!opts || !opts.destinationType || opts.destinationType > 0) {
|
|
||||||
// url
|
|
||||||
return success(window.URL.createObjectURL(this.result.blob));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
takePicture: takePicture,
|
|
||||||
cleanup: function(){}
|
|
||||||
};
|
|
||||||
|
|
||||||
require("cordova/exec/proxy").add("Camera", module.exports);
|
|
||||||
+58
-57
@@ -42,7 +42,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
|
SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
|
||||||
SEL s2 = NSSelectorFromString(@"base64EncodedString");
|
SEL s2 = NSSelectorFromString(@"base64EncodedString");
|
||||||
SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
|
SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
|
||||||
|
|
||||||
if ([data respondsToSelector:s1]) {
|
if ([data respondsToSelector:s1]) {
|
||||||
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
|
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
|
||||||
return func(data, s1);
|
return func(data, s1);
|
||||||
@@ -66,7 +66,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
|
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
|
||||||
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
|
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
|
||||||
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(UIImagePickerControllerSourceTypeCamera)] unsignedIntegerValue];
|
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(UIImagePickerControllerSourceTypeCamera)] unsignedIntegerValue];
|
||||||
|
|
||||||
NSNumber* targetWidth = [command argumentAtIndex:3 withDefault:nil];
|
NSNumber* targetWidth = [command argumentAtIndex:3 withDefault:nil];
|
||||||
NSNumber* targetHeight = [command argumentAtIndex:4 withDefault:nil];
|
NSNumber* targetHeight = [command argumentAtIndex:4 withDefault:nil];
|
||||||
pictureOptions.targetSize = CGSizeMake(0, 0);
|
pictureOptions.targetSize = CGSizeMake(0, 0);
|
||||||
@@ -81,10 +81,10 @@ static NSString* toBase64(NSData* data) {
|
|||||||
pictureOptions.saveToPhotoAlbum = [[command argumentAtIndex:9 withDefault:@(NO)] boolValue];
|
pictureOptions.saveToPhotoAlbum = [[command argumentAtIndex:9 withDefault:@(NO)] boolValue];
|
||||||
pictureOptions.popoverOptions = [command argumentAtIndex:10 withDefault:nil];
|
pictureOptions.popoverOptions = [command argumentAtIndex:10 withDefault:nil];
|
||||||
pictureOptions.cameraDirection = [[command argumentAtIndex:11 withDefault:@(UIImagePickerControllerCameraDeviceRear)] unsignedIntegerValue];
|
pictureOptions.cameraDirection = [[command argumentAtIndex:11 withDefault:@(UIImagePickerControllerCameraDeviceRear)] unsignedIntegerValue];
|
||||||
|
|
||||||
pictureOptions.popoverSupported = NO;
|
pictureOptions.popoverSupported = NO;
|
||||||
pictureOptions.usesGeolocation = NO;
|
pictureOptions.usesGeolocation = NO;
|
||||||
|
|
||||||
return pictureOptions;
|
return pictureOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +109,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
- (NSURL*) urlTransformer:(NSURL*)url
|
- (NSURL*) urlTransformer:(NSURL*)url
|
||||||
{
|
{
|
||||||
NSURL* urlToTransform = url;
|
NSURL* urlToTransform = url;
|
||||||
|
|
||||||
// for backwards compatibility - we check if this property is there
|
// for backwards compatibility - we check if this property is there
|
||||||
SEL sel = NSSelectorFromString(@"urlTransformer");
|
SEL sel = NSSelectorFromString(@"urlTransformer");
|
||||||
if ([self.commandDelegate respondsToSelector:sel]) {
|
if ([self.commandDelegate respondsToSelector:sel]) {
|
||||||
@@ -120,7 +120,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
urlToTransform = urlTransformer(url);
|
urlToTransform = urlTransformer(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return urlToTransform;
|
return urlToTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,16 +139,16 @@ static NSString* toBase64(NSData* data) {
|
|||||||
- (void)takePicture:(CDVInvokedUrlCommand*)command
|
- (void)takePicture:(CDVInvokedUrlCommand*)command
|
||||||
{
|
{
|
||||||
self.hasPendingOperation = YES;
|
self.hasPendingOperation = YES;
|
||||||
|
|
||||||
__weak CDVCamera* weakSelf = self;
|
__weak CDVCamera* weakSelf = self;
|
||||||
|
|
||||||
[self.commandDelegate runInBackground:^{
|
[self.commandDelegate runInBackground:^{
|
||||||
|
|
||||||
CDVPictureOptions* pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
|
CDVPictureOptions* pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
|
||||||
pictureOptions.popoverSupported = [weakSelf popoverSupported];
|
pictureOptions.popoverSupported = [weakSelf popoverSupported];
|
||||||
pictureOptions.usesGeolocation = [weakSelf usesGeolocation];
|
pictureOptions.usesGeolocation = [weakSelf usesGeolocation];
|
||||||
pictureOptions.cropToSize = NO;
|
pictureOptions.cropToSize = NO;
|
||||||
|
|
||||||
BOOL hasCamera = [UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType];
|
BOOL hasCamera = [UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType];
|
||||||
if (!hasCamera) {
|
if (!hasCamera) {
|
||||||
NSLog(@"Camera.getPicture: source type %lu not available.", (unsigned long)pictureOptions.sourceType);
|
NSLog(@"Camera.getPicture: source type %lu not available.", (unsigned long)pictureOptions.sourceType);
|
||||||
@@ -184,12 +184,12 @@ static NSString* toBase64(NSData* data) {
|
|||||||
|
|
||||||
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
|
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
|
||||||
weakSelf.pickerController = cameraPicker;
|
weakSelf.pickerController = cameraPicker;
|
||||||
|
|
||||||
cameraPicker.delegate = weakSelf;
|
cameraPicker.delegate = weakSelf;
|
||||||
cameraPicker.callbackId = command.callbackId;
|
cameraPicker.callbackId = command.callbackId;
|
||||||
// we need to capture this state for memory warnings that dealloc this object
|
// we need to capture this state for memory warnings that dealloc this object
|
||||||
cameraPicker.webView = weakSelf.webView;
|
cameraPicker.webView = weakSelf.webView;
|
||||||
|
|
||||||
// Perform UI operations on the main thread
|
// Perform UI operations on the main thread
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
// If a popover is already open, close it; we only want one at a time.
|
// If a popover is already open, close it; we only want one at a time.
|
||||||
@@ -206,6 +206,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
[weakSelf displayPopover:pictureOptions.popoverOptions];
|
[weakSelf displayPopover:pictureOptions.popoverOptions];
|
||||||
weakSelf.hasPendingOperation = NO;
|
weakSelf.hasPendingOperation = NO;
|
||||||
} else {
|
} else {
|
||||||
|
cameraPicker.modalPresentationStyle = UIModalPresentationCurrentContext;
|
||||||
[weakSelf.viewController presentViewController:cameraPicker animated:YES completion:^{
|
[weakSelf.viewController presentViewController:cameraPicker animated:YES completion:^{
|
||||||
weakSelf.hasPendingOperation = NO;
|
weakSelf.hasPendingOperation = NO;
|
||||||
}];
|
}];
|
||||||
@@ -291,7 +292,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
{
|
{
|
||||||
if([navigationController isKindOfClass:[UIImagePickerController class]]){
|
if([navigationController isKindOfClass:[UIImagePickerController class]]){
|
||||||
UIImagePickerController* cameraPicker = (UIImagePickerController*)navigationController;
|
UIImagePickerController* cameraPicker = (UIImagePickerController*)navigationController;
|
||||||
|
|
||||||
if(![cameraPicker.mediaTypes containsObject:(NSString*)kUTTypeImage]){
|
if(![cameraPicker.mediaTypes containsObject:(NSString*)kUTTypeImage]){
|
||||||
[viewController.navigationItem setTitle:NSLocalizedString(@"Videos", nil)];
|
[viewController.navigationItem setTitle:NSLocalizedString(@"Videos", nil)];
|
||||||
}
|
}
|
||||||
@@ -351,7 +352,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options
|
- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options
|
||||||
{
|
{
|
||||||
NSData* data = nil;
|
NSData* data = nil;
|
||||||
|
|
||||||
switch (options.encodingType) {
|
switch (options.encodingType) {
|
||||||
case EncodingTypePNG:
|
case EncodingTypePNG:
|
||||||
data = UIImagePNGRepresentation(image);
|
data = UIImagePNGRepresentation(image);
|
||||||
@@ -364,18 +365,18 @@ static NSString* toBase64(NSData* data) {
|
|||||||
} else {
|
} else {
|
||||||
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
|
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.usesGeolocation) {
|
if (options.usesGeolocation) {
|
||||||
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
|
||||||
if (controllerMetadata) {
|
if (controllerMetadata) {
|
||||||
self.data = data;
|
self.data = data;
|
||||||
self.metadata = [[NSMutableDictionary alloc] init];
|
self.metadata = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
|
||||||
if (EXIFDictionary) {
|
if (EXIFDictionary) {
|
||||||
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsAtLeastiOSVersion(@"8.0")) {
|
if (IsAtLeastiOSVersion(@"8.0")) {
|
||||||
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
|
||||||
}
|
}
|
||||||
@@ -387,7 +388,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,13 +397,13 @@ static NSString* toBase64(NSData* data) {
|
|||||||
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
|
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
|
||||||
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by Apple (vs [NSFileManager defaultManager]) to be threadsafe
|
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by Apple (vs [NSFileManager defaultManager]) to be threadsafe
|
||||||
NSString* filePath;
|
NSString* filePath;
|
||||||
|
|
||||||
// generate unique file name
|
// generate unique file name
|
||||||
int i = 1;
|
int i = 1;
|
||||||
do {
|
do {
|
||||||
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, extension];
|
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, extension];
|
||||||
} while ([fileMgr fileExistsAtPath:filePath]);
|
} while ([fileMgr fileExistsAtPath:filePath]);
|
||||||
|
|
||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -415,13 +416,13 @@ static NSString* toBase64(NSData* data) {
|
|||||||
} else {
|
} else {
|
||||||
image = [info objectForKey:UIImagePickerControllerOriginalImage];
|
image = [info objectForKey:UIImagePickerControllerOriginalImage];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.correctOrientation) {
|
if (options.correctOrientation) {
|
||||||
image = [image imageCorrectedForCaptureOrientation];
|
image = [image imageCorrectedForCaptureOrientation];
|
||||||
}
|
}
|
||||||
|
|
||||||
UIImage* scaledImage = nil;
|
UIImage* scaledImage = nil;
|
||||||
|
|
||||||
if ((options.targetSize.width > 0) && (options.targetSize.height > 0)) {
|
if ((options.targetSize.width > 0) && (options.targetSize.height > 0)) {
|
||||||
// if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
|
// if cropToSize, resize image and crop to target size, otherwise resize to fit target without cropping
|
||||||
if (options.cropToSize) {
|
if (options.cropToSize) {
|
||||||
@@ -430,7 +431,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
|
scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (scaledImage == nil ? image : scaledImage);
|
return (scaledImage == nil ? image : scaledImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -472,11 +473,11 @@ static NSString* toBase64(NSData* data) {
|
|||||||
image = [self retrieveImage:info options:options];
|
image = [self retrieveImage:info options:options];
|
||||||
NSData* data = [self processImage:image info:info options:options];
|
NSData* data = [self processImage:image info:info options:options];
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|
||||||
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
|
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
|
||||||
NSString* filePath = [self tempFilePath:extension];
|
NSString* filePath = [self tempFilePath:extension];
|
||||||
NSError* err = nil;
|
NSError* err = nil;
|
||||||
|
|
||||||
// save file
|
// save file
|
||||||
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||||
@@ -498,7 +499,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (saveToPhotoAlbum && image) {
|
if (saveToPhotoAlbum && image) {
|
||||||
ALAssetsLibrary* library = [ALAssetsLibrary new];
|
ALAssetsLibrary* library = [ALAssetsLibrary new];
|
||||||
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil];
|
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil];
|
||||||
@@ -517,10 +518,10 @@ static NSString* toBase64(NSData* data) {
|
|||||||
{
|
{
|
||||||
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
|
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
|
||||||
__weak CDVCamera* weakSelf = self;
|
__weak CDVCamera* weakSelf = self;
|
||||||
|
|
||||||
dispatch_block_t invoke = ^(void) {
|
dispatch_block_t invoke = ^(void) {
|
||||||
__block CDVPluginResult* result = nil;
|
__block CDVPluginResult* result = nil;
|
||||||
|
|
||||||
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
|
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
|
||||||
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
|
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
|
||||||
[weakSelf resultForImage:cameraPicker.pictureOptions info:info completion:^(CDVPluginResult* res) {
|
[weakSelf resultForImage:cameraPicker.pictureOptions info:info completion:^(CDVPluginResult* res) {
|
||||||
@@ -538,7 +539,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
weakSelf.pickerController = nil;
|
weakSelf.pickerController = nil;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (cameraPicker.pictureOptions.popoverSupported && (cameraPicker.pickerPopoverController != nil)) {
|
if (cameraPicker.pictureOptions.popoverSupported && (cameraPicker.pickerPopoverController != nil)) {
|
||||||
[cameraPicker.pickerPopoverController dismissPopoverAnimated:YES];
|
[cameraPicker.pickerPopoverController dismissPopoverAnimated:YES];
|
||||||
cameraPicker.pickerPopoverController.delegate = nil;
|
cameraPicker.pickerPopoverController.delegate = nil;
|
||||||
@@ -561,7 +562,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
{
|
{
|
||||||
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
|
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
|
||||||
__weak CDVCamera* weakSelf = self;
|
__weak CDVCamera* weakSelf = self;
|
||||||
|
|
||||||
dispatch_block_t invoke = ^ (void) {
|
dispatch_block_t invoke = ^ (void) {
|
||||||
CDVPluginResult* result;
|
CDVPluginResult* result;
|
||||||
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != ALAuthorizationStatusAuthorized) {
|
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != ALAuthorizationStatusAuthorized) {
|
||||||
@@ -569,12 +570,12 @@ static NSString* toBase64(NSData* data) {
|
|||||||
} else if (picker.sourceType != UIImagePickerControllerSourceTypeCamera && [ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusAuthorized) {
|
} else if (picker.sourceType != UIImagePickerControllerSourceTypeCamera && [ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusAuthorized) {
|
||||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"];
|
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"];
|
||||||
} else {
|
} else {
|
||||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"];
|
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No Image Selected"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
|
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
|
||||||
|
|
||||||
weakSelf.hasPendingOperation = NO;
|
weakSelf.hasPendingOperation = NO;
|
||||||
weakSelf.pickerController = nil;
|
weakSelf.pickerController = nil;
|
||||||
};
|
};
|
||||||
@@ -587,11 +588,11 @@ static NSString* toBase64(NSData* data) {
|
|||||||
if (locationManager != nil) {
|
if (locationManager != nil) {
|
||||||
return locationManager;
|
return locationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
locationManager = [[CLLocationManager alloc] init];
|
locationManager = [[CLLocationManager alloc] init];
|
||||||
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
|
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
|
||||||
[locationManager setDelegate:self];
|
[locationManager setDelegate:self];
|
||||||
|
|
||||||
return locationManager;
|
return locationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,15 +601,15 @@ static NSString* toBase64(NSData* data) {
|
|||||||
if (locationManager == nil) {
|
if (locationManager == nil) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[self.locationManager stopUpdatingLocation];
|
[self.locationManager stopUpdatingLocation];
|
||||||
self.locationManager = nil;
|
self.locationManager = nil;
|
||||||
|
|
||||||
NSMutableDictionary *GPSDictionary = [[NSMutableDictionary dictionary] init];
|
NSMutableDictionary *GPSDictionary = [[NSMutableDictionary dictionary] init];
|
||||||
|
|
||||||
CLLocationDegrees latitude = newLocation.coordinate.latitude;
|
CLLocationDegrees latitude = newLocation.coordinate.latitude;
|
||||||
CLLocationDegrees longitude = newLocation.coordinate.longitude;
|
CLLocationDegrees longitude = newLocation.coordinate.longitude;
|
||||||
|
|
||||||
// latitude
|
// latitude
|
||||||
if (latitude < 0.0) {
|
if (latitude < 0.0) {
|
||||||
latitude = latitude * -1.0f;
|
latitude = latitude * -1.0f;
|
||||||
@@ -617,7 +618,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
[GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
|
[GPSDictionary setObject:@"N" forKey:(NSString*)kCGImagePropertyGPSLatitudeRef];
|
||||||
}
|
}
|
||||||
[GPSDictionary setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
|
[GPSDictionary setObject:[NSNumber numberWithFloat:latitude] forKey:(NSString*)kCGImagePropertyGPSLatitude];
|
||||||
|
|
||||||
// longitude
|
// longitude
|
||||||
if (longitude < 0.0) {
|
if (longitude < 0.0) {
|
||||||
longitude = longitude * -1.0f;
|
longitude = longitude * -1.0f;
|
||||||
@@ -627,7 +628,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
[GPSDictionary setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
|
[GPSDictionary setObject:@"E" forKey:(NSString*)kCGImagePropertyGPSLongitudeRef];
|
||||||
}
|
}
|
||||||
[GPSDictionary setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString*)kCGImagePropertyGPSLongitude];
|
[GPSDictionary setObject:[NSNumber numberWithFloat:longitude] forKey:(NSString*)kCGImagePropertyGPSLongitude];
|
||||||
|
|
||||||
// altitude
|
// altitude
|
||||||
CGFloat altitude = newLocation.altitude;
|
CGFloat altitude = newLocation.altitude;
|
||||||
if (!isnan(altitude)){
|
if (!isnan(altitude)){
|
||||||
@@ -639,7 +640,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
}
|
}
|
||||||
[GPSDictionary setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
|
[GPSDictionary setObject:[NSNumber numberWithFloat:altitude] forKey:(NSString *)kCGImagePropertyGPSAltitude];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Time and date
|
// Time and date
|
||||||
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
||||||
[formatter setDateFormat:@"HH:mm:ss.SSSSSS"];
|
[formatter setDateFormat:@"HH:mm:ss.SSSSSS"];
|
||||||
@@ -647,7 +648,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp];
|
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSTimeStamp];
|
||||||
[formatter setDateFormat:@"yyyy:MM:dd"];
|
[formatter setDateFormat:@"yyyy:MM:dd"];
|
||||||
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp];
|
[GPSDictionary setObject:[formatter stringFromDate:newLocation.timestamp] forKey:(NSString *)kCGImagePropertyGPSDateStamp];
|
||||||
|
|
||||||
[self.metadata setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
|
[self.metadata setObject:GPSDictionary forKey:(NSString *)kCGImagePropertyGPSDictionary];
|
||||||
[self imagePickerControllerReturnImageResult];
|
[self imagePickerControllerReturnImageResult];
|
||||||
}
|
}
|
||||||
@@ -660,7 +661,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
|
|
||||||
[self.locationManager stopUpdatingLocation];
|
[self.locationManager stopUpdatingLocation];
|
||||||
self.locationManager = nil;
|
self.locationManager = nil;
|
||||||
|
|
||||||
[self imagePickerControllerReturnImageResult];
|
[self imagePickerControllerReturnImageResult];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,26 +669,26 @@ static NSString* toBase64(NSData* data) {
|
|||||||
{
|
{
|
||||||
CDVPictureOptions* options = self.pickerController.pictureOptions;
|
CDVPictureOptions* options = self.pickerController.pictureOptions;
|
||||||
CDVPluginResult* result = nil;
|
CDVPluginResult* result = nil;
|
||||||
|
|
||||||
if (self.metadata) {
|
if (self.metadata) {
|
||||||
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL);
|
CGImageSourceRef sourceImage = CGImageSourceCreateWithData((__bridge CFDataRef)self.data, NULL);
|
||||||
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
|
CFStringRef sourceType = CGImageSourceGetType(sourceImage);
|
||||||
|
|
||||||
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL);
|
CGImageDestinationRef destinationImage = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)self.data, sourceType, 1, NULL);
|
||||||
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
|
CGImageDestinationAddImageFromSource(destinationImage, sourceImage, 0, (__bridge CFDictionaryRef)self.metadata);
|
||||||
CGImageDestinationFinalize(destinationImage);
|
CGImageDestinationFinalize(destinationImage);
|
||||||
|
|
||||||
CFRelease(sourceImage);
|
CFRelease(sourceImage);
|
||||||
CFRelease(destinationImage);
|
CFRelease(destinationImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (options.destinationType) {
|
switch (options.destinationType) {
|
||||||
case DestinationTypeFileUri:
|
case DestinationTypeFileUri:
|
||||||
{
|
{
|
||||||
NSError* err = nil;
|
NSError* err = nil;
|
||||||
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
|
NSString* extension = self.pickerController.pictureOptions.encodingType == EncodingTypePNG ? @"png":@"jpg";
|
||||||
NSString* filePath = [self tempFilePath:extension];
|
NSString* filePath = [self tempFilePath:extension];
|
||||||
|
|
||||||
// save file
|
// save file
|
||||||
if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
if (![self.data writeToFile:filePath options:NSAtomicWrite error:&err]) {
|
||||||
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
|
||||||
@@ -706,16 +707,16 @@ static NSString* toBase64(NSData* data) {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
[self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
|
[self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
|
||||||
}
|
}
|
||||||
|
|
||||||
self.hasPendingOperation = NO;
|
self.hasPendingOperation = NO;
|
||||||
self.pickerController = nil;
|
self.pickerController = nil;
|
||||||
self.data = nil;
|
self.data = nil;
|
||||||
self.metadata = nil;
|
self.metadata = nil;
|
||||||
|
|
||||||
if (options.saveToPhotoAlbum) {
|
if (options.saveToPhotoAlbum) {
|
||||||
ALAssetsLibrary *library = [ALAssetsLibrary new];
|
ALAssetsLibrary *library = [ALAssetsLibrary new];
|
||||||
[library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil];
|
[library writeImageDataToSavedPhotosAlbum:self.data metadata:self.metadata completionBlock:nil];
|
||||||
@@ -735,14 +736,14 @@ static NSString* toBase64(NSData* data) {
|
|||||||
{
|
{
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated
|
- (void)viewWillAppear:(BOOL)animated
|
||||||
{
|
{
|
||||||
SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
|
SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
|
||||||
if ([self respondsToSelector:sel]) {
|
if ([self respondsToSelector:sel]) {
|
||||||
[self performSelector:sel withObject:nil afterDelay:0];
|
[self performSelector:sel withObject:nil afterDelay:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
[super viewWillAppear:animated];
|
[super viewWillAppear:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -752,7 +753,7 @@ static NSString* toBase64(NSData* data) {
|
|||||||
cameraPicker.pictureOptions = pictureOptions;
|
cameraPicker.pictureOptions = pictureOptions;
|
||||||
cameraPicker.sourceType = pictureOptions.sourceType;
|
cameraPicker.sourceType = pictureOptions.sourceType;
|
||||||
cameraPicker.allowsEditing = pictureOptions.allowsEditing;
|
cameraPicker.allowsEditing = pictureOptions.allowsEditing;
|
||||||
|
|
||||||
if (cameraPicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
if (cameraPicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
|
||||||
// We only allow taking pictures (no video) in this API.
|
// We only allow taking pictures (no video) in this API.
|
||||||
cameraPicker.mediaTypes = @[(NSString*)kUTTypeImage];
|
cameraPicker.mediaTypes = @[(NSString*)kUTTypeImage];
|
||||||
@@ -764,8 +765,8 @@ static NSString* toBase64(NSData* data) {
|
|||||||
NSArray* mediaArray = @[(NSString*)(pictureOptions.mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage)];
|
NSArray* mediaArray = @[(NSString*)(pictureOptions.mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage)];
|
||||||
cameraPicker.mediaTypes = mediaArray;
|
cameraPicker.mediaTypes = mediaArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cameraPicker;
|
return cameraPicker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#import <Quartz/Quartz.h>
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#import <Cordova/CDVPlugin.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
enum CDVDestinationType {
|
||||||
|
DestinationTypeDataUrl = 0,
|
||||||
|
DestinationTypeFileUri,
|
||||||
|
DestinationTypeNativeUri
|
||||||
|
};
|
||||||
|
typedef NSUInteger CDVDestinationType;
|
||||||
|
|
||||||
|
enum CDVSourceType {
|
||||||
|
SourceTypePhotoLibrary = 0,
|
||||||
|
SourceTypeCamera,
|
||||||
|
SourceTypePhotoAlbum
|
||||||
|
};
|
||||||
|
typedef NSUInteger CDVSourceType;
|
||||||
|
|
||||||
|
enum CDVEncodingType {
|
||||||
|
EncodingTypeJPEG = 0,
|
||||||
|
EncodingTypePNG
|
||||||
|
};
|
||||||
|
typedef NSUInteger CDVEncodingType;
|
||||||
|
|
||||||
|
enum CDVMediaType {
|
||||||
|
MediaTypePicture = 0,
|
||||||
|
MediaTypeVideo,
|
||||||
|
MediaTypeAll
|
||||||
|
};
|
||||||
|
typedef NSUInteger CDVMediaType;
|
||||||
|
|
||||||
|
|
||||||
|
// ======================================================================= //
|
||||||
|
|
||||||
|
|
||||||
|
@interface CDVPictureOptions : NSObject
|
||||||
|
|
||||||
|
@property (strong) NSNumber *quality;
|
||||||
|
@property (assign) CDVDestinationType destinationType;
|
||||||
|
@property (assign) CDVSourceType sourceType;
|
||||||
|
@property (assign) CGSize targetSize;
|
||||||
|
@property (assign) CDVEncodingType encodingType;
|
||||||
|
@property (assign) CDVMediaType mediaType;
|
||||||
|
@property (assign) BOOL allowsEditing;
|
||||||
|
@property (assign) BOOL correctOrientation;
|
||||||
|
@property (assign) BOOL saveToPhotoAlbum;
|
||||||
|
|
||||||
|
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand *)command;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
// ======================================================================= //
|
||||||
|
|
||||||
|
|
||||||
|
@interface CDVCamera : CDVPlugin
|
||||||
|
|
||||||
|
- (void)takePicture:(CDVInvokedUrlCommand *)command;
|
||||||
|
- (void)cleanup:(CDVInvokedUrlCommand *)command;
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
/*
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
#import "CDVCamera.h"
|
||||||
|
|
||||||
|
|
||||||
|
@implementation CDVPictureOptions
|
||||||
|
|
||||||
|
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand*)command {
|
||||||
|
CDVPictureOptions *pictureOptions = [[CDVPictureOptions alloc] init];
|
||||||
|
|
||||||
|
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
|
||||||
|
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
|
||||||
|
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(SourceTypeCamera)] unsignedIntegerValue];
|
||||||
|
|
||||||
|
NSNumber *targetWidth = [command argumentAtIndex:3 withDefault:nil];
|
||||||
|
NSNumber *targetHeight = [command argumentAtIndex:4 withDefault:nil];
|
||||||
|
pictureOptions.targetSize = CGSizeMake(0, 0);
|
||||||
|
if ((targetWidth != nil) && (targetHeight != nil)) {
|
||||||
|
pictureOptions.targetSize = CGSizeMake([targetWidth floatValue], [targetHeight floatValue]);
|
||||||
|
}
|
||||||
|
|
||||||
|
pictureOptions.encodingType = [[command argumentAtIndex:5 withDefault:@(EncodingTypeJPEG)] unsignedIntegerValue];
|
||||||
|
pictureOptions.mediaType = [[command argumentAtIndex:6 withDefault:@(MediaTypePicture)] unsignedIntegerValue];
|
||||||
|
pictureOptions.allowsEditing = [[command argumentAtIndex:7 withDefault:@(NO)] boolValue];
|
||||||
|
pictureOptions.correctOrientation = [[command argumentAtIndex:8 withDefault:@(NO)] boolValue];
|
||||||
|
pictureOptions.saveToPhotoAlbum = [[command argumentAtIndex:9 withDefault:@(NO)] boolValue];
|
||||||
|
|
||||||
|
return pictureOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
// ======================================================================= //
|
||||||
|
|
||||||
|
|
||||||
|
@implementation CDVCamera
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Static array that stores the temporary created files allowing to delete them when calling navigator.camera.cleanup(...)
|
||||||
|
*/
|
||||||
|
static NSMutableArray *cleanUpFiles;
|
||||||
|
|
||||||
|
+ (void)initialize {
|
||||||
|
cleanUpFiles = [NSMutableArray array];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)takePicture:(CDVInvokedUrlCommand *)command {
|
||||||
|
CDVPictureOptions *pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
|
||||||
|
if (pictureOptions.sourceType == SourceTypeCamera) {
|
||||||
|
[self takePictureFromCamera:command withOptions:pictureOptions];
|
||||||
|
} else {
|
||||||
|
[self takePictureFromFile:command withOptions:pictureOptions];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)cleanup:(CDVInvokedUrlCommand*)command {
|
||||||
|
[self.commandDelegate runInBackground:^{
|
||||||
|
if (cleanUpFiles.count > 0) {
|
||||||
|
for (int i=0; i<cleanUpFiles.count; i++) {
|
||||||
|
NSString *path = [cleanUpFiles objectAtIndex:i];
|
||||||
|
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
[cleanUpFiles removeAllObjects];
|
||||||
|
|
||||||
|
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
|
||||||
|
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Camera
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Takes a picture from the iSight camera using the default OS dialog.
|
||||||
|
@see https://developer.apple.com/documentation/quartz/ikpicturetaker
|
||||||
|
*/
|
||||||
|
- (void)takePictureFromCamera:(CDVInvokedUrlCommand *)command withOptions:(CDVPictureOptions *)pictureOptions {
|
||||||
|
IKPictureTaker *pictureTaker = [IKPictureTaker pictureTaker];
|
||||||
|
[pictureTaker setValue:[NSNumber numberWithBool:YES] forKey:IKPictureTakerAllowsVideoCaptureKey];
|
||||||
|
[pictureTaker setValue:[NSNumber numberWithBool:NO] forKey:IKPictureTakerAllowsFileChoosingKey];
|
||||||
|
[pictureTaker setValue:[NSNumber numberWithBool:pictureOptions.allowsEditing] forKey:IKPictureTakerShowEffectsKey];
|
||||||
|
[pictureTaker setValue:[NSNumber numberWithBool:pictureOptions.allowsEditing] forKey:IKPictureTakerAllowsEditingKey];
|
||||||
|
|
||||||
|
NSDictionary *contextInfo = @{ @"command": command, @"pictureOptions" : pictureOptions};
|
||||||
|
[pictureTaker beginPictureTakerSheetForWindow:self.viewController.contentView.window withDelegate:self didEndSelector:@selector(pictureTakerDidEnd:returnCode:contextInfo:) contextInfo:(void *)CFBridgingRetain(contextInfo)];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)pictureTakerDidEnd:(IKPictureTaker *)pictureTaker returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo {
|
||||||
|
if (returnCode == NSOKButton) {
|
||||||
|
NSDictionary *contextInfoDictionary = (NSDictionary *)CFBridgingRelease(contextInfo);
|
||||||
|
CDVInvokedUrlCommand *command = [contextInfoDictionary valueForKey:@"command"];
|
||||||
|
CDVPictureOptions *pictureOptions = [contextInfoDictionary valueForKey:@"pictureOptions"];
|
||||||
|
|
||||||
|
[self returnImage:pictureTaker.outputImage command:command options:pictureOptions];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - File
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Allows to select an image or video using the system native dialog.
|
||||||
|
*/
|
||||||
|
- (void)takePictureFromFile:(CDVInvokedUrlCommand *)command withOptions:(CDVPictureOptions *)pictureOptions {
|
||||||
|
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
|
||||||
|
openPanel.canChooseFiles = YES;
|
||||||
|
openPanel.canChooseDirectories = NO;
|
||||||
|
openPanel.canCreateDirectories = YES;
|
||||||
|
openPanel.allowsMultipleSelection = NO;
|
||||||
|
|
||||||
|
NSMutableArray *allowedTypes = [NSMutableArray array];
|
||||||
|
if (pictureOptions.mediaType == MediaTypePicture || pictureOptions.mediaType == MediaTypeAll) {
|
||||||
|
[allowedTypes addObjectsFromArray:[NSImage imageTypes]];
|
||||||
|
}
|
||||||
|
if (pictureOptions.mediaType == MediaTypeVideo || pictureOptions.mediaType == MediaTypeAll) {
|
||||||
|
[allowedTypes addObjectsFromArray:@[(NSString *)kUTTypeMovie]];
|
||||||
|
}
|
||||||
|
[openPanel setAllowedFileTypes:allowedTypes];
|
||||||
|
|
||||||
|
[openPanel beginSheetModalForWindow:self.viewController.contentView.window completionHandler:^(NSInteger result) {
|
||||||
|
if (result == NSOKButton) {
|
||||||
|
NSURL *fileURL = [openPanel.URLs objectAtIndex:0];
|
||||||
|
|
||||||
|
if ([self fileIsImage:fileURL]) {
|
||||||
|
NSImage *image = [[NSImage alloc] initWithContentsOfFile:fileURL.path];
|
||||||
|
[self returnImage:image command:command options:pictureOptions];
|
||||||
|
} else {
|
||||||
|
if (pictureOptions.destinationType == DestinationTypeDataUrl) {
|
||||||
|
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Camera.DestinationType.DATA_URL is only available with image files"];
|
||||||
|
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||||
|
} else {
|
||||||
|
[self returnUri:fileURL.path command:command options:pictureOptions];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma mark - Common
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns to JavaScript a URI.
|
||||||
|
Called when Camera.DestinationType.FILE_URI or Camera.DestinationType.NATIVE_URI.
|
||||||
|
*/
|
||||||
|
- (void)returnUri:(NSString *)path command:(CDVInvokedUrlCommand *)command options:(CDVPictureOptions *)pictureOptions {
|
||||||
|
NSString *protocol = (pictureOptions.destinationType == DestinationTypeFileUri) ? @"file://" : @"";
|
||||||
|
NSString *uri = [NSString stringWithFormat:@"%@%@", protocol, path];
|
||||||
|
|
||||||
|
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:uri];
|
||||||
|
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns to JavaScript a base64 encoded image.
|
||||||
|
Called when Camera.DestinationType.DATA_URL.
|
||||||
|
*/
|
||||||
|
- (void)returnImage:(NSImage *)image command:(CDVInvokedUrlCommand *)command options:(CDVPictureOptions *)pictureOptions {
|
||||||
|
[self.commandDelegate runInBackground:^{
|
||||||
|
NSData *processedImageData = [self processImage:image options:pictureOptions];
|
||||||
|
|
||||||
|
if (pictureOptions.destinationType == DestinationTypeDataUrl) {
|
||||||
|
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[processedImageData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed]];
|
||||||
|
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
|
||||||
|
} else {
|
||||||
|
NSString *tempFilePath = [self uniqueImageName:pictureOptions];
|
||||||
|
[processedImageData writeToFile:tempFilePath atomically:YES];
|
||||||
|
[cleanUpFiles addObject:tempFilePath];
|
||||||
|
[self returnUri:tempFilePath command:command options:pictureOptions];
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Top level method to apply the size and quality required changes to an image.
|
||||||
|
*/
|
||||||
|
- (NSData *)processImage:(NSImage *)image options:(CDVPictureOptions *)pictureOptions {
|
||||||
|
NSImage *sourceImage = image;
|
||||||
|
if (pictureOptions.targetSize.width > 0 && pictureOptions.targetSize.height > 0) {
|
||||||
|
sourceImage = [self resizeImage:sourceImage toSize:pictureOptions.targetSize];
|
||||||
|
}
|
||||||
|
|
||||||
|
CGImageRef cgRef = [sourceImage CGImageForProposedRect:NULL context:nil hints:nil];
|
||||||
|
NSBitmapImageRep *imageRepresentation = [[NSBitmapImageRep alloc] initWithCGImage:cgRef];
|
||||||
|
|
||||||
|
NSData *data = (pictureOptions.encodingType == EncodingTypeJPEG)
|
||||||
|
? [imageRepresentation representationUsingType:NSJPEGFileType properties:@{NSImageCompressionFactor: [NSNumber numberWithFloat:pictureOptions.quality.floatValue/100.f]}]
|
||||||
|
: [imageRepresentation representationUsingType:NSPNGFileType properties:@{NSImageCompressionFactor: @1.0}];
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Auxiliar method to resize an image.
|
||||||
|
*/
|
||||||
|
- (NSImage *)resizeImage:(NSImage *)image toSize:(CGSize)newSize {
|
||||||
|
CGFloat aspectWidth = newSize.width / image.size.width;
|
||||||
|
CGFloat aspectHeight = newSize.height / image.size.height;
|
||||||
|
CGFloat aspectRatio = MIN(aspectWidth, aspectHeight);
|
||||||
|
|
||||||
|
CGSize scaledSize = NSMakeSize(image.size.width*aspectRatio, image.size.height*aspectRatio);
|
||||||
|
|
||||||
|
NSImage *smallImage = [[NSImage alloc] initWithSize: scaledSize];
|
||||||
|
[smallImage lockFocus];
|
||||||
|
[image setSize: scaledSize];
|
||||||
|
[[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh];
|
||||||
|
[image drawAtPoint:NSZeroPoint fromRect:CGRectMake(0, 0, scaledSize.width, scaledSize.height) operation:NSCompositeCopy fraction:1.0];
|
||||||
|
[smallImage unlockFocus];
|
||||||
|
return smallImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Auxiliar method to know if a given file is an image or not.
|
||||||
|
*/
|
||||||
|
- (BOOL)fileIsImage:(NSURL *)fileURL {
|
||||||
|
NSString *type;
|
||||||
|
BOOL isImage = NO;
|
||||||
|
|
||||||
|
if ([fileURL getResourceValue:&type forKey:NSURLTypeIdentifierKey error:nil]) {
|
||||||
|
isImage = [[NSImage imageTypes] containsObject:type];
|
||||||
|
}
|
||||||
|
|
||||||
|
return isImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Auxiliar method that generates an unique filename for an image in the temporary directory.
|
||||||
|
*/
|
||||||
|
- (NSString *)uniqueImageName:(CDVPictureOptions *)pictureOptions {
|
||||||
|
NSString *tempDir = NSTemporaryDirectory();
|
||||||
|
NSString *guid = [[NSProcessInfo processInfo] globallyUniqueString] ;
|
||||||
|
NSString *extension = (pictureOptions.encodingType == EncodingTypeJPEG) ? @"jpeg" : @"png";
|
||||||
|
NSString *uniqueFileName = [NSString stringWithFormat:@"%@%@.%@", tempDir, guid, extension];
|
||||||
|
return uniqueFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* Copyright 2013 Canonical Ltd.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
import QtQuick 2.0
|
|
||||||
import QtMultimedia 5.0
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
property string shootImagePath: "shoot.png"
|
|
||||||
function isSuffix(str, suffix) {
|
|
||||||
return String(str).substr(String(str).length - suffix.length) == suffix
|
|
||||||
}
|
|
||||||
|
|
||||||
id: ui
|
|
||||||
color: "#252423"
|
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
Camera {
|
|
||||||
objectName: "camera"
|
|
||||||
id: camera
|
|
||||||
onError: {
|
|
||||||
console.log(errorString);
|
|
||||||
}
|
|
||||||
videoRecorder.audioBitRate: 128000
|
|
||||||
imageCapture {
|
|
||||||
onImageSaved: {
|
|
||||||
root.exec("Camera", "onImageSaved", [path]);
|
|
||||||
ui.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VideoOutput {
|
|
||||||
id: output
|
|
||||||
source: camera
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
width: parent.width
|
|
||||||
height: shootButton.height
|
|
||||||
BorderImage {
|
|
||||||
id: leftBackground
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.right: middle.left
|
|
||||||
anchors.topMargin: units.dp(2)
|
|
||||||
anchors.bottomMargin: units.dp(2)
|
|
||||||
source: "toolbar-left.png"
|
|
||||||
Image {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: parent.iconSpacing
|
|
||||||
source: "back.png"
|
|
||||||
width: units.gu(6)
|
|
||||||
height: units.gu(5)
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
root.exec("Camera", "cancel");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BorderImage {
|
|
||||||
id: middle
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
height: shootButton.height + units.gu(1)
|
|
||||||
width: shootButton.width
|
|
||||||
source: "toolbar-middle.png"
|
|
||||||
Image {
|
|
||||||
id: shootButton
|
|
||||||
width: units.gu(8)
|
|
||||||
height: width
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
source: shootImagePath
|
|
||||||
MouseArea {
|
|
||||||
anchors.fill: parent
|
|
||||||
onClicked: {
|
|
||||||
camera.imageCapture.captureToLocation(ui.parent.plugin('Camera').generateLocation("jpg"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BorderImage {
|
|
||||||
id: rightBackground
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.left: middle.right
|
|
||||||
anchors.topMargin: units.dp(2)
|
|
||||||
anchors.bottomMargin: units.dp(2)
|
|
||||||
source: "toolbar-right.png"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB |
@@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "camera.h"
|
|
||||||
#include <cordova.h>
|
|
||||||
|
|
||||||
#include <QCameraViewfinder>
|
|
||||||
#include <QCameraImageCapture>
|
|
||||||
#include <QGraphicsObject>
|
|
||||||
#include <QCloseEvent>
|
|
||||||
#include <QQuickItem>
|
|
||||||
|
|
||||||
const char code[] = "\
|
|
||||||
var component, object; \
|
|
||||||
function createObject() { \
|
|
||||||
component = Qt.createComponent(%1); \
|
|
||||||
if (component.status == Component.Ready) \
|
|
||||||
finishCreation(); \
|
|
||||||
else \
|
|
||||||
component.statusChanged.connect(finishCreation); \
|
|
||||||
} \
|
|
||||||
function finishCreation() { \
|
|
||||||
CordovaWrapper.global.cameraPluginWidget = component.createObject(root, \
|
|
||||||
{root: root, cordova: cordova}); \
|
|
||||||
} \
|
|
||||||
createObject()";
|
|
||||||
|
|
||||||
|
|
||||||
Camera::Camera(Cordova *cordova):
|
|
||||||
CPlugin(cordova),
|
|
||||||
_lastScId(0),
|
|
||||||
_lastEcId(0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Camera::preprocessImage(QString &path) {
|
|
||||||
bool convertToPNG = (*_options.find("encodingType")).toInt() == Camera::PNG;
|
|
||||||
int quality = (*_options.find("quality")).toInt();
|
|
||||||
int width = (*_options.find("targetWidth")).toInt();
|
|
||||||
int height = (*_options.find("targetHeight")).toInt();
|
|
||||||
|
|
||||||
QImage image(path);
|
|
||||||
if (width <= 0)
|
|
||||||
width = image.width();
|
|
||||||
if (height <= 0)
|
|
||||||
height = image.height();
|
|
||||||
image = image.scaled(width, height, Qt::KeepAspectRatio, Qt::SmoothTransformation);
|
|
||||||
|
|
||||||
QFile oldImage(path);
|
|
||||||
QTemporaryFile newImage;
|
|
||||||
|
|
||||||
const char *type;
|
|
||||||
if (convertToPNG) {
|
|
||||||
path = generateLocation("png");
|
|
||||||
type = "png";
|
|
||||||
} else {
|
|
||||||
path = generateLocation("jpg");
|
|
||||||
type = "jpg";
|
|
||||||
}
|
|
||||||
|
|
||||||
image.save(path, type, quality);
|
|
||||||
|
|
||||||
oldImage.remove();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::onImageSaved(QString path) {
|
|
||||||
bool dataURL = _options.find("destinationType")->toInt() == Camera::DATA_URL;
|
|
||||||
|
|
||||||
QString cbParams;
|
|
||||||
if (preprocessImage(path)) {
|
|
||||||
QString absolutePath = QFileInfo(path).absoluteFilePath();
|
|
||||||
if (dataURL) {
|
|
||||||
QFile image(absolutePath);
|
|
||||||
image.open(QIODevice::ReadOnly);
|
|
||||||
QByteArray content = image.readAll().toBase64();
|
|
||||||
cbParams = QString("\"%1\"").arg(content.data());
|
|
||||||
image.remove();
|
|
||||||
} else {
|
|
||||||
cbParams = CordovaInternal::format(QString("file://localhost") + absolutePath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->callback(_lastScId, cbParams);
|
|
||||||
|
|
||||||
_lastEcId = _lastScId = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::takePicture(int scId, int ecId, int quality, int destinationType, int/*sourceType*/, int targetWidth, int targetHeight, int encodingType,
|
|
||||||
int/*mediaType*/, bool/*allowEdit*/, bool/*correctOrientation*/, bool/*saveToPhotoAlbum*/, const QVariantMap &/*popoverOptions*/, int/*cameraDirection*/) {
|
|
||||||
if (_camera.isNull()) {
|
|
||||||
_camera = QSharedPointer<QCamera>(new QCamera());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((_lastScId || _lastEcId) && (_lastScId != scId && _lastEcId != ecId)) || !_camera->isAvailable() || _camera->lockStatus() != QCamera::Unlocked) {
|
|
||||||
this->cb(_lastEcId, "Device is busy");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_options.clear();
|
|
||||||
_options.insert("quality", quality);
|
|
||||||
_options.insert("destinationType", destinationType);
|
|
||||||
_options.insert("targetWidth", targetWidth);
|
|
||||||
_options.insert("targetHeight", targetHeight);
|
|
||||||
_options.insert("encodingType", encodingType);
|
|
||||||
|
|
||||||
_lastScId = scId;
|
|
||||||
_lastEcId = ecId;
|
|
||||||
|
|
||||||
QString path = m_cordova->get_app_dir() + "/../qml/CaptureWidget.qml";
|
|
||||||
|
|
||||||
// TODO: relative url
|
|
||||||
QString qml = QString(code).arg(CordovaInternal::format(path));
|
|
||||||
m_cordova->execQML(qml);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Camera::cancel() {
|
|
||||||
m_cordova->execQML("CordovaWrapper.global.cameraPluginWidget.destroy()");
|
|
||||||
this->cb(_lastEcId, "canceled");
|
|
||||||
|
|
||||||
_lastEcId = _lastScId = 0;
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef CAMERA_H
|
|
||||||
#define CAMERA_H
|
|
||||||
|
|
||||||
#include <cplugin.h>
|
|
||||||
|
|
||||||
#include <QtCore>
|
|
||||||
#include <QQuickView>
|
|
||||||
#include <QCamera>
|
|
||||||
#include <QtMultimediaWidgets/QCameraViewfinder>
|
|
||||||
#include <QCameraImageCapture>
|
|
||||||
|
|
||||||
class Camera: public CPlugin {
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit Camera(Cordova *cordova);
|
|
||||||
|
|
||||||
virtual const QString fullName() override {
|
|
||||||
return Camera::fullID();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual const QString shortName() override {
|
|
||||||
return "Camera";
|
|
||||||
}
|
|
||||||
|
|
||||||
static const QString fullID() {
|
|
||||||
return "Camera";
|
|
||||||
}
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void takePicture(int scId, int ecId, int quality, int destinationType, int/*sourceType*/, int targetWidth, int targetHeight, int encodingType,
|
|
||||||
int/*mediaType*/, bool/*allowEdit*/, bool/*correctOrientation*/, bool/*saveToPhotoAlbum*/, const QVariantMap &popoverOptions, int cameraDirection);
|
|
||||||
void cancel();
|
|
||||||
|
|
||||||
void onImageSaved(QString path);
|
|
||||||
|
|
||||||
QString generateLocation(const QString &extension) {
|
|
||||||
int i = 1;
|
|
||||||
for (;;++i) {
|
|
||||||
QString path = QString("%1/.local/share/%2/persistent/%3.%4").arg(QDir::homePath())
|
|
||||||
.arg(QCoreApplication::applicationName()).arg(i).arg(extension);
|
|
||||||
|
|
||||||
if (!QFileInfo(path).exists())
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
bool preprocessImage(QString &path);
|
|
||||||
|
|
||||||
int _lastScId;
|
|
||||||
int _lastEcId;
|
|
||||||
QSharedPointer<QCamera> _camera;
|
|
||||||
|
|
||||||
QVariantMap _options;
|
|
||||||
protected:
|
|
||||||
enum DestinationType {
|
|
||||||
DATA_URL = 0,
|
|
||||||
FILE_URI = 1
|
|
||||||
};
|
|
||||||
enum EncodingType {
|
|
||||||
JPEG = 0,
|
|
||||||
PNG = 1
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // CAMERA_H
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
+185
-200
@@ -19,13 +19,10 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*jshint unused:true, undef:true, browser:true */
|
/* global Windows:true, URL:true, module:true, require:true, WinJS:true */
|
||||||
/*global Windows:true, URL:true, module:true, require:true, WinJS:true */
|
|
||||||
|
|
||||||
|
|
||||||
var Camera = require('./Camera');
|
var Camera = require('./Camera');
|
||||||
|
|
||||||
|
|
||||||
var getAppData = function () {
|
var getAppData = function () {
|
||||||
return Windows.Storage.ApplicationData.current;
|
return Windows.Storage.ApplicationData.current;
|
||||||
};
|
};
|
||||||
@@ -58,7 +55,7 @@ module.exports = {
|
|||||||
takePicture: function (successCallback, errorCallback, args) {
|
takePicture: function (successCallback, errorCallback, args) {
|
||||||
var sourceType = args[2];
|
var sourceType = args[2];
|
||||||
|
|
||||||
if (sourceType != Camera.PictureSourceType.CAMERA) {
|
if (sourceType !== Camera.PictureSourceType.CAMERA) {
|
||||||
takePictureFromFile(successCallback, errorCallback, args);
|
takePictureFromFile(successCallback, errorCallback, args);
|
||||||
} else {
|
} else {
|
||||||
takePictureFromCamera(successCallback, errorCallback, args);
|
takePictureFromCamera(successCallback, errorCallback, args);
|
||||||
@@ -67,8 +64,8 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/apps/ff462087(v=vs.105).aspx
|
// https://msdn.microsoft.com/en-us/library/windows/apps/ff462087(v=vs.105).aspx
|
||||||
var windowsVideoContainers = [".avi", ".flv", ".asx", ".asf", ".mov", ".mp4", ".mpg", ".rm", ".srt", ".swf", ".wmv", ".vob"];
|
var windowsVideoContainers = ['.avi', '.flv', '.asx', '.asf', '.mov', '.mp4', '.mpg', '.rm', '.srt', '.swf', '.wmv', '.vob'];
|
||||||
var windowsPhoneVideoContainers = [".avi", ".3gp", ".3g2", ".wmv", ".3gp", ".3g2", ".mp4", ".m4v"];
|
var windowsPhoneVideoContainers = ['.avi', '.3gp', '.3g2', '.wmv', '.3gp', '.3g2', '.mp4', '.m4v'];
|
||||||
|
|
||||||
// Default aspect ratio 1.78 (16:9 hd video standard)
|
// Default aspect ratio 1.78 (16:9 hd video standard)
|
||||||
var DEFAULT_ASPECT_RATIO = '1.8';
|
var DEFAULT_ASPECT_RATIO = '1.8';
|
||||||
@@ -77,16 +74,16 @@ var DEFAULT_ASPECT_RATIO = '1.8';
|
|||||||
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
var HIGHEST_POSSIBLE_Z_INDEX = 2147483647;
|
||||||
|
|
||||||
// Resize method
|
// Resize method
|
||||||
function resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
|
function resizeImage (successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
|
||||||
var tempPhotoFileName = "";
|
var tempPhotoFileName = '';
|
||||||
var targetContentType = "";
|
var targetContentType = '';
|
||||||
|
|
||||||
if (encodingType == Camera.EncodingType.PNG) {
|
if (encodingType === Camera.EncodingType.PNG) {
|
||||||
tempPhotoFileName = "camera_cordova_temp_return.png";
|
tempPhotoFileName = 'camera_cordova_temp_return.png';
|
||||||
targetContentType = "image/png";
|
targetContentType = 'image/png';
|
||||||
} else {
|
} else {
|
||||||
tempPhotoFileName = "camera_cordova_temp_return.jpg";
|
tempPhotoFileName = 'camera_cordova_temp_return.jpg';
|
||||||
targetContentType = "image/jpeg";
|
targetContentType = 'image/jpeg';
|
||||||
}
|
}
|
||||||
|
|
||||||
var storageFolder = getAppData().localFolder;
|
var storageFolder = getAppData().localFolder;
|
||||||
@@ -94,12 +91,12 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
|
|||||||
.then(function (storageFile) {
|
.then(function (storageFile) {
|
||||||
return fileIO.readBufferAsync(storageFile);
|
return fileIO.readBufferAsync(storageFile);
|
||||||
})
|
})
|
||||||
.then(function(buffer) {
|
.then(function (buffer) {
|
||||||
var strBase64 = encodeToBase64String(buffer);
|
var strBase64 = encodeToBase64String(buffer);
|
||||||
var imageData = "data:" + file.contentType + ";base64," + strBase64;
|
var imageData = 'data:' + file.contentType + ';base64,' + strBase64;
|
||||||
var image = new Image();
|
var image = new Image(); /* eslint no-undef : 0 */
|
||||||
image.src = imageData;
|
image.src = imageData;
|
||||||
image.onload = function() {
|
image.onload = function () {
|
||||||
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
|
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
|
||||||
var imageWidth = ratio * this.width;
|
var imageWidth = ratio * this.width;
|
||||||
var imageHeight = ratio * this.height;
|
var imageHeight = ratio * this.height;
|
||||||
@@ -110,7 +107,7 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
|
|||||||
canvas.width = imageWidth;
|
canvas.width = imageWidth;
|
||||||
canvas.height = imageHeight;
|
canvas.height = imageHeight;
|
||||||
|
|
||||||
canvas.getContext("2d").drawImage(this, 0, 0, imageWidth, imageHeight);
|
canvas.getContext('2d').drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||||
|
|
||||||
var fileContent = canvas.toDataURL(targetContentType).split(',')[1];
|
var fileContent = canvas.toDataURL(targetContentType).split(',')[1];
|
||||||
|
|
||||||
@@ -123,26 +120,25 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
|
|||||||
return fileIO.writeBufferAsync(storagefile, content);
|
return fileIO.writeBufferAsync(storagefile, content);
|
||||||
})
|
})
|
||||||
.done(function () {
|
.done(function () {
|
||||||
successCallback("ms-appdata:///local/" + storageFileName);
|
successCallback('ms-appdata:///local/' + storageFileName);
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.done(null, function(err) {
|
.done(null, function (err) {
|
||||||
errorCallback(err);
|
errorCallback(err);
|
||||||
}
|
});
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Because of asynchronous method, so let the successCallback be called in it.
|
// Because of asynchronous method, so let the successCallback be called in it.
|
||||||
function resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight) {
|
function resizeImageBase64 (successCallback, errorCallback, file, targetWidth, targetHeight) {
|
||||||
fileIO.readBufferAsync(file).done( function(buffer) {
|
fileIO.readBufferAsync(file).done(function (buffer) {
|
||||||
var strBase64 = encodeToBase64String(buffer);
|
var strBase64 = encodeToBase64String(buffer);
|
||||||
var imageData = "data:" + file.contentType + ";base64," + strBase64;
|
var imageData = 'data:' + file.contentType + ';base64,' + strBase64;
|
||||||
|
|
||||||
var image = new Image();
|
var image = new Image(); /* eslint no-undef : 0 */
|
||||||
image.src = imageData;
|
image.src = imageData;
|
||||||
|
|
||||||
image.onload = function() {
|
image.onload = function () {
|
||||||
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
|
var ratio = Math.min(targetWidth / this.width, targetHeight / this.height);
|
||||||
var imageWidth = ratio * this.width;
|
var imageWidth = ratio * this.width;
|
||||||
var imageHeight = ratio * this.height;
|
var imageHeight = ratio * this.height;
|
||||||
@@ -151,21 +147,21 @@ function resizeImageBase64(successCallback, errorCallback, file, targetWidth, ta
|
|||||||
canvas.width = imageWidth;
|
canvas.width = imageWidth;
|
||||||
canvas.height = imageHeight;
|
canvas.height = imageHeight;
|
||||||
|
|
||||||
var ctx = canvas.getContext("2d");
|
var ctx = canvas.getContext('2d');
|
||||||
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
|
ctx.drawImage(this, 0, 0, imageWidth, imageHeight);
|
||||||
|
|
||||||
// The resized file ready for upload
|
// The resized file ready for upload
|
||||||
var finalFile = canvas.toDataURL(file.contentType);
|
var finalFile = canvas.toDataURL(file.contentType);
|
||||||
|
|
||||||
// Remove the prefix such as "data:" + contentType + ";base64," , in order to meet the Cordova API.
|
// Remove the prefix such as "data:" + contentType + ";base64," , in order to meet the Cordova API.
|
||||||
var arr = finalFile.split(",");
|
var arr = finalFile.split(',');
|
||||||
var newStr = finalFile.substr(arr[0].length + 1);
|
var newStr = finalFile.substr(arr[0].length + 1);
|
||||||
successCallback(newStr);
|
successCallback(newStr);
|
||||||
};
|
};
|
||||||
}, function(err) { errorCallback(err); });
|
}, function (err) { errorCallback(err); });
|
||||||
}
|
}
|
||||||
|
|
||||||
function takePictureFromFile(successCallback, errorCallback, args) {
|
function takePictureFromFile (successCallback, errorCallback, args) {
|
||||||
// Detect Windows Phone
|
// Detect Windows Phone
|
||||||
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
|
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
|
||||||
takePictureFromFileWP(successCallback, errorCallback, args);
|
takePictureFromFileWP(successCallback, errorCallback, args);
|
||||||
@@ -174,94 +170,87 @@ function takePictureFromFile(successCallback, errorCallback, args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function takePictureFromFileWP(successCallback, errorCallback, args) {
|
function takePictureFromFileWP (successCallback, errorCallback, args) {
|
||||||
var mediaType = args[6],
|
var mediaType = args[6];
|
||||||
destinationType = args[1],
|
var destinationType = args[1];
|
||||||
targetWidth = args[3],
|
var targetWidth = args[3];
|
||||||
targetHeight = args[4],
|
var targetHeight = args[4];
|
||||||
encodingType = args[5];
|
var encodingType = args[5];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Need to add and remove an event listener to catch activation state
|
Need to add and remove an event listener to catch activation state
|
||||||
Using FileOpenPicker will suspend the app and it's required to catch the PickSingleFileAndContinue
|
Using FileOpenPicker will suspend the app and it's required to catch the PickSingleFileAndContinue
|
||||||
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
|
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
|
||||||
*/
|
*/
|
||||||
var filePickerActivationHandler = function(eventArgs) {
|
var filePickerActivationHandler = function (eventArgs) {
|
||||||
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickFileContinuation) {
|
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickFileContinuation) {
|
||||||
var file = eventArgs.files[0];
|
var file = eventArgs.files[0];
|
||||||
if (!file) {
|
if (!file) {
|
||||||
errorCallback("User didn't choose a file.");
|
errorCallback("User didn't choose a file.");
|
||||||
webUIApp.removeEventListener("activated", filePickerActivationHandler);
|
webUIApp.removeEventListener('activated', filePickerActivationHandler);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
|
if (destinationType === Camera.DestinationType.FILE_URI || destinationType === Camera.DestinationType.NATIVE_URI) {
|
||||||
if (targetHeight > 0 && targetWidth > 0) {
|
if (targetHeight > 0 && targetWidth > 0) {
|
||||||
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var storageFolder = getAppData().localFolder;
|
var storageFolder = getAppData().localFolder;
|
||||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
|
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
|
||||||
if(destinationType == Camera.DestinationType.NATIVE_URI) {
|
if (destinationType === Camera.DestinationType.NATIVE_URI) {
|
||||||
successCallback("ms-appdata:///local/" + storageFile.name);
|
successCallback('ms-appdata:///local/' + storageFile.name);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
successCallback(URL.createObjectURL(storageFile));
|
successCallback(URL.createObjectURL(storageFile));
|
||||||
}
|
}
|
||||||
}, function () {
|
}, function () {
|
||||||
errorCallback("Can't access localStorage folder.");
|
errorCallback("Can't access localStorage folder.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (targetHeight > 0 && targetWidth > 0) {
|
if (targetHeight > 0 && targetWidth > 0) {
|
||||||
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
|
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
|
||||||
} else {
|
} else {
|
||||||
fileIO.readBufferAsync(file).done(function (buffer) {
|
fileIO.readBufferAsync(file).done(function (buffer) {
|
||||||
var strBase64 =encodeToBase64String(buffer);
|
var strBase64 = encodeToBase64String(buffer);
|
||||||
successCallback(strBase64);
|
successCallback(strBase64);
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
webUIApp.removeEventListener("activated", filePickerActivationHandler);
|
webUIApp.removeEventListener('activated', filePickerActivationHandler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||||
if (mediaType == Camera.MediaType.PICTURE) {
|
if (mediaType === Camera.MediaType.PICTURE) {
|
||||||
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
|
fileOpenPicker.fileTypeFilter.replaceAll(['.png', '.jpg', '.jpeg']);
|
||||||
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
||||||
}
|
} else if (mediaType === Camera.MediaType.VIDEO) {
|
||||||
else if (mediaType == Camera.MediaType.VIDEO) {
|
|
||||||
fileOpenPicker.fileTypeFilter.replaceAll(windowsPhoneVideoContainers);
|
fileOpenPicker.fileTypeFilter.replaceAll(windowsPhoneVideoContainers);
|
||||||
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
|
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
|
||||||
}
|
} else {
|
||||||
else {
|
fileOpenPicker.fileTypeFilter.replaceAll(['*']);
|
||||||
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
|
|
||||||
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
|
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
|
||||||
}
|
}
|
||||||
|
|
||||||
webUIApp.addEventListener("activated", filePickerActivationHandler);
|
webUIApp.addEventListener('activated', filePickerActivationHandler);
|
||||||
fileOpenPicker.pickSingleFileAndContinue();
|
fileOpenPicker.pickSingleFileAndContinue();
|
||||||
}
|
}
|
||||||
|
|
||||||
function takePictureFromFileWindows(successCallback, errorCallback, args) {
|
function takePictureFromFileWindows (successCallback, errorCallback, args) {
|
||||||
var mediaType = args[6],
|
var mediaType = args[6];
|
||||||
destinationType = args[1],
|
var destinationType = args[1];
|
||||||
targetWidth = args[3],
|
var targetWidth = args[3];
|
||||||
targetHeight = args[4],
|
var targetHeight = args[4];
|
||||||
encodingType = args[5];
|
var encodingType = args[5];
|
||||||
|
|
||||||
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
|
||||||
if (mediaType == Camera.MediaType.PICTURE) {
|
if (mediaType === Camera.MediaType.PICTURE) {
|
||||||
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
|
fileOpenPicker.fileTypeFilter.replaceAll(['.png', '.jpg', '.jpeg']);
|
||||||
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
||||||
}
|
} else if (mediaType === Camera.MediaType.VIDEO) {
|
||||||
else if (mediaType == Camera.MediaType.VIDEO) {
|
|
||||||
fileOpenPicker.fileTypeFilter.replaceAll(windowsVideoContainers);
|
fileOpenPicker.fileTypeFilter.replaceAll(windowsVideoContainers);
|
||||||
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
|
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
|
||||||
}
|
} else {
|
||||||
else {
|
fileOpenPicker.fileTypeFilter.replaceAll(['*']);
|
||||||
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
|
|
||||||
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
|
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,30 +259,27 @@ function takePictureFromFileWindows(successCallback, errorCallback, args) {
|
|||||||
errorCallback("User didn't choose a file.");
|
errorCallback("User didn't choose a file.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
|
if (destinationType === Camera.DestinationType.FILE_URI || destinationType === Camera.DestinationType.NATIVE_URI) {
|
||||||
if (targetHeight > 0 && targetWidth > 0) {
|
if (targetHeight > 0 && targetWidth > 0) {
|
||||||
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
var storageFolder = getAppData().localFolder;
|
var storageFolder = getAppData().localFolder;
|
||||||
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
|
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
|
||||||
if(destinationType == Camera.DestinationType.NATIVE_URI) {
|
if (destinationType === Camera.DestinationType.NATIVE_URI) {
|
||||||
successCallback("ms-appdata:///local/" + storageFile.name);
|
successCallback('ms-appdata:///local/' + storageFile.name);
|
||||||
}
|
} else {
|
||||||
else {
|
successCallback(URL.createObjectURL(storageFile));
|
||||||
successCallback(URL.createObjectURL(storageFile));
|
}
|
||||||
}
|
|
||||||
}, function () {
|
}, function () {
|
||||||
errorCallback("Can't access localStorage folder.");
|
errorCallback("Can't access localStorage folder.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
if (targetHeight > 0 && targetWidth > 0) {
|
if (targetHeight > 0 && targetWidth > 0) {
|
||||||
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
|
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
|
||||||
} else {
|
} else {
|
||||||
fileIO.readBufferAsync(file).done(function (buffer) {
|
fileIO.readBufferAsync(file).done(function (buffer) {
|
||||||
var strBase64 =encodeToBase64String(buffer);
|
var strBase64 = encodeToBase64String(buffer);
|
||||||
successCallback(strBase64);
|
successCallback(strBase64);
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
@@ -303,7 +289,7 @@ function takePictureFromFileWindows(successCallback, errorCallback, args) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function takePictureFromCamera(successCallback, errorCallback, args) {
|
function takePictureFromCamera (successCallback, errorCallback, args) {
|
||||||
// Check if necessary API available
|
// Check if necessary API available
|
||||||
if (!Windows.Media.Capture.CameraCaptureUI) {
|
if (!Windows.Media.Capture.CameraCaptureUI) {
|
||||||
takePictureFromCameraWP(successCallback, errorCallback, args);
|
takePictureFromCameraWP(successCallback, errorCallback, args);
|
||||||
@@ -312,42 +298,42 @@ function takePictureFromCamera(successCallback, errorCallback, args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
function takePictureFromCameraWP (successCallback, errorCallback, args) {
|
||||||
// We are running on WP8.1 which lacks CameraCaptureUI class
|
// We are running on WP8.1 which lacks CameraCaptureUI class
|
||||||
// so we need to use MediaCapture class instead and implement custom UI for camera
|
// so we need to use MediaCapture class instead and implement custom UI for camera
|
||||||
var destinationType = args[1],
|
var destinationType = args[1];
|
||||||
targetWidth = args[3],
|
var targetWidth = args[3];
|
||||||
targetHeight = args[4],
|
var targetHeight = args[4];
|
||||||
encodingType = args[5],
|
var encodingType = args[5];
|
||||||
saveToPhotoAlbum = args[9],
|
var saveToPhotoAlbum = args[9];
|
||||||
cameraDirection = args[11],
|
var cameraDirection = args[11];
|
||||||
capturePreview = null,
|
var capturePreview = null;
|
||||||
cameraCaptureButton = null,
|
var cameraCaptureButton = null;
|
||||||
cameraCancelButton = null,
|
var cameraCancelButton = null;
|
||||||
capture = null,
|
var capture = null;
|
||||||
captureSettings = null,
|
var captureSettings = null;
|
||||||
CaptureNS = Windows.Media.Capture,
|
var CaptureNS = Windows.Media.Capture;
|
||||||
sensor = null;
|
var sensor = null;
|
||||||
|
|
||||||
function createCameraUI() {
|
function createCameraUI () {
|
||||||
// create style for take and cancel buttons
|
// 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;";
|
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;';
|
||||||
|
|
||||||
// Create fullscreen preview
|
// Create fullscreen preview
|
||||||
// z-order style element for capturePreview and cameraCancelButton elts
|
// z-order style element for capturePreview and cameraCancelButton elts
|
||||||
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
|
// is necessary to avoid overriding by another page elements, -1 sometimes is not enough
|
||||||
capturePreview = document.createElement("video");
|
capturePreview = document.createElement('video');
|
||||||
capturePreview.style.cssText = "position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: " + (HIGHEST_POSSIBLE_Z_INDEX - 1) + ";";
|
capturePreview.style.cssText = 'position: fixed; left: 0; top: 0; width: 100%; height: 100%; z-index: ' + (HIGHEST_POSSIBLE_Z_INDEX - 1) + ';';
|
||||||
|
|
||||||
// Create capture button
|
// Create capture button
|
||||||
cameraCaptureButton = document.createElement("button");
|
cameraCaptureButton = document.createElement('button');
|
||||||
cameraCaptureButton.innerText = "Take";
|
cameraCaptureButton.innerText = 'Take';
|
||||||
cameraCaptureButton.style.cssText = buttonStyle + "position: fixed; left: 0; bottom: 0; margin: 20px; z-index: " + HIGHEST_POSSIBLE_Z_INDEX + ";";
|
cameraCaptureButton.style.cssText = buttonStyle + 'position: fixed; left: 0; bottom: 0; margin: 20px; z-index: ' + HIGHEST_POSSIBLE_Z_INDEX + ';';
|
||||||
|
|
||||||
// Create cancel button
|
// Create cancel button
|
||||||
cameraCancelButton = document.createElement("button");
|
cameraCancelButton = document.createElement('button');
|
||||||
cameraCancelButton.innerText = "Cancel";
|
cameraCancelButton.innerText = 'Cancel';
|
||||||
cameraCancelButton.style.cssText = buttonStyle + "position: fixed; right: 0; bottom: 0; margin: 20px; z-index: " + HIGHEST_POSSIBLE_Z_INDEX + ";";
|
cameraCancelButton.style.cssText = buttonStyle + 'position: fixed; right: 0; bottom: 0; margin: 20px; z-index: ' + HIGHEST_POSSIBLE_Z_INDEX + ';';
|
||||||
|
|
||||||
capture = new CaptureNS.MediaCapture();
|
capture = new CaptureNS.MediaCapture();
|
||||||
|
|
||||||
@@ -355,21 +341,21 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
|
captureSettings.streamingCaptureMode = CaptureNS.StreamingCaptureMode.video;
|
||||||
}
|
}
|
||||||
|
|
||||||
function continueVideoOnFocus() {
|
function continueVideoOnFocus () {
|
||||||
// if preview is defined it would be stuck, play it
|
// if preview is defined it would be stuck, play it
|
||||||
if (capturePreview) {
|
if (capturePreview) {
|
||||||
capturePreview.play();
|
capturePreview.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function startCameraPreview() {
|
function startCameraPreview () {
|
||||||
// Search for available camera devices
|
// Search for available camera devices
|
||||||
// This is necessary to detect which camera (front or back) we should use
|
// This is necessary to detect which camera (front or back) we should use
|
||||||
var DeviceEnum = Windows.Devices.Enumeration;
|
var DeviceEnum = Windows.Devices.Enumeration;
|
||||||
var expectedPanel = cameraDirection === 1 ? DeviceEnum.Panel.front : DeviceEnum.Panel.back;
|
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
|
// 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);
|
window.addEventListener('focus', continueVideoOnFocus);
|
||||||
|
|
||||||
DeviceEnum.DeviceInformation.findAllAsync(DeviceEnum.DeviceClass.videoCapture).then(function (devices) {
|
DeviceEnum.DeviceInformation.findAllAsync(DeviceEnum.DeviceClass.videoCapture).then(function (devices) {
|
||||||
if (devices.length <= 0) {
|
if (devices.length <= 0) {
|
||||||
@@ -378,8 +364,8 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
devices.forEach(function(currDev) {
|
devices.forEach(function (currDev) {
|
||||||
if (currDev.enclosureLocation.panel && currDev.enclosureLocation.panel == expectedPanel) {
|
if (currDev.enclosureLocation.panel && currDev.enclosureLocation.panel === expectedPanel) {
|
||||||
captureSettings.videoDeviceId = currDev.id;
|
captureSettings.videoDeviceId = currDev.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -418,7 +404,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
// Bind events to controls
|
// Bind events to controls
|
||||||
sensor = Windows.Devices.Sensors.SimpleOrientationSensor.getDefault();
|
sensor = Windows.Devices.Sensors.SimpleOrientationSensor.getDefault();
|
||||||
if (sensor !== null) {
|
if (sensor !== null) {
|
||||||
sensor.addEventListener("orientationchanged", onOrientationChange);
|
sensor.addEventListener('orientationchanged', onOrientationChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
// add click events to capture and cancel buttons
|
// add click events to capture and cancel buttons
|
||||||
@@ -459,7 +445,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function destroyCameraPreview() {
|
function destroyCameraPreview () {
|
||||||
// If sensor is available, remove event listener
|
// If sensor is available, remove event listener
|
||||||
if (sensor !== null) {
|
if (sensor !== null) {
|
||||||
sensor.removeEventListener('orientationchanged', onOrientationChange);
|
sensor.removeEventListener('orientationchanged', onOrientationChange);
|
||||||
@@ -474,7 +460,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
cameraCancelButton.removeEventListener('click', onCameraCancelButtonClick);
|
cameraCancelButton.removeEventListener('click', onCameraCancelButtonClick);
|
||||||
|
|
||||||
// Remove the focus event handler
|
// Remove the focus event handler
|
||||||
window.removeEventListener("focus", continueVideoOnFocus);
|
window.removeEventListener('focus', continueVideoOnFocus);
|
||||||
|
|
||||||
// Remove elements
|
// Remove elements
|
||||||
[capturePreview, cameraCaptureButton, cameraCancelButton].forEach(function (elem) {
|
[capturePreview, cameraCaptureButton, cameraCancelButton].forEach(function (elem) {
|
||||||
@@ -490,13 +476,13 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function captureAction() {
|
function captureAction () {
|
||||||
|
|
||||||
var encodingProperties,
|
var encodingProperties;
|
||||||
fileName,
|
var fileName;
|
||||||
tempFolder = getAppData().temporaryFolder;
|
var tempFolder = getAppData().temporaryFolder;
|
||||||
|
|
||||||
if (encodingType == Camera.EncodingType.PNG) {
|
if (encodingType === Camera.EncodingType.PNG) {
|
||||||
fileName = 'photo.png';
|
fileName = 'photo.png';
|
||||||
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createPng();
|
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createPng();
|
||||||
} else {
|
} else {
|
||||||
@@ -505,41 +491,41 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tempFolder.createFileAsync(fileName, OptUnique)
|
tempFolder.createFileAsync(fileName, OptUnique)
|
||||||
.then(function(tempCapturedFile) {
|
.then(function (tempCapturedFile) {
|
||||||
return new WinJS.Promise(function (complete) {
|
return new WinJS.Promise(function (complete) {
|
||||||
var photoStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
var photoStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
||||||
var finalStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
var finalStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
|
||||||
capture.capturePhotoToStreamAsync(encodingProperties, photoStream)
|
capture.capturePhotoToStreamAsync(encodingProperties, photoStream)
|
||||||
.then(function() {
|
.then(function () {
|
||||||
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
|
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
|
||||||
})
|
})
|
||||||
.then(function(dec) {
|
.then(function (dec) {
|
||||||
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
|
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
|
||||||
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
|
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
|
||||||
})
|
})
|
||||||
.then(function(enc) {
|
.then(function (enc) {
|
||||||
// We need to rotate the photo wrt sensor orientation
|
// We need to rotate the photo wrt sensor orientation
|
||||||
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
|
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
|
||||||
return enc.flushAsync();
|
return enc.flushAsync();
|
||||||
})
|
})
|
||||||
.then(function() {
|
.then(function () {
|
||||||
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
|
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
|
||||||
})
|
})
|
||||||
.then(function(fileStream) {
|
.then(function (fileStream) {
|
||||||
return Windows.Storage.Streams.RandomAccessStream.copyAndCloseAsync(finalStream, fileStream);
|
return Windows.Storage.Streams.RandomAccessStream.copyAndCloseAsync(finalStream, fileStream);
|
||||||
})
|
})
|
||||||
.done(function() {
|
.done(function () {
|
||||||
photoStream.close();
|
photoStream.close();
|
||||||
finalStream.close();
|
finalStream.close();
|
||||||
complete(tempCapturedFile);
|
complete(tempCapturedFile);
|
||||||
}, function() {
|
}, function () {
|
||||||
photoStream.close();
|
photoStream.close();
|
||||||
finalStream.close();
|
finalStream.close();
|
||||||
throw new Error("An error has occured while capturing the photo.");
|
throw new Error('An error has occured while capturing the photo.');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.done(function(capturedFile) {
|
.done(function (capturedFile) {
|
||||||
destroyCameraPreview();
|
destroyCameraPreview();
|
||||||
savePhoto(capturedFile, {
|
savePhoto(capturedFile, {
|
||||||
destinationType: destinationType,
|
destinationType: destinationType,
|
||||||
@@ -548,13 +534,13 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
encodingType: encodingType,
|
encodingType: encodingType,
|
||||||
saveToPhotoAlbum: saveToPhotoAlbum
|
saveToPhotoAlbum: saveToPhotoAlbum
|
||||||
}, successCallback, errorCallback);
|
}, successCallback, errorCallback);
|
||||||
}, function(err) {
|
}, function (err) {
|
||||||
destroyCameraPreview();
|
destroyCameraPreview();
|
||||||
errorCallback(err);
|
errorCallback(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAspectRatios(capture) {
|
function getAspectRatios (capture) {
|
||||||
var videoDeviceController = capture.videoDeviceController;
|
var videoDeviceController = capture.videoDeviceController;
|
||||||
var photoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo).map(function (element) {
|
var photoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo).map(function (element) {
|
||||||
return (element.width / element.height).toFixed(1);
|
return (element.width / element.height).toFixed(1);
|
||||||
@@ -583,7 +569,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function setAspectRatio(capture, aspect) {
|
function setAspectRatio (capture, aspect) {
|
||||||
// Max photo resolution with desired aspect ratio
|
// Max photo resolution with desired aspect ratio
|
||||||
var videoDeviceController = capture.videoDeviceController;
|
var videoDeviceController = capture.videoDeviceController;
|
||||||
var photoResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo)
|
var photoResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo)
|
||||||
@@ -624,7 +610,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
/**
|
/**
|
||||||
* When Capture button is clicked, try to capture a picture and return
|
* When Capture button is clicked, try to capture a picture and return
|
||||||
*/
|
*/
|
||||||
function onCameraCaptureButtonClick() {
|
function onCameraCaptureButtonClick () {
|
||||||
// Make sure user can't click more than once
|
// Make sure user can't click more than once
|
||||||
if (this.getAttribute('clicked') === '1') {
|
if (this.getAttribute('clicked') === '1') {
|
||||||
return false;
|
return false;
|
||||||
@@ -637,7 +623,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
/**
|
/**
|
||||||
* When Cancel button is clicked, destroy camera preview and return with error callback
|
* When Cancel button is clicked, destroy camera preview and return with error callback
|
||||||
*/
|
*/
|
||||||
function onCameraCancelButtonClick() {
|
function onCameraCancelButtonClick () {
|
||||||
// Make sure user can't click more than once
|
// Make sure user can't click more than once
|
||||||
if (this.getAttribute('clicked') === '1') {
|
if (this.getAttribute('clicked') === '1') {
|
||||||
return false;
|
return false;
|
||||||
@@ -652,7 +638,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
* When the phone orientation change, get the event and change camera preview rotation
|
* When the phone orientation change, get the event and change camera preview rotation
|
||||||
* @param {Object} e - SimpleOrientationSensorOrientationChangedEventArgs
|
* @param {Object} e - SimpleOrientationSensorOrientationChangedEventArgs
|
||||||
*/
|
*/
|
||||||
function onOrientationChange(e) {
|
function onOrientationChange (e) {
|
||||||
setPreviewRotation(e.orientation);
|
setPreviewRotation(e.orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -662,29 +648,29 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
|
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
|
||||||
* @return {number} - Windows.Media.Capture.VideoRotation
|
* @return {number} - Windows.Media.Capture.VideoRotation
|
||||||
*/
|
*/
|
||||||
function orientationToRotation(orientation) {
|
function orientationToRotation (orientation) {
|
||||||
// VideoRotation enumerable and BitmapRotation enumerable have the same values
|
// 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.media.capture.videorotation.aspx
|
||||||
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaprotation.aspx
|
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaprotation.aspx
|
||||||
|
|
||||||
switch (orientation) {
|
switch (orientation) {
|
||||||
// portrait
|
// portrait
|
||||||
case Windows.Devices.Sensors.SimpleOrientation.notRotated:
|
case Windows.Devices.Sensors.SimpleOrientation.notRotated:
|
||||||
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
||||||
// landscape
|
// landscape
|
||||||
case Windows.Devices.Sensors.SimpleOrientation.rotated90DegreesCounterclockwise:
|
case Windows.Devices.Sensors.SimpleOrientation.rotated90DegreesCounterclockwise:
|
||||||
return Windows.Media.Capture.VideoRotation.none;
|
return Windows.Media.Capture.VideoRotation.none;
|
||||||
// portrait-flipped (not supported by WinPhone Apps)
|
// portrait-flipped (not supported by WinPhone Apps)
|
||||||
case Windows.Devices.Sensors.SimpleOrientation.rotated180DegreesCounterclockwise:
|
case Windows.Devices.Sensors.SimpleOrientation.rotated180DegreesCounterclockwise:
|
||||||
// Falling back to portrait default
|
// Falling back to portrait default
|
||||||
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
||||||
// landscape-flipped
|
// landscape-flipped
|
||||||
case Windows.Devices.Sensors.SimpleOrientation.rotated270DegreesCounterclockwise:
|
case Windows.Devices.Sensors.SimpleOrientation.rotated270DegreesCounterclockwise:
|
||||||
return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
|
return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
|
||||||
// faceup & facedown
|
// faceup & facedown
|
||||||
default:
|
default:
|
||||||
// Falling back to portrait default
|
// Falling back to portrait default
|
||||||
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -692,7 +678,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
* Rotates the current MediaCapture's video
|
* Rotates the current MediaCapture's video
|
||||||
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
|
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
|
||||||
*/
|
*/
|
||||||
function setPreviewRotation(orientation) {
|
function setPreviewRotation (orientation) {
|
||||||
capture.setPreviewRotation(orientationToRotation(orientation));
|
capture.setPreviewRotation(orientationToRotation(orientation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,19 +690,19 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function takePictureFromCameraWindows(successCallback, errorCallback, args) {
|
function takePictureFromCameraWindows (successCallback, errorCallback, args) {
|
||||||
var destinationType = args[1],
|
var destinationType = args[1];
|
||||||
targetWidth = args[3],
|
var targetWidth = args[3];
|
||||||
targetHeight = args[4],
|
var targetHeight = args[4];
|
||||||
encodingType = args[5],
|
var encodingType = args[5];
|
||||||
allowCrop = !!args[7],
|
var allowCrop = !!args[7];
|
||||||
saveToPhotoAlbum = args[9],
|
var saveToPhotoAlbum = args[9];
|
||||||
WMCapture = Windows.Media.Capture,
|
var WMCapture = Windows.Media.Capture;
|
||||||
cameraCaptureUI = new WMCapture.CameraCaptureUI();
|
var cameraCaptureUI = new WMCapture.CameraCaptureUI();
|
||||||
|
|
||||||
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
|
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
|
||||||
|
|
||||||
if (encodingType == Camera.EncodingType.PNG) {
|
if (encodingType === Camera.EncodingType.PNG) {
|
||||||
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.png;
|
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.png;
|
||||||
} else {
|
} else {
|
||||||
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.jpeg;
|
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.jpeg;
|
||||||
@@ -727,14 +713,13 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
|
|||||||
var UIMaxRes = WMCapture.CameraCaptureUIMaxPhotoResolution;
|
var UIMaxRes = WMCapture.CameraCaptureUIMaxPhotoResolution;
|
||||||
var totalPixels = targetWidth * targetHeight;
|
var totalPixels = targetWidth * targetHeight;
|
||||||
|
|
||||||
if (targetWidth == -1 && targetHeight == -1) {
|
if (targetWidth === -1 && targetHeight === -1) {
|
||||||
maxRes = UIMaxRes.highestAvailable;
|
maxRes = UIMaxRes.highestAvailable;
|
||||||
}
|
|
||||||
// Temp fix for CB-10539
|
// Temp fix for CB-10539
|
||||||
/*else if (totalPixels <= 320 * 240) {
|
/* else if (totalPixels <= 320 * 240) {
|
||||||
maxRes = UIMaxRes.verySmallQvga;
|
maxRes = UIMaxRes.verySmallQvga;
|
||||||
}*/
|
} */
|
||||||
else if (totalPixels <= 640 * 480) {
|
} else if (totalPixels <= 640 * 480) {
|
||||||
maxRes = UIMaxRes.smallVga;
|
maxRes = UIMaxRes.smallVga;
|
||||||
} else if (totalPixels <= 1024 * 768) {
|
} else if (totalPixels <= 1024 * 768) {
|
||||||
maxRes = UIMaxRes.mediumXga;
|
maxRes = UIMaxRes.mediumXga;
|
||||||
@@ -752,7 +737,7 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
|
|||||||
|
|
||||||
// define focus handler for windows phone 10.0
|
// define focus handler for windows phone 10.0
|
||||||
var savePhotoOnFocus = function () {
|
var savePhotoOnFocus = function () {
|
||||||
window.removeEventListener("focus", savePhotoOnFocus);
|
window.removeEventListener('focus', savePhotoOnFocus);
|
||||||
// call only when the app is in focus again
|
// call only when the app is in focus again
|
||||||
savePhoto(cameraPicture, {
|
savePhoto(cameraPicture, {
|
||||||
destinationType: destinationType,
|
destinationType: destinationType,
|
||||||
@@ -765,14 +750,14 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
|
|||||||
|
|
||||||
// if windows phone 10, add and delete focus eventHandler to capture the focus back from cameraUI to app
|
// 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) {
|
if (navigator.appVersion.indexOf('Windows Phone 10.0') >= 0) {
|
||||||
window.addEventListener("focus", savePhotoOnFocus);
|
window.addEventListener('focus', savePhotoOnFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function (picture) {
|
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function (picture) {
|
||||||
if (!picture) {
|
if (!picture) {
|
||||||
errorCallback("User didn't capture a photo.");
|
errorCallback("User didn't capture a photo.");
|
||||||
// Remove the focus handler if present
|
// Remove the focus handler if present
|
||||||
window.removeEventListener("focus", savePhotoOnFocus);
|
window.removeEventListener('focus', savePhotoOnFocus);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
cameraPicture = picture;
|
cameraPicture = picture;
|
||||||
@@ -788,31 +773,31 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
|
|||||||
}, successCallback, errorCallback);
|
}, successCallback, errorCallback);
|
||||||
}
|
}
|
||||||
}, function () {
|
}, function () {
|
||||||
errorCallback("Fail to capture a photo.");
|
errorCallback('Fail to capture a photo.');
|
||||||
window.removeEventListener("focus", savePhotoOnFocus);
|
window.removeEventListener('focus', savePhotoOnFocus);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function savePhoto(picture, options, successCallback, errorCallback) {
|
function savePhoto (picture, options, successCallback, errorCallback) {
|
||||||
// success callback for capture operation
|
// success callback for capture operation
|
||||||
var success = function(picture) {
|
var success = function (picture) {
|
||||||
if (options.destinationType == Camera.DestinationType.FILE_URI || options.destinationType == Camera.DestinationType.NATIVE_URI) {
|
if (options.destinationType === Camera.DestinationType.FILE_URI || options.destinationType === Camera.DestinationType.NATIVE_URI) {
|
||||||
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
||||||
resizeImage(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight, options.encodingType);
|
resizeImage(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight, options.encodingType);
|
||||||
} else {
|
} else {
|
||||||
picture.copyAsync(getAppData().localFolder, picture.name, OptUnique).done(function (copiedFile) {
|
picture.copyAsync(getAppData().localFolder, picture.name, OptUnique).done(function (copiedFile) {
|
||||||
successCallback("ms-appdata:///local/" + copiedFile.name);
|
successCallback('ms-appdata:///local/' + copiedFile.name);
|
||||||
},errorCallback);
|
}, errorCallback);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
if (options.targetHeight > 0 && options.targetWidth > 0) {
|
||||||
resizeImageBase64(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight);
|
resizeImageBase64(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight);
|
||||||
} else {
|
} else {
|
||||||
fileIO.readBufferAsync(picture).done(function(buffer) {
|
fileIO.readBufferAsync(picture).done(function (buffer) {
|
||||||
var strBase64 = encodeToBase64String(buffer);
|
var strBase64 = encodeToBase64String(buffer);
|
||||||
picture.deleteAsync().done(function() {
|
picture.deleteAsync().done(function () {
|
||||||
successCallback(strBase64);
|
successCallback(strBase64);
|
||||||
}, function(err) {
|
}, function (err) {
|
||||||
errorCallback(err);
|
errorCallback(err);
|
||||||
});
|
});
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
@@ -822,38 +807,38 @@ function savePhoto(picture, options, successCallback, errorCallback) {
|
|||||||
|
|
||||||
if (!options.saveToPhotoAlbum) {
|
if (!options.saveToPhotoAlbum) {
|
||||||
success(picture);
|
success(picture);
|
||||||
return;
|
|
||||||
} else {
|
} else {
|
||||||
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
|
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
|
||||||
var saveFile = function(file) {
|
var saveFile = function (file) {
|
||||||
if (file) {
|
if (file) {
|
||||||
// Prevent updates to the remote version of the file until we're done
|
// Prevent updates to the remote version of the file until we're done
|
||||||
Windows.Storage.CachedFileManager.deferUpdates(file);
|
Windows.Storage.CachedFileManager.deferUpdates(file);
|
||||||
picture.moveAndReplaceAsync(file)
|
picture.moveAndReplaceAsync(file)
|
||||||
.then(function() {
|
.then(function () {
|
||||||
// Let Windows know that we're finished changing the file so
|
// Let Windows know that we're finished changing the file so
|
||||||
// the other app can update the remote version of the file.
|
// the other app can update the remote version of the file.
|
||||||
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
|
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
|
||||||
})
|
})
|
||||||
.done(function(updateStatus) {
|
.done(function (updateStatus) {
|
||||||
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
|
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
|
||||||
success(picture);
|
success(picture);
|
||||||
} else {
|
} else {
|
||||||
errorCallback("File update status is not complete.");
|
errorCallback('File update status is not complete.');
|
||||||
}
|
}
|
||||||
}, errorCallback);
|
}, errorCallback);
|
||||||
} else {
|
} else {
|
||||||
errorCallback("Failed to select a file.");
|
errorCallback('Failed to select a file.');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
savePicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
savePicker.suggestedStartLocation = pickerLocId.picturesLibrary;
|
||||||
|
|
||||||
if (options.encodingType === Camera.EncodingType.PNG) {
|
if (options.encodingType === Camera.EncodingType.PNG) {
|
||||||
savePicker.fileTypeChoices.insert("PNG", [".png"]);
|
savePicker.fileTypeChoices.insert('PNG', ['.png']);
|
||||||
savePicker.suggestedFileName = "photo.png";
|
savePicker.suggestedFileName = 'photo.png';
|
||||||
} else {
|
} else {
|
||||||
savePicker.fileTypeChoices.insert("JPEG", [".jpg"]);
|
savePicker.fileTypeChoices.insert('JPEG', ['.jpg']);
|
||||||
savePicker.suggestedFileName = "photo.jpg";
|
savePicker.suggestedFileName = 'photo.jpg';
|
||||||
}
|
}
|
||||||
|
|
||||||
// If Windows Phone 8.1 use pickSaveFileAndContinue()
|
// If Windows Phone 8.1 use pickSaveFileAndContinue()
|
||||||
@@ -863,14 +848,14 @@ function savePhoto(picture, options, successCallback, errorCallback) {
|
|||||||
Using FileSavePicker will suspend the app and it's required to catch the pickSaveFileContinuation
|
Using FileSavePicker will suspend the app and it's required to catch the pickSaveFileContinuation
|
||||||
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
|
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
|
||||||
*/
|
*/
|
||||||
var fileSaveHandler = function(eventArgs) {
|
var fileSaveHandler = function (eventArgs) {
|
||||||
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickSaveFileContinuation) {
|
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickSaveFileContinuation) {
|
||||||
var file = eventArgs.file;
|
var file = eventArgs.file;
|
||||||
saveFile(file);
|
saveFile(file);
|
||||||
webUIApp.removeEventListener("activated", fileSaveHandler);
|
webUIApp.removeEventListener('activated', fileSaveHandler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
webUIApp.addEventListener("activated", fileSaveHandler);
|
webUIApp.addEventListener('activated', fileSaveHandler);
|
||||||
savePicker.pickSaveFileAndContinue();
|
savePicker.pickSaveFileAndContinue();
|
||||||
} else {
|
} else {
|
||||||
savePicker.pickSaveFileAsync()
|
savePicker.pickSaveFileAsync()
|
||||||
@@ -879,4 +864,4 @@ function savePhoto(picture, options, successCallback, errorCallback) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require("cordova/exec/proxy").add("Camera",module.exports);
|
require('cordova/exec/proxy').add('Camera', module.exports);
|
||||||
|
|||||||
@@ -1,534 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Net;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Documents;
|
|
||||||
using System.Windows.Ink;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Media.Animation;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using Microsoft.Phone.Tasks;
|
|
||||||
using System.Runtime.Serialization;
|
|
||||||
using System.IO;
|
|
||||||
using System.IO.IsolatedStorage;
|
|
||||||
using System.Windows.Media.Imaging;
|
|
||||||
using Microsoft.Phone;
|
|
||||||
using Microsoft.Xna.Framework.Media;
|
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace WPCordovaClassLib.Cordova.Commands
|
|
||||||
{
|
|
||||||
public class Camera : BaseCommand
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return base64 encoded string
|
|
||||||
/// </summary>
|
|
||||||
private const int DATA_URL = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Return file uri
|
|
||||||
/// </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>
|
|
||||||
private const int PHOTOLIBRARY = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Take picture from camera
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
private const int CAMERA = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Choose image from picture library
|
|
||||||
/// </summary>
|
|
||||||
private const int SAVEDPHOTOALBUM = 2;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Take a picture of type JPEG
|
|
||||||
/// </summary>
|
|
||||||
private const int JPEG = 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Take a picture of type PNG
|
|
||||||
/// </summary>
|
|
||||||
private const int PNG = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Folder to store captured images
|
|
||||||
/// </summary>
|
|
||||||
private const string isoFolder = "CapturedImagesCache";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Represents captureImage action options.
|
|
||||||
/// </summary>
|
|
||||||
[DataContract]
|
|
||||||
public class CameraOptions
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Source to getPicture from.
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "sourceType")]
|
|
||||||
public int PictureSourceType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Format of image that returned from getPicture.
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "destinationType")]
|
|
||||||
public int DestinationType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Quality of saved image
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "quality")]
|
|
||||||
public int Quality { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controls whether or not the image is also added to the device photo album.
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "saveToPhotoAlbum")]
|
|
||||||
public bool SaveToPhotoAlbum { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ignored
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "correctOrientation")]
|
|
||||||
public bool CorrectOrientation { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Ignored
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "allowEdit")]
|
|
||||||
public bool AllowEdit { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Height in pixels to scale image
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "encodingType")]
|
|
||||||
public int EncodingType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Height in pixels to scale image
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "mediaType")]
|
|
||||||
public int MediaType { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Height in pixels to scale image
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "targetHeight")]
|
|
||||||
public int TargetHeight { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Width in pixels to scale image
|
|
||||||
/// </summary>
|
|
||||||
[DataMember(IsRequired = false, Name = "targetWidth")]
|
|
||||||
public int TargetWidth { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Creates options object with default parameters
|
|
||||||
/// </summary>
|
|
||||||
public CameraOptions()
|
|
||||||
{
|
|
||||||
this.SetDefaultValues(new StreamingContext());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes default values for class fields.
|
|
||||||
/// Implemented in separate method because default constructor is not invoked during deserialization.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
[OnDeserializing()]
|
|
||||||
public void SetDefaultValues(StreamingContext context)
|
|
||||||
{
|
|
||||||
PictureSourceType = CAMERA;
|
|
||||||
DestinationType = FILE_URI;
|
|
||||||
Quality = 80;
|
|
||||||
TargetHeight = -1;
|
|
||||||
TargetWidth = -1;
|
|
||||||
SaveToPhotoAlbum = false;
|
|
||||||
CorrectOrientation = true;
|
|
||||||
AllowEdit = false;
|
|
||||||
MediaType = -1;
|
|
||||||
EncodingType = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Camera options
|
|
||||||
/// </summary>
|
|
||||||
CameraOptions cameraOptions;
|
|
||||||
|
|
||||||
public void takePicture(string options)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
|
|
||||||
// ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
|
|
||||||
// "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
|
|
||||||
cameraOptions = new CameraOptions();
|
|
||||||
cameraOptions.Quality = int.Parse(args[0]);
|
|
||||||
cameraOptions.DestinationType = int.Parse(args[1]);
|
|
||||||
cameraOptions.PictureSourceType = int.Parse(args[2]);
|
|
||||||
cameraOptions.TargetWidth = int.Parse(args[3]);
|
|
||||||
cameraOptions.TargetHeight = int.Parse(args[4]);
|
|
||||||
cameraOptions.EncodingType = int.Parse(args[5]);
|
|
||||||
cameraOptions.MediaType = int.Parse(args[6]);
|
|
||||||
cameraOptions.AllowEdit = bool.Parse(args[7]);
|
|
||||||
cameraOptions.CorrectOrientation = bool.Parse(args[8]);
|
|
||||||
cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
|
|
||||||
|
|
||||||
// a very large number will force the other value to be the bound
|
|
||||||
if (cameraOptions.TargetWidth > -1 && cameraOptions.TargetHeight == -1)
|
|
||||||
{
|
|
||||||
cameraOptions.TargetHeight = 100000;
|
|
||||||
}
|
|
||||||
else if (cameraOptions.TargetHeight > -1 && cameraOptions.TargetWidth == -1)
|
|
||||||
{
|
|
||||||
cameraOptions.TargetWidth = 100000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Api supports FILE_URI, DATA_URL, NATIVE_URI destination types.
|
|
||||||
// Treat all other destination types as an error.
|
|
||||||
switch (cameraOptions.DestinationType)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
if (cameraOptions.PictureSourceType == CAMERA)
|
|
||||||
{
|
|
||||||
chooserTask = new CameraCaptureTask();
|
|
||||||
}
|
|
||||||
else if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
|
|
||||||
{
|
|
||||||
chooserTask = new PhotoChooserTask();
|
|
||||||
}
|
|
||||||
// if chooserTask is still null, then PictureSourceType was invalid
|
|
||||||
if (chooserTask != null)
|
|
||||||
{
|
|
||||||
chooserTask.Completed += onTaskCompleted;
|
|
||||||
chooserTask.Show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.WriteLine("Unrecognized PictureSourceType :: " + cameraOptions.PictureSourceType.ToString());
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onTaskCompleted(object sender, PhotoResult e)
|
|
||||||
{
|
|
||||||
var task = sender as ChooserBase<PhotoResult>;
|
|
||||||
if (task != null)
|
|
||||||
{
|
|
||||||
task.Completed -= onTaskCompleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.Error != null)
|
|
||||||
{
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (e.TaskResult)
|
|
||||||
{
|
|
||||||
case TaskResult.OK:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string imagePathOrContent = string.Empty;
|
|
||||||
|
|
||||||
// Save image back to media library
|
|
||||||
// only save to photoalbum if it didn't come from there ...
|
|
||||||
if (cameraOptions.PictureSourceType == CAMERA && cameraOptions.SaveToPhotoAlbum)
|
|
||||||
{
|
|
||||||
MediaLibrary library = new MediaLibrary();
|
|
||||||
Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
|
|
||||||
}
|
|
||||||
|
|
||||||
int newAngle = 0;
|
|
||||||
// There's bug in Windows Phone 8.1 causing Seek on a DssPhotoStream not working properly.
|
|
||||||
// https://connect.microsoft.com/VisualStudio/feedback/details/783252
|
|
||||||
// But a mis-oriented file is better than nothing, so try and catch.
|
|
||||||
try {
|
|
||||||
int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
|
|
||||||
switch (orient) {
|
|
||||||
case ImageExifOrientation.LandscapeLeft:
|
|
||||||
newAngle = 90;
|
|
||||||
break;
|
|
||||||
case ImageExifOrientation.PortraitUpsideDown:
|
|
||||||
newAngle = 180;
|
|
||||||
break;
|
|
||||||
case ImageExifOrientation.LandscapeRight:
|
|
||||||
newAngle = 270;
|
|
||||||
break;
|
|
||||||
case ImageExifOrientation.Portrait:
|
|
||||||
default: break; // 0 default already set
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
Debug.WriteLine("Error fetching orientation from Exif");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newAngle != 0)
|
|
||||||
{
|
|
||||||
using (Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle))
|
|
||||||
{
|
|
||||||
// we should reset stream position after saving stream to media library
|
|
||||||
rotImageStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
if (cameraOptions.DestinationType == DATA_URL)
|
|
||||||
{
|
|
||||||
imagePathOrContent = GetImageContent(rotImageStream);
|
|
||||||
}
|
|
||||||
else // FILE_URL or NATIVE_URI (both use the same resultant uri format)
|
|
||||||
{
|
|
||||||
imagePathOrContent = SaveImageToLocalStorage(rotImageStream, Path.GetFileName(e.OriginalFileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // no need to reorient
|
|
||||||
{
|
|
||||||
if (cameraOptions.DestinationType == DATA_URL)
|
|
||||||
{
|
|
||||||
imagePathOrContent = GetImageContent(e.ChosenPhoto);
|
|
||||||
}
|
|
||||||
else // FILE_URL or NATIVE_URI (both use the same resultant uri format)
|
|
||||||
{
|
|
||||||
imagePathOrContent = SaveImageToLocalStorage(e.ChosenPhoto, Path.GetFileName(e.OriginalFileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TaskResult.Cancel:
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns image content in a form of base64 string
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">Image stream</param>
|
|
||||||
/// <returns>Base64 representation of the image</returns>
|
|
||||||
private string GetImageContent(Stream stream)
|
|
||||||
{
|
|
||||||
byte[] imageContent = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Resize photo and convert to JPEG
|
|
||||||
imageContent = ResizePhoto(stream);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
stream.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Convert.ToBase64String(imageContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Resize image
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="stream">Image stream</param>
|
|
||||||
/// <returns>resized image</returns>
|
|
||||||
private byte[] ResizePhoto(Stream stream)
|
|
||||||
{
|
|
||||||
//output
|
|
||||||
byte[] resizedFile;
|
|
||||||
|
|
||||||
BitmapImage objBitmap = new BitmapImage();
|
|
||||||
objBitmap.SetSource(stream);
|
|
||||||
objBitmap.CreateOptions = BitmapCreateOptions.None;
|
|
||||||
|
|
||||||
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
|
|
||||||
objBitmap.UriSource = null;
|
|
||||||
|
|
||||||
// Calculate resultant image size
|
|
||||||
int width, height;
|
|
||||||
if (cameraOptions.TargetWidth >= 0 && cameraOptions.TargetHeight >= 0)
|
|
||||||
{
|
|
||||||
// Keep proportionally
|
|
||||||
double ratio = Math.Min(
|
|
||||||
(double)cameraOptions.TargetWidth / objWB.PixelWidth,
|
|
||||||
(double)cameraOptions.TargetHeight / objWB.PixelHeight);
|
|
||||||
width = Convert.ToInt32(ratio * objWB.PixelWidth);
|
|
||||||
height = Convert.ToInt32(ratio * objWB.PixelHeight);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
width = objWB.PixelWidth;
|
|
||||||
height = objWB.PixelHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Hold the result stream
|
|
||||||
using (MemoryStream objBitmapStreamResized = new MemoryStream())
|
|
||||||
{
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// resize the photo with user defined TargetWidth & TargetHeight
|
|
||||||
Extensions.SaveJpeg(objWB, objBitmapStreamResized, width, height, 0, cameraOptions.Quality);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
//Dispose bitmaps immediately, they are memory expensive
|
|
||||||
DisposeImage(objBitmap);
|
|
||||||
DisposeImage(objWB);
|
|
||||||
GC.Collect();
|
|
||||||
}
|
|
||||||
|
|
||||||
//Convert the resized stream to a byte array.
|
|
||||||
int streamLength = (int)objBitmapStreamResized.Length;
|
|
||||||
resizedFile = new Byte[streamLength]; //-1
|
|
||||||
objBitmapStreamResized.Position = 0;
|
|
||||||
|
|
||||||
//for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
|
|
||||||
objBitmapStreamResized.Read(resizedFile, 0, streamLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
return resizedFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Util: Dispose a bitmap resource
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="image">BitmapSource subclass to dispose</param>
|
|
||||||
private void DisposeImage(BitmapSource image)
|
|
||||||
{
|
|
||||||
if (image != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var ms = new MemoryStream(new byte[] { 0x0 }))
|
|
||||||
{
|
|
||||||
image.SetSource(ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Saves captured image in isolated storage
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="imageFileName">image file name</param>
|
|
||||||
/// <returns>Image path</returns>
|
|
||||||
private string SaveImageToLocalStorage(Stream stream, string imageFileName)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (stream == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException("imageBytes");
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
|
|
||||||
|
|
||||||
if (!isoFile.DirectoryExists(isoFolder))
|
|
||||||
{
|
|
||||||
isoFile.CreateDirectory(isoFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
|
|
||||||
|
|
||||||
using (IsolatedStorageFileStream outputStream = isoFile.CreateFile(filePath))
|
|
||||||
{
|
|
||||||
BitmapImage objBitmap = new BitmapImage();
|
|
||||||
objBitmap.SetSource(stream);
|
|
||||||
objBitmap.CreateOptions = BitmapCreateOptions.None;
|
|
||||||
|
|
||||||
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
|
|
||||||
objBitmap.UriSource = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
//use photo's actual width & height if user doesn't provide width & height
|
|
||||||
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
|
||||||
{
|
|
||||||
objWB.SaveJpeg(outputStream, objWB.PixelWidth, objWB.PixelHeight, 0, cameraOptions.Quality);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//Resize
|
|
||||||
//Keep proportionally
|
|
||||||
double ratio = Math.Min((double)cameraOptions.TargetWidth / objWB.PixelWidth, (double)cameraOptions.TargetHeight / objWB.PixelHeight);
|
|
||||||
int width = Convert.ToInt32(ratio * objWB.PixelWidth);
|
|
||||||
int height = Convert.ToInt32(ratio * objWB.PixelHeight);
|
|
||||||
|
|
||||||
// resize the photo with user defined TargetWidth & TargetHeight
|
|
||||||
objWB.SaveJpeg(outputStream, width, height, 0, cameraOptions.Quality);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
//Dispose bitmaps immediately, they are memory expensive
|
|
||||||
DisposeImage(objBitmap);
|
|
||||||
DisposeImage(objWB);
|
|
||||||
GC.Collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Uri(filePath, UriKind.Relative).ToString();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
//TODO: log or do something else
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
stream.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "cordova-plugin-camera-tests",
|
||||||
|
"version": "2.4.1-dev",
|
||||||
|
"description": "",
|
||||||
|
"cordova": {
|
||||||
|
"id": "cordova-plugin-camera-tests",
|
||||||
|
"platforms": []
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"ecosystem:cordova"
|
||||||
|
],
|
||||||
|
"author": "",
|
||||||
|
"license": "Apache 2.0"
|
||||||
|
}
|
||||||
+3
-3
@@ -22,11 +22,11 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
||||||
id="cordova-plugin-camera-tests"
|
id="cordova-plugin-camera-tests"
|
||||||
version="2.3.0">
|
version="4.0.3">
|
||||||
<name>Cordova Camera Plugin Tests</name>
|
<name>Cordova Camera Plugin Tests</name>
|
||||||
<license>Apache 2.0</license>
|
<license>Apache 2.0</license>
|
||||||
|
|
||||||
<dependency id="cordova-plugin-file" version=">=2.0.0" />
|
<dependency id="cordova-plugin-file-transfer" />
|
||||||
|
|
||||||
<js-module src="tests.js" name="tests">
|
<js-module src="tests.js" name="tests">
|
||||||
</js-module>
|
</js-module>
|
||||||
|
|||||||
+70
-68
@@ -20,26 +20,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* globals Camera, resolveLocalFileSystemURL, FileEntry, CameraPopoverOptions, FileTransfer, FileUploadOptions, LocalFileSystem, MSApp */
|
/* globals Camera, resolveLocalFileSystemURL, FileEntry, CameraPopoverOptions, FileTransfer, FileUploadOptions, LocalFileSystem, MSApp */
|
||||||
/* jshint jasmine: true */
|
/* eslint-env jasmine */
|
||||||
|
|
||||||
exports.defineAutoTests = function () {
|
exports.defineAutoTests = function () {
|
||||||
describe('Camera (navigator.camera)', function () {
|
describe('Camera (navigator.camera)', function () {
|
||||||
it("should exist", function () {
|
it('should exist', function () {
|
||||||
expect(navigator.camera).toBeDefined();
|
expect(navigator.camera).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should contain a getPicture function", function () {
|
it('should contain a getPicture function', function () {
|
||||||
expect(navigator.camera.getPicture).toBeDefined();
|
expect(navigator.camera.getPicture).toBeDefined();
|
||||||
expect(typeof navigator.camera.getPicture == 'function').toBe(true);
|
expect(typeof navigator.camera.getPicture === 'function').toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Camera Constants (window.Camera + navigator.camera)', function () {
|
describe('Camera Constants (window.Camera + navigator.camera)', function () {
|
||||||
it("camera.spec.1 window.Camera should exist", function () {
|
it('camera.spec.1 window.Camera should exist', function () {
|
||||||
expect(window.Camera).toBeDefined();
|
expect(window.Camera).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("camera.spec.2 should contain three DestinationType constants", function () {
|
it('camera.spec.2 should contain three DestinationType constants', function () {
|
||||||
expect(Camera.DestinationType.DATA_URL).toBe(0);
|
expect(Camera.DestinationType.DATA_URL).toBe(0);
|
||||||
expect(Camera.DestinationType.FILE_URI).toBe(1);
|
expect(Camera.DestinationType.FILE_URI).toBe(1);
|
||||||
expect(Camera.DestinationType.NATIVE_URI).toBe(2);
|
expect(Camera.DestinationType.NATIVE_URI).toBe(2);
|
||||||
@@ -48,14 +48,14 @@ exports.defineAutoTests = function () {
|
|||||||
expect(navigator.camera.DestinationType.NATIVE_URI).toBe(2);
|
expect(navigator.camera.DestinationType.NATIVE_URI).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("camera.spec.3 should contain two EncodingType constants", function () {
|
it('camera.spec.3 should contain two EncodingType constants', function () {
|
||||||
expect(Camera.EncodingType.JPEG).toBe(0);
|
expect(Camera.EncodingType.JPEG).toBe(0);
|
||||||
expect(Camera.EncodingType.PNG).toBe(1);
|
expect(Camera.EncodingType.PNG).toBe(1);
|
||||||
expect(navigator.camera.EncodingType.JPEG).toBe(0);
|
expect(navigator.camera.EncodingType.JPEG).toBe(0);
|
||||||
expect(navigator.camera.EncodingType.PNG).toBe(1);
|
expect(navigator.camera.EncodingType.PNG).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("camera.spec.4 should contain three MediaType constants", function () {
|
it('camera.spec.4 should contain three MediaType constants', function () {
|
||||||
expect(Camera.MediaType.PICTURE).toBe(0);
|
expect(Camera.MediaType.PICTURE).toBe(0);
|
||||||
expect(Camera.MediaType.VIDEO).toBe(1);
|
expect(Camera.MediaType.VIDEO).toBe(1);
|
||||||
expect(Camera.MediaType.ALLMEDIA).toBe(2);
|
expect(Camera.MediaType.ALLMEDIA).toBe(2);
|
||||||
@@ -64,7 +64,7 @@ exports.defineAutoTests = function () {
|
|||||||
expect(navigator.camera.MediaType.ALLMEDIA).toBe(2);
|
expect(navigator.camera.MediaType.ALLMEDIA).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("camera.spec.5 should contain three PictureSourceType constants", function () {
|
it('camera.spec.5 should contain three PictureSourceType constants', function () {
|
||||||
expect(Camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
|
expect(Camera.PictureSourceType.PHOTOLIBRARY).toBe(0);
|
||||||
expect(Camera.PictureSourceType.CAMERA).toBe(1);
|
expect(Camera.PictureSourceType.CAMERA).toBe(1);
|
||||||
expect(Camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
|
expect(Camera.PictureSourceType.SAVEDPHOTOALBUM).toBe(2);
|
||||||
@@ -75,7 +75,6 @@ exports.defineAutoTests = function () {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@@ -86,7 +85,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
var fileEntry = null;
|
var fileEntry = null;
|
||||||
var pageStartTime = +new Date();
|
var pageStartTime = +new Date();
|
||||||
|
|
||||||
//default camera options
|
// default camera options
|
||||||
var camQualityDefault = ['50', 50];
|
var camQualityDefault = ['50', 50];
|
||||||
var camDestinationTypeDefault = ['FILE_URI', 1];
|
var camDestinationTypeDefault = ['FILE_URI', 1];
|
||||||
var camPictureSourceTypeDefault = ['CAMERA', 1];
|
var camPictureSourceTypeDefault = ['CAMERA', 1];
|
||||||
@@ -96,12 +95,12 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
var camCorrectOrientationDefault = ['correctOrientation', false];
|
var camCorrectOrientationDefault = ['correctOrientation', false];
|
||||||
var camSaveToPhotoAlbumDefault = ['saveToPhotoAlbum', true];
|
var camSaveToPhotoAlbumDefault = ['saveToPhotoAlbum', true];
|
||||||
|
|
||||||
function log(value) {
|
function log (value) {
|
||||||
console.log(value);
|
console.log(value);
|
||||||
document.getElementById('camera_status').textContent += (new Date() - pageStartTime) / 1000 + ': ' + value + '\n';
|
document.getElementById('camera_status').textContent += (new Date() - pageStartTime) / 1000 + ': ' + value + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearStatus() {
|
function clearStatus () {
|
||||||
document.getElementById('camera_status').innerHTML = '';
|
document.getElementById('camera_status').innerHTML = '';
|
||||||
document.getElementById('camera_image').src = 'about:blank';
|
document.getElementById('camera_image').src = 'about:blank';
|
||||||
var canvas = document.getElementById('canvas');
|
var canvas = document.getElementById('canvas');
|
||||||
@@ -111,11 +110,11 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
fileEntry = null;
|
fileEntry = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPicture(url, callback) {
|
function setPicture (url, callback) {
|
||||||
try {
|
try {
|
||||||
window.atob(url);
|
window.atob(url);
|
||||||
// if we got here it is a base64 string (DATA_URL)
|
// if we got here it is a base64 string (DATA_URL)
|
||||||
url = "data:image/jpeg;base64," + url;
|
url = 'data:image/jpeg;base64,' + url;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// not DATA_URL
|
// not DATA_URL
|
||||||
}
|
}
|
||||||
@@ -134,11 +133,11 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function onGetPictureError(e) {
|
function onGetPictureError (e) {
|
||||||
log('Error getting picture: ' + (e.code || e));
|
log('Error getting picture: ' + (e.code || e));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPictureWin(data) {
|
function getPictureWin (data) {
|
||||||
setPicture(data);
|
setPicture(data);
|
||||||
// TODO: Fix resolveLocalFileSystemURI to work with native-uri.
|
// TODO: Fix resolveLocalFileSystemURI to work with native-uri.
|
||||||
if (pictureUrl.indexOf('file:') === 0 || pictureUrl.indexOf('content:') === 0 || pictureUrl.indexOf('ms-appdata:') === 0 || pictureUrl.indexOf('assets-library:') === 0) {
|
if (pictureUrl.indexOf('file:') === 0 || pictureUrl.indexOf('content:') === 0 || pictureUrl.indexOf('ms-appdata:') === 0 || pictureUrl.indexOf('assets-library:') === 0) {
|
||||||
@@ -155,7 +154,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPicture() {
|
function getPicture () {
|
||||||
clearStatus();
|
clearStatus();
|
||||||
var options = extractOptions();
|
var options = extractOptions();
|
||||||
log('Getting picture with options: ' + JSON.stringify(options));
|
log('Getting picture with options: ' + JSON.stringify(options));
|
||||||
@@ -168,27 +167,27 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function uploadImage() {
|
function uploadImage () {
|
||||||
var ft = new FileTransfer(),
|
var ft = new FileTransfer();
|
||||||
options = new FileUploadOptions();
|
var options = new FileUploadOptions();
|
||||||
options.fileKey = "photo";
|
options.fileKey = 'photo';
|
||||||
options.fileName = 'test.jpg';
|
options.fileName = 'test.jpg';
|
||||||
options.mimeType = "image/jpeg";
|
options.mimeType = 'image/jpeg';
|
||||||
ft.onprogress = function (progressEvent) {
|
ft.onprogress = function (progressEvent) {
|
||||||
console.log('progress: ' + progressEvent.loaded + ' of ' + progressEvent.total);
|
console.log('progress: ' + progressEvent.loaded + ' of ' + progressEvent.total);
|
||||||
};
|
};
|
||||||
var server = "http://cordova-filetransfer.jitsu.com";
|
var server = 'http://sheltered-retreat-43956.herokuapp.com';
|
||||||
|
|
||||||
ft.upload(pictureUrl, server + '/upload', win, fail, options);
|
ft.upload(pictureUrl, server + '/upload', win, fail, options);
|
||||||
function win(information_back) {
|
function win (information_back) {
|
||||||
log('upload complete');
|
log('upload complete');
|
||||||
}
|
}
|
||||||
function fail(message) {
|
function fail (message) {
|
||||||
log('upload failed: ' + JSON.stringify(message));
|
log('upload failed: ' + JSON.stringify(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logCallback(apiName, success) {
|
function logCallback (apiName, success) {
|
||||||
return function () {
|
return function () {
|
||||||
log('Call to ' + apiName + (success ? ' success: ' : ' failed: ') + JSON.stringify([].slice.call(arguments)));
|
log('Call to ' + apiName + (success ? ' success: ' : ' failed: ') + JSON.stringify([].slice.call(arguments)));
|
||||||
};
|
};
|
||||||
@@ -198,20 +197,21 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
* Select image from library using a NATIVE_URI destination type
|
* Select image from library using a NATIVE_URI destination type
|
||||||
* This calls FileEntry.getMetadata, FileEntry.setMetadata, FileEntry.getParent, FileEntry.file, and FileReader.readAsDataURL.
|
* This calls FileEntry.getMetadata, FileEntry.setMetadata, FileEntry.getParent, FileEntry.file, and FileReader.readAsDataURL.
|
||||||
*/
|
*/
|
||||||
function readFile() {
|
function readFile () {
|
||||||
function onFileReadAsDataURL(evt) {
|
function onFileReadAsDataURL (evt) {
|
||||||
var img = document.getElementById('camera_image');
|
var img = document.getElementById('camera_image');
|
||||||
img.style.visibility = "visible";
|
img.style.visibility = 'visible';
|
||||||
img.style.display = "block";
|
img.style.display = 'block';
|
||||||
img.src = evt.target.result;
|
img.src = evt.target.result;
|
||||||
log("FileReader.readAsDataURL success");
|
log('FileReader.readAsDataURL success');
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFileReceived(file) {
|
function onFileReceived (file) {
|
||||||
log('Got file: ' + JSON.stringify(file));
|
log('Got file: ' + JSON.stringify(file));
|
||||||
fileObj = file;
|
fileObj = file;
|
||||||
|
/* eslint-disable no-undef */
|
||||||
var reader = new FileReader();
|
var reader = new FileReader();
|
||||||
|
/* eslint-enable no-undef */
|
||||||
reader.onload = function () {
|
reader.onload = function () {
|
||||||
log('FileReader.readAsDataURL() - length = ' + reader.result.length);
|
log('FileReader.readAsDataURL() - length = ' + reader.result.length);
|
||||||
};
|
};
|
||||||
@@ -228,10 +228,10 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFileInfo() {
|
function getFileInfo () {
|
||||||
// Test FileEntry API here.
|
// Test FileEntry API here.
|
||||||
fileEntry.getMetadata(logCallback('FileEntry.getMetadata', true), logCallback('FileEntry.getMetadata', false));
|
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.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));
|
||||||
fileEntry.getParent(logCallback('FileEntry.getParent', true), logCallback('FileEntry.getParent', false));
|
fileEntry.getParent(logCallback('FileEntry.getParent', true), logCallback('FileEntry.getParent', false));
|
||||||
}
|
}
|
||||||
@@ -240,7 +240,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
* Copy image from library using a NATIVE_URI destination type
|
* Copy image from library using a NATIVE_URI destination type
|
||||||
* This calls FileEntry.copyTo and FileEntry.moveTo.
|
* This calls FileEntry.copyTo and FileEntry.moveTo.
|
||||||
*/
|
*/
|
||||||
function copyImage() {
|
function copyImage () {
|
||||||
var onFileSystemReceived = function (fileSystem) {
|
var onFileSystemReceived = function (fileSystem) {
|
||||||
var destDirEntry = fileSystem.root;
|
var destDirEntry = fileSystem.root;
|
||||||
var origName = fileEntry.name;
|
var origName = fileEntry.name;
|
||||||
@@ -249,17 +249,17 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
fileEntry.copyTo(destDirEntry, 'copied_file.png', logCallback('FileEntry.copyTo', true), logCallback('FileEntry.copyTo', false));
|
fileEntry.copyTo(destDirEntry, 'copied_file.png', logCallback('FileEntry.copyTo', true), logCallback('FileEntry.copyTo', false));
|
||||||
fileEntry.moveTo(destDirEntry, 'moved_file.png', logCallback('FileEntry.moveTo', true), logCallback('FileEntry.moveTo', false));
|
fileEntry.moveTo(destDirEntry, 'moved_file.png', logCallback('FileEntry.moveTo', true), logCallback('FileEntry.moveTo', false));
|
||||||
|
|
||||||
//cleanup
|
// cleanup
|
||||||
//rename moved file back to original name so other tests can reference image
|
// rename moved file back to original name so other tests can reference image
|
||||||
resolveLocalFileSystemURL(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));
|
fileEntry.moveTo(destDirEntry, origName, logCallback('FileEntry.moveTo', true), logCallback('FileEntry.moveTo', false));
|
||||||
console.log('Cleanup: successfully renamed file back to original name');
|
console.log('Cleanup: successfully renamed file back to original name');
|
||||||
}, function () {
|
}, function () {
|
||||||
console.log('Cleanup: failed to rename file back to original name');
|
console.log('Cleanup: failed to rename file back to original name');
|
||||||
});
|
});
|
||||||
|
|
||||||
//remove copied file
|
// remove copied file
|
||||||
resolveLocalFileSystemURL(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));
|
fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
||||||
console.log('Cleanup: successfully removed copied file');
|
console.log('Cleanup: successfully removed copied file');
|
||||||
}, function () {
|
}, function () {
|
||||||
@@ -274,11 +274,11 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
* Write image to library using a NATIVE_URI destination type
|
* Write image to library using a NATIVE_URI destination type
|
||||||
* This calls FileEntry.createWriter, FileWriter.write, and FileWriter.truncate.
|
* This calls FileEntry.createWriter, FileWriter.write, and FileWriter.truncate.
|
||||||
*/
|
*/
|
||||||
function writeImage() {
|
function writeImage () {
|
||||||
var onFileWriterReceived = function (fileWriter) {
|
var onFileWriterReceived = function (fileWriter) {
|
||||||
fileWriter.onwrite = logCallback('FileWriter.write', true);
|
fileWriter.onwrite = logCallback('FileWriter.write', true);
|
||||||
fileWriter.onerror = logCallback('FileWriter.write', false);
|
fileWriter.onerror = logCallback('FileWriter.write', false);
|
||||||
fileWriter.write("some text!");
|
fileWriter.write('some text!');
|
||||||
};
|
};
|
||||||
|
|
||||||
var onFileTruncateWriterReceived = function (fileWriter) {
|
var onFileTruncateWriterReceived = function (fileWriter) {
|
||||||
@@ -291,7 +291,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
fileEntry.createWriter(onFileTruncateWriterReceived, null);
|
fileEntry.createWriter(onFileTruncateWriterReceived, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayImageUsingCanvas() {
|
function displayImageUsingCanvas () {
|
||||||
var canvas = document.getElementById('canvas');
|
var canvas = document.getElementById('canvas');
|
||||||
var img = document.getElementById('camera_image');
|
var img = document.getElementById('camera_image');
|
||||||
var w = img.width;
|
var w = img.width;
|
||||||
@@ -308,11 +308,11 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
* Remove image from library using a NATIVE_URI destination type
|
* Remove image from library using a NATIVE_URI destination type
|
||||||
* This calls FileEntry.remove.
|
* This calls FileEntry.remove.
|
||||||
*/
|
*/
|
||||||
function removeImage() {
|
function removeImage () {
|
||||||
fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
fileEntry.remove(logCallback('FileEntry.remove', true), logCallback('FileEntry.remove', false));
|
||||||
}
|
}
|
||||||
|
|
||||||
function testInputTag(inputEl) {
|
function testInputTag (inputEl) {
|
||||||
clearStatus();
|
clearStatus();
|
||||||
// iOS 6 likes to dead-lock in the onchange context if you
|
// iOS 6 likes to dead-lock in the onchange context if you
|
||||||
// do any alerts or try to remote-debug.
|
// do any alerts or try to remote-debug.
|
||||||
@@ -321,7 +321,8 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function testNativeFile2(inputEl) {
|
function testNativeFile2 (inputEl) {
|
||||||
|
/* eslint-disable no-undef */
|
||||||
if (!inputEl.value) {
|
if (!inputEl.value) {
|
||||||
alert('No file selected.');
|
alert('No file selected.');
|
||||||
return;
|
return;
|
||||||
@@ -331,6 +332,7 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
alert('Got value but no file.');
|
alert('Got value but no file.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-undef */
|
||||||
var URLApi = window.URL || window.webkitURL;
|
var URLApi = window.URL || window.webkitURL;
|
||||||
if (URLApi) {
|
if (URLApi) {
|
||||||
var blobURL = URLApi.createObjectURL(fileObj);
|
var blobURL = URLApi.createObjectURL(fileObj);
|
||||||
@@ -346,26 +348,26 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractOptions() {
|
function extractOptions () {
|
||||||
var els = document.querySelectorAll('#image-options select');
|
var els = document.querySelectorAll('#image-options select');
|
||||||
var ret = {};
|
var ret = {};
|
||||||
/*jshint -W084 */
|
/* eslint-disable no-cond-assign */
|
||||||
for (var i = 0, el; el = els[i]; ++i) {
|
for (var i = 0, el; el = els[i]; ++i) {
|
||||||
var value = el.value;
|
var value = el.value;
|
||||||
if (value === '') continue;
|
if (value === '') continue;
|
||||||
value = +value;
|
value = +value;
|
||||||
|
|
||||||
if (el.isBool) {
|
if (el.isBool) {
|
||||||
ret[el.getAttribute("name")] = !!value;
|
ret[el.getAttribute('name')] = !!value;
|
||||||
} else {
|
} else {
|
||||||
ret[el.getAttribute("name")] = value;
|
ret[el.getAttribute('name')] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*jshint +W084 */
|
/* eslint-enable no-cond-assign */
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createOptionsEl(name, values, selectionDefault) {
|
function createOptionsEl (name, values, selectionDefault) {
|
||||||
var openDiv = '<div style="display: inline-block">' + name + ': ';
|
var openDiv = '<div style="display: inline-block">' + name + ': ';
|
||||||
var select = '<select name=' + name + ' id="' + name + '">';
|
var select = '<select name=' + name + ' id="' + name + '">';
|
||||||
|
|
||||||
@@ -375,13 +377,13 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var options = '';
|
var options = '';
|
||||||
if (typeof values == 'boolean') {
|
if (typeof values === 'boolean') {
|
||||||
values = { 'true': 1, 'false': 0 };
|
values = { 'true': 1, 'false': 0 };
|
||||||
}
|
}
|
||||||
for (var k in values) {
|
for (var k in values) {
|
||||||
var isSelected = '';
|
var isSelected = '';
|
||||||
if (selectionDefault) {
|
if (selectionDefault) {
|
||||||
if (selectionDefault[0] == k) {
|
if (selectionDefault[0] === k) {
|
||||||
isSelected = 'selected';
|
isSelected = 'selected';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -400,8 +402,8 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
'<b>Status:</b> <div id="camera_status"></div>' +
|
'<b>Status:</b> <div id="camera_status"></div>' +
|
||||||
'img: <img width="100" id="camera_image">' +
|
'img: <img width="100" id="camera_image">' +
|
||||||
'canvas: <canvas id="canvas" width="1" height="1"></canvas>' +
|
'canvas: <canvas id="canvas" width="1" height="1"></canvas>' +
|
||||||
'</div>',
|
'</div>';
|
||||||
options_div = '<h2>Cordova Camera API Options</h2>' +
|
var options_div = '<h2>Cordova Camera API Options</h2>' +
|
||||||
'<div id="image-options">' +
|
'<div id="image-options">' +
|
||||||
createOptionsEl('sourceType', Camera.PictureSourceType, camPictureSourceTypeDefault) +
|
createOptionsEl('sourceType', Camera.PictureSourceType, camPictureSourceTypeDefault) +
|
||||||
createOptionsEl('destinationType', Camera.DestinationType, camDestinationTypeDefault) +
|
createOptionsEl('destinationType', Camera.DestinationType, camDestinationTypeDefault) +
|
||||||
@@ -414,9 +416,9 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
createOptionsEl('correctOrientation', true, camCorrectOrientationDefault) +
|
createOptionsEl('correctOrientation', true, camCorrectOrientationDefault) +
|
||||||
createOptionsEl('saveToPhotoAlbum', true, camSaveToPhotoAlbumDefault) +
|
createOptionsEl('saveToPhotoAlbum', true, camSaveToPhotoAlbumDefault) +
|
||||||
createOptionsEl('cameraDirection', Camera.Direction) +
|
createOptionsEl('cameraDirection', Camera.Direction) +
|
||||||
'</div>',
|
'</div>';
|
||||||
getpicture_div = '<div id="getpicture"></div>',
|
var getpicture_div = '<div id="getpicture"></div>';
|
||||||
test_procedure = '<h4>Recommended Test Procedure</h4>' +
|
var test_procedure = '<h4>Recommended Test Procedure</h4>' +
|
||||||
'Options not specified should be the default value' +
|
'Options not specified should be the default value' +
|
||||||
'<br>Status box should update with image and info whenever an image is taken or selected from library' +
|
'<br>Status box should update with image and info whenever an image is taken or selected from library' +
|
||||||
'</p><div style="background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto">' +
|
'</p><div style="background:#B0C4DE;border:1px solid #FFA07A;margin:15px 6px 0px;min-width:295px;max-width:97%;padding:4px 0px 2px 10px;min-height:160px;max-height:200px;overflow:auto">' +
|
||||||
@@ -429,14 +431,14 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
'</p><li>sourceType=PHOTOLIBRARY<br>mediaType=ALLMEDIA<br>allowEdit=true<br>Should be able to select pics and videos and edit picture if selected</li>' +
|
'</p><li>sourceType=PHOTOLIBRARY<br>mediaType=ALLMEDIA<br>allowEdit=true<br>Should be able to select pics and videos and edit picture if selected</li>' +
|
||||||
'</p><li>sourceType=CAMERA<br>targetWidth & targetHeight=50<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with width and height=800. Size should be significantly larger.</li>' +
|
'</p><li>sourceType=CAMERA<br>targetWidth & targetHeight=50<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with width and height=800. Size should be significantly larger.</li>' +
|
||||||
'</p><li>quality=0<br>targetWidth & targetHeight=default<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with quality=80. Size should be significantly larger.</li>' +
|
'</p><li>quality=0<br>targetWidth & targetHeight=default<br>allowEdit=false<br>Do Get File Metadata test below and take note of size<br>Repeat test but with quality=80. Size should be significantly larger.</li>' +
|
||||||
'</ol></div>',
|
'</ol></div>';
|
||||||
inputs_div = '<h2>Native File Inputs</h2>' +
|
var inputs_div = '<h2>Native File Inputs</h2>' +
|
||||||
'For the following tests, status box should update with file selected' +
|
'For the following tests, status box should update with file selected' +
|
||||||
'</p><div>input type=file <input type="file" class="testInputTag"></div>' +
|
'</p><div>input type=file <input type="file" class="testInputTag"></div>' +
|
||||||
'<div>capture=camera <input type="file" accept="image/*;capture=camera" class="testInputTag"></div>' +
|
'<div>capture=camera <input type="file" accept="image/*;capture=camera" class="testInputTag"></div>' +
|
||||||
'<div>capture=camcorder <input type="file" accept="video/*;capture=camcorder" class="testInputTag"></div>' +
|
'<div>capture=camcorder <input type="file" accept="video/*;capture=camcorder" class="testInputTag"></div>' +
|
||||||
'<div>capture=microphone <input type="file" accept="audio/*;capture=microphone" class="testInputTag"></div>',
|
'<div>capture=microphone <input type="file" accept="audio/*;capture=microphone" class="testInputTag"></div>';
|
||||||
actions_div = '<h2>Actions</h2>' +
|
var actions_div = '<h2>Actions</h2>' +
|
||||||
'For the following tests, ensure that an image is set in status box' +
|
'For the following tests, ensure that an image is set in status box' +
|
||||||
'</p><div id="metadata"></div>' +
|
'</p><div id="metadata"></div>' +
|
||||||
'Expected result: Get metadata about file selected.<br>Status box will show, along with the metadata, "Call to FileEntry.getMetadata success, Call to FileEntry.setMetadata success, Call to FileEntry.getParent success"' +
|
'Expected result: Get metadata about file selected.<br>Status box will show, along with the metadata, "Call to FileEntry.getMetadata success, Call to FileEntry.setMetadata success, Call to FileEntry.getParent success"' +
|
||||||
@@ -456,20 +458,20 @@ exports.defineManualTests = function (contentEl, createActionButton) {
|
|||||||
// We need to wrap this code due to Windows security restrictions
|
// We need to wrap this code due to Windows security restrictions
|
||||||
// see http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences for details
|
// see http://msdn.microsoft.com/en-us/library/windows/apps/hh465380.aspx#differences for details
|
||||||
if (window.MSApp && window.MSApp.execUnsafeLocalFunction) {
|
if (window.MSApp && window.MSApp.execUnsafeLocalFunction) {
|
||||||
MSApp.execUnsafeLocalFunction(function() {
|
MSApp.execUnsafeLocalFunction(function () {
|
||||||
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
contentEl.innerHTML = info_div + options_div + getpicture_div + test_procedure + inputs_div + actions_div;
|
||||||
}
|
}
|
||||||
|
|
||||||
var elements = document.getElementsByClassName("testInputTag");
|
var elements = document.getElementsByClassName('testInputTag');
|
||||||
var listener = function (e) {
|
var listener = function (e) {
|
||||||
testInputTag(e.target);
|
testInputTag(e.target);
|
||||||
};
|
};
|
||||||
for (var i = 0; i < elements.length; ++i) {
|
for (var i = 0; i < elements.length; ++i) {
|
||||||
var item = elements[i];
|
var item = elements[i];
|
||||||
item.addEventListener("change", listener, false);
|
item.addEventListener('change', listener, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
createActionButton('Get picture', function () {
|
createActionButton('Get picture', function () {
|
||||||
|
|||||||
Vendored
+174
@@ -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;
|
||||||
|
}
|
||||||
|
};
|
||||||
+12
-12
@@ -19,11 +19,11 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var argscheck = require('cordova/argscheck'),
|
var argscheck = require('cordova/argscheck');
|
||||||
exec = require('cordova/exec'),
|
var exec = require('cordova/exec');
|
||||||
Camera = require('./Camera');
|
var Camera = require('./Camera');
|
||||||
// XXX: commented out
|
// XXX: commented out
|
||||||
//CameraPopoverHandle = require('./CameraPopoverHandle');
|
// CameraPopoverHandle = require('./CameraPopoverHandle');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @namespace navigator
|
* @namespace navigator
|
||||||
@@ -66,7 +66,7 @@ for (var key in Camera) {
|
|||||||
* @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 {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.DestinationType} [destinationType=FILE_URI] - Choose the format of the return value.
|
||||||
* @property {module:Camera.PictureSourceType} [sourceType=CAMERA] - Set the source of the picture.
|
* @property {module:Camera.PictureSourceType} [sourceType=CAMERA] - Set the source of the picture.
|
||||||
* @property {Boolean} [allowEdit=true] - Allow simple editing of image before selection.
|
* @property {Boolean} [allowEdit=false] - Allow simple editing of image before selection.
|
||||||
* @property {module:Camera.EncodingType} [encodingType=JPEG] - Choose the returned image file's encoding.
|
* @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} [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 {number} [targetHeight] - Height in pixels to scale image. Must be used with `targetWidth`. Aspect ratio remains constant.
|
||||||
@@ -131,7 +131,7 @@ for (var key in Camera) {
|
|||||||
* @param {module:camera.onError} errorCallback
|
* @param {module:camera.onError} errorCallback
|
||||||
* @param {module:camera.CameraOptions} options CameraOptions
|
* @param {module:camera.CameraOptions} options CameraOptions
|
||||||
*/
|
*/
|
||||||
cameraExport.getPicture = function(successCallback, errorCallback, options) {
|
cameraExport.getPicture = function (successCallback, errorCallback, options) {
|
||||||
argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
|
argscheck.checkArgs('fFO', 'Camera.getPicture', arguments);
|
||||||
options = options || {};
|
options = options || {};
|
||||||
var getValue = argscheck.getValue;
|
var getValue = argscheck.getValue;
|
||||||
@@ -150,11 +150,11 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
|
|||||||
var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
|
var cameraDirection = getValue(options.cameraDirection, Camera.Direction.BACK);
|
||||||
|
|
||||||
var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
|
var args = [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
|
||||||
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
|
mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
|
||||||
|
|
||||||
exec(successCallback, errorCallback, "Camera", "takePicture", args);
|
exec(successCallback, errorCallback, 'Camera', 'takePicture', args);
|
||||||
// XXX: commented out
|
// XXX: commented out
|
||||||
//return new CameraPopoverHandle();
|
// return new CameraPopoverHandle();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,8 +178,8 @@ cameraExport.getPicture = function(successCallback, errorCallback, options) {
|
|||||||
* alert('Failed because: ' + message);
|
* alert('Failed because: ' + message);
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
cameraExport.cleanup = function(successCallback, errorCallback) {
|
cameraExport.cleanup = function (successCallback, errorCallback) {
|
||||||
exec(successCallback, errorCallback, "Camera", "cleanup", []);
|
exec(successCallback, errorCallback, 'Camera', 'cleanup', []);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = cameraExport;
|
module.exports = cameraExport;
|
||||||
|
|||||||
+75
-75
@@ -23,79 +23,79 @@
|
|||||||
* @module Camera
|
* @module Camera
|
||||||
*/
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* Defines the output format of `Camera.getPicture` call.
|
* Defines the output format of `Camera.getPicture` call.
|
||||||
* _Note:_ On iOS passing `DestinationType.NATIVE_URI` along with
|
* _Note:_ On iOS passing `DestinationType.NATIVE_URI` along with
|
||||||
* `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM` will
|
* `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM` will
|
||||||
* disable any image modifications (resize, quality change, cropping, etc.) due
|
* disable any image modifications (resize, quality change, cropping, etc.) due
|
||||||
* to implementation specific.
|
* to implementation specific.
|
||||||
*
|
*
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
DestinationType:{
|
DestinationType: {
|
||||||
/** 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 */
|
/** 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,
|
DATA_URL: 0,
|
||||||
/** Return file uri (content://media/external/images/media/2 for Android) */
|
/** Return file uri (content://media/external/images/media/2 for Android) */
|
||||||
FILE_URI: 1,
|
FILE_URI: 1,
|
||||||
/** Return native uri (eg. asset-library://... for iOS) */
|
/** Return native uri (eg. asset-library://... for iOS) */
|
||||||
NATIVE_URI: 2
|
NATIVE_URI: 2
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
EncodingType:{
|
EncodingType: {
|
||||||
/** Return JPEG encoded image */
|
/** Return JPEG encoded image */
|
||||||
JPEG: 0,
|
JPEG: 0,
|
||||||
/** Return PNG encoded image */
|
/** Return PNG encoded image */
|
||||||
PNG: 1
|
PNG: 1
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
MediaType:{
|
MediaType: {
|
||||||
/** Allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType */
|
/** Allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType */
|
||||||
PICTURE: 0,
|
PICTURE: 0,
|
||||||
/** Allow selection of video only, ONLY RETURNS URL */
|
/** Allow selection of video only, ONLY RETURNS URL */
|
||||||
VIDEO: 1,
|
VIDEO: 1,
|
||||||
/** Allow selection from all media types */
|
/** Allow selection from all media types */
|
||||||
ALLMEDIA : 2
|
ALLMEDIA: 2
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @description
|
* @description
|
||||||
* Defines the output format of `Camera.getPicture` call.
|
* Defines the output format of `Camera.getPicture` call.
|
||||||
* _Note:_ On iOS passing `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM`
|
* _Note:_ On iOS passing `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM`
|
||||||
* along with `DestinationType.NATIVE_URI` will disable any image modifications (resize, quality
|
* along with `DestinationType.NATIVE_URI` will disable any image modifications (resize, quality
|
||||||
* change, cropping, etc.) due to implementation specific.
|
* change, cropping, etc.) due to implementation specific.
|
||||||
*
|
*
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
PictureSourceType:{
|
PictureSourceType: {
|
||||||
/** Choose image from the device's photo library (same as SAVEDPHOTOALBUM for Android) */
|
/** Choose image from the device's photo library (same as SAVEDPHOTOALBUM for Android) */
|
||||||
PHOTOLIBRARY : 0,
|
PHOTOLIBRARY: 0,
|
||||||
/** Take picture from camera */
|
/** Take picture from camera */
|
||||||
CAMERA : 1,
|
CAMERA: 1,
|
||||||
/** Choose image only from the device's Camera Roll album (same as PHOTOLIBRARY for Android) */
|
/** Choose image only from the device's Camera Roll album (same as PHOTOLIBRARY for Android) */
|
||||||
SAVEDPHOTOALBUM : 2
|
SAVEDPHOTOALBUM: 2
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Matches iOS UIPopoverArrowDirection constants to specify arrow location on popover.
|
* Matches iOS UIPopoverArrowDirection constants to specify arrow location on popover.
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
PopoverArrowDirection:{
|
PopoverArrowDirection: {
|
||||||
ARROW_UP : 1,
|
ARROW_UP: 1,
|
||||||
ARROW_DOWN : 2,
|
ARROW_DOWN: 2,
|
||||||
ARROW_LEFT : 4,
|
ARROW_LEFT: 4,
|
||||||
ARROW_RIGHT : 8,
|
ARROW_RIGHT: 8,
|
||||||
ARROW_ANY : 15
|
ARROW_ANY: 15
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* @enum {number}
|
* @enum {number}
|
||||||
*/
|
*/
|
||||||
Direction:{
|
Direction: {
|
||||||
/** Use the back-facing camera */
|
/** Use the back-facing camera */
|
||||||
BACK: 0,
|
BACK: 0,
|
||||||
/** Use the front-facing camera */
|
/** Use the front-facing camera */
|
||||||
FRONT: 1
|
FRONT: 1
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -23,8 +23,8 @@
|
|||||||
* @ignore in favour of iOS' one
|
* @ignore in favour of iOS' one
|
||||||
* A handle to an image picker popover.
|
* A handle to an image picker popover.
|
||||||
*/
|
*/
|
||||||
var CameraPopoverHandle = function() {
|
var CameraPopoverHandle = function () {
|
||||||
this.setPosition = function(popoverOptions) {
|
this.setPosition = function (popoverOptions) {
|
||||||
console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
|
console.log('CameraPopoverHandle.setPosition is only supported on iOS.');
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
var Camera = require('./Camera');
|
var Camera = require('./Camera');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @namespace navigator
|
* @namespace navigator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -1,82 +0,0 @@
|
|||||||
<!--
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
-->
|
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<script src="camera.js" type="text/javascript"></script>
|
|
||||||
<script>
|
|
||||||
var meta = document.createElement("meta");
|
|
||||||
meta.setAttribute('name','viewport');
|
|
||||||
meta.setAttribute('content','initial-scale='+ (1/window.devicePixelRatio) + ',user-scalable=no');
|
|
||||||
document.getElementsByTagName('head')[0].appendChild(meta);
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<style type="text/css">
|
|
||||||
body {
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
.action-bar {
|
|
||||||
position: fixed;
|
|
||||||
left: 0px;
|
|
||||||
bottom: 0px;
|
|
||||||
height: 100px;
|
|
||||||
width: 100%;
|
|
||||||
background-image: linear-gradient(rgb(41, 41, 41), rgb(32, 32, 32));
|
|
||||||
}
|
|
||||||
.action-bar-back {
|
|
||||||
width: 78px;
|
|
||||||
height: 100px;
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
.action-bar-divider {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 0px;
|
|
||||||
left: 78px;
|
|
||||||
width: 28px;
|
|
||||||
height: 100px;
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAABkCAYAAACVfFA4AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3ggLDiUo1ElOKAAABHdJREFUaN612ktvE1cUB/Bz7bGdGcePJk1i4tA0DomCRJUQEGphAa3KQwGpUHWFYMEiIBZdtKgfADbdsekXaFW1XZUuQAiFh0BRi+iqap2EJrUSJSjg2I7Hk3GmdmwPi5Z4ziQEx57/XeXKUn6255w558y1ICKTUOvKverfpkl0/ahwEXKZ1c/iLhlERAQDu85etcrkX3mGBbPhPiIhXnkUyPyNBfO+1upGEIUyU1iQXG52LSd/HRNY0BIwPiNbfR8Iq+3CN5brZ5KiJrBgLrCT7cPpKSxYlPyWgBE0e/d7AQMHDh0nEhavVOSx5DRodA6zm6WSX8SC2dYBFjCvEh4GrsjtbB9MT2BB0+Vh++nx2wIG7jp2liW8VNQ23oAc/To79lavH5nkV+ewoBrqsVak9Rs2DCx4Q6xCfDbUKcHA/R+dsnydRKJSputfXyvDwHSgl1cIPbl51XLs+rXvYQnfnJ3BgprSyfah1AQWrLh9ZI2YxKNfBAzs//A0a2/da/nXdx5OgHpkH9sr2jwWVFv6eIVIT2HBVV8Lq/DB9CQWZP/GrNDTJw8FDIydHGUB4zXStb61OvOvY5BXiOUEFlSb3+EVYouAcQQsSTKrEHP3fxQwsP/gCdYSukqF7YRXHS1hlLeE8v8zIAzMtu62tYTTWDAvt7HrF0jFsaApJBahM7+NCRjYe+wcS3hPIbfde9I2W8LIEK8Q2QQWVEMxFjDB1CQWLHoCrEIs3P1OwMD/WkKLV16rp67UvpYCu1jCN+kvsKC9JfQvT2NBXdnB9rUkfEMgawkF0ez4TQEDe4+c4S1hMV9vM1Jjw7RjH3vapGwyAzoKqm/1sSmp+Q0VvmHQ8IVZwn85MuSFguwpYaVMX31+eQ0Gdo9cZDOgdzXdSAdbQ0sYGeQBk5nBgiuBnSxggqm/sGDJrbCAmX/0s4CBvQdPsIR3rRmNTiFvagmtM6BJsraABXMtA+xYwJ96igVXlTZbhZjAgqZLYl9p4vf7Agb2fHyOPyU0VCdG1y3yr3Mvr/A1toR1g1qwh+39S3EsWPTylnDxwQ8CBg7bW0LbsYDj4FKQt4Q+/TkW1Nre4+dIqSksqPt5SxhMxbFgxW09FhA09/iOgIGxw2fY9XMXdSefWW0yUkeHWYTK6iwW1MJ9tC6aJvlrnAHrBo0m/pTwxtVRFxQk6+8KKmU69P4BEwZ2j4w23BJuC1Q7hnjC284BHQd128FxczKOBcuSwhL+2fgNAQNjHxznLWHJIKfWpqDRxY8FmnILWFCzPSV0IuG3BPPK2yzho//OYUESHjYl/fHgloCB3UfPs4T3GMvk5NoA5iODvEJk/sGCWijGK8RSHAvaW8LnD38SUJDFTrlITi8GRj/5gr3oW1nEglo7bwnlOmfAmkFdiVjv1xRI/okFTclrHQFp/smYgIHvHv6U/5awoBFirYOr0WH2gpydxYK5cD+vEA4n/Aaw0BRiCZ+8962AgrwlLBFquYiIuk5ess2ASRgoERHp7XtIUGUdlTPTVACBLwEignyX5voIbgAAAABJRU5ErkJggg==);
|
|
||||||
}
|
|
||||||
.action-bar-back-button {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 35px;
|
|
||||||
left: 34px;
|
|
||||||
width: 18px;
|
|
||||||
height: 28px;
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAAcCAYAAABsxO8nAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3ggLDh43ZfOs5QAAAvdJREFUOMuVlU9LMm0Uh6+ZRguUKaNoWbs0QVIMCb9CqQtb1UdI2lS0dBNhn6D2EVQQLaM2UZpTUknRwm3UooI2lTZ/9H42b/M+g2k+A/fmPjcXF+f8OAMg/vVEIhFRLBbF4+Oj0DRNjI2NCem/YsdfJBJhf38fWZbtu4eHBzoGSZJELBZjZ2cHSZKa6nKnkKmpKQ4ODgAQQjhOpVL53UiWZaanp9nY2EDX9ab6+/s76XS6vVFXVxepVIrNzc0fIU9PTySTSSqVCkoriKIozM7OksvlqNVqjpoQwoa8vb217pHL5SKTybC+vk61WnX0o9FocH9/TyKRsCFAs5Hb7SaTybC8vMzn56ej1mg0uL29ZWZmhnq93npqHo+HhYUFlpaW+Pj4sC0ALMuiUCiQTqebIA4jr9dLNptlbm7OYSJJEqZpcnx8zPz8fOuIAEJVVdbW1kilUo7GSpJErVbj8PCQxcXF9lnz+XxidXWVRCLRNJ1qtcr29ja5XO7X0MorKyskk8kmiCzLHB0ddQQBkF9eXnh9fUWSpKboK4rCwMBAR6CuYrGYdbvdBAIBuru77SkJIQiFQoyMjKBpGtVqtT0IyF5cXKAoCuFwGEX5P1qWZeH3+xkeHiafz/P19dUeBFAqlejr68Pv9+NyuWwz0zQJBAKoqkq5XG7qZRMI4OzsDFVVCQQCuFwu+5FhGExMTKCqKtfX1z/CHCCAQqGA1+tlfHzcHgCArutEo1E8Hg+lUgnDMNqDAM7PzxkcHMTv9yPLsg0zDINoNIosy9zd3TlWy48ggJOTE/r7+wkGg47Vapom8XgcRVG4ubmxzVqCAE5PT+nt7SUcDtNoNOx7XdeZnJwE4OrqCsuy2oMA8vk8Q0NDBINBxzYwDINYLMb3xH8FfZv5fD5CoZAN+s5ZPB5H07TOQEII8vk8qqoSjUaxLMu2M02T5+fnzn5HAPV6nVwux+7uLm63277v6emhXC53ZvT3qr28vMTn8zE6OgrA3t4eW1tb/AH4Zo+8fEoEngAAAABJRU5ErkJggg==);
|
|
||||||
}
|
|
||||||
.camera-cross-hairs {
|
|
||||||
position: fixed;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
-webkit-transform: translate(-50%, -147px);
|
|
||||||
width: 194px;
|
|
||||||
height: 195px;
|
|
||||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAQAAAB6tAYfAAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfeCAsPDxicOdibAAACbElEQVR42u3a7U7aYBgG4Oet0yZLTMx2bDu5nYo7nSUmzi34DUSe/bA0UHSDbpS5Xpc/sECNuW/a5yV5I4CI8toL03y31R94ipMy7gj/PKmNp7/lWSx27PEuzkZaxFWe7viJL3EdH8ovS5hnRkTu+K9UcVzGehX0u/WsXxNVt4LcuYLoccb/ok9WGRHzfKWEWS56xvkw2hLue1a3WKuh9L+wluqRD+bH7BdA6Q7qr59mOW1/LBr3Wdky5VlenK9dCattjv2TPdwwXy5oqu4iSQX7VpfsjPVq7OubQ1i8VMLFefndF2j24i7b1O/z6LWJzT4Wtp28K5EcnhKUgBKUgBKUgBKUgBKUgBKUgBKUgBKUgBLeSglP7eFMIgPo5t3Zd2TDyzCWe4/q1X1HPz5HREykM5BJyBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWhfn05zl95TEMC5zmrO8bPIuzw+zfD44LgIawjxzJe8qIuI2MzIyFnHnWhjATS6avG+zLaFqXz6W0ACO2t9OYj1/DkYJSkAJSkAJSkAJSkAJSkAJSkAJSkAJSkAJb6WE6y+COIzbiGh3WzxmaQ5P7LfYu4dc3oDq4nb0j8yA5ni50yWbHUjsz2bCTQmTlakwVcMeraZ73TyWzftUiYiMjO2Gw1O8H/kUuc+jrd6XUZpsY20ixGrSy+HceXoLN/FxpEVc5elO71+9ydTlxbT73oiq0e5hneei55l1eWVQ1z2jHO8Qyb9QwcZqqS6TPuve8a74e5wz2fiwl22m+C6tjnu9IyngTfsJ8D+4z5r15Y4AAAAASUVORK5CYII=);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<body>
|
|
||||||
<div id="camera">
|
|
||||||
<video id="v"></video>
|
|
||||||
<div class="camera-cross-hairs"></div>
|
|
||||||
</div>
|
|
||||||
<canvas id="c" style="display: none;"></canvas>
|
|
||||||
<div class="action-bar">
|
|
||||||
<div id="back" class="action-bar-back">
|
|
||||||
<div class="action-bar-back-button"></div>
|
|
||||||
</div>
|
|
||||||
<div class="action-bar-divider"></div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function () {
|
|
||||||
document.getElementById('back').onclick = function () {
|
|
||||||
window.qnx.callExtensionMethod('cordova-plugin-camera', 'cancel');
|
|
||||||
};
|
|
||||||
window.navigator.webkitGetUserMedia(
|
|
||||||
{ video: true },
|
|
||||||
function (stream) {
|
|
||||||
var video = document.getElementById('v'),
|
|
||||||
canvas = document.getElementById('c'),
|
|
||||||
camera = document.getElementById('camera');
|
|
||||||
video.autoplay = true;
|
|
||||||
video.width = window.innerWidth;
|
|
||||||
video.height = window.innerHeight - 100;
|
|
||||||
video.src = window.webkitURL.createObjectURL(stream);
|
|
||||||
camera.onclick = function () {
|
|
||||||
canvas.width = video.videoWidth;
|
|
||||||
canvas.height = video.videoHeight;
|
|
||||||
canvas.getContext('2d').drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
|
|
||||||
window.qnx.callExtensionMethod('cordova-plugin-camera', canvas.toDataURL('img/png'));
|
|
||||||
};
|
|
||||||
},
|
|
||||||
function () {
|
|
||||||
window.qnx.callExtensionMethod('cordova-plugin-camera', 'error', 'getUserMedia failed');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -48,7 +48,7 @@ var exec = require('cordova/exec');
|
|||||||
* }
|
* }
|
||||||
* @module CameraPopoverHandle
|
* @module CameraPopoverHandle
|
||||||
*/
|
*/
|
||||||
var CameraPopoverHandle = function() {
|
var CameraPopoverHandle = function () {
|
||||||
/**
|
/**
|
||||||
* Can be used to reposition the image selection dialog,
|
* Can be used to reposition the image selection dialog,
|
||||||
* for example, when the device orientation changes.
|
* for example, when the device orientation changes.
|
||||||
@@ -57,9 +57,9 @@ var CameraPopoverHandle = function() {
|
|||||||
* @method setPosition
|
* @method setPosition
|
||||||
* @param {module:CameraPopoverOptions} popoverOptions
|
* @param {module:CameraPopoverOptions} popoverOptions
|
||||||
*/
|
*/
|
||||||
this.setPosition = function(popoverOptions) {
|
this.setPosition = function (popoverOptions) {
|
||||||
var args = [ popoverOptions ];
|
var args = [ popoverOptions ];
|
||||||
exec(null, null, "Camera", "repositionPopover", args);
|
exec(null, null, 'Camera', 'repositionPopover', args);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user