Compare commits

...

32 Commits

Author SHA1 Message Date
4afa471fb6 修改压缩比例quality为100 2024-03-30 23:45:02 +08:00
a19ba493b3 修改压缩比例 2024-03-30 23:18:10 +08:00
c95f4f7501 修改ios选择,删除拍照按钮的位置 2024-03-30 20:51:49 +08:00
6c38d126ec 修改ios选择,删除拍照按钮的位置 2024-03-30 20:20:33 +08:00
ffaef73016 修改ios选择,删除拍照按钮的位置 2024-03-30 20:02:52 +08:00
4d59795df1 修改ios选择,删除拍照按钮的位置 2024-03-30 20:02:02 +08:00
6e82b526a2 修改ios选择,删除拍照按钮的位置 2024-03-30 19:46:27 +08:00
f13d7c7aa4 压缩比例改为100 2024-03-30 19:02:26 +08:00
2c804cea55 修改“拍照按钮”的高度y="513" 改为 y="463"
修改“自拍按钮”的位置 x="180" 改为 x="230"
2024-03-28 10:01:53 +08:00
976623696@qq.com
6d76d1defe 照片增加裁剪 2020-12-30 19:04:53 +08:00
zher52
47da7a7c29 修复iso13相机未全屏的问题 2020-12-30 14:27:57 +08:00
zher52
739cb20679 ios合并cordova camera 的取相册照片的方法 2020-06-01 16:00:56 +08:00
zher52
9782d52be4 fix 修复Failed to find configured root that contains错误 2020-05-13 13:24:29 +08:00
zher52
852531344a fix provider冲突造成的无法获取路径的问题 2020-05-13 12:00:50 +08:00
zher52
07e7444ee1 调整readme 2020-05-12 12:28:33 +08:00
zher52
ad603fcdaa 修改Readme 2020-05-12 12:23:25 +08:00
zher52
0a2c778c5f 调整ios端代码为custom项目代码 避免疯狂调用导致的webview崩溃 2020-05-12 12:10:19 +08:00
Jesse MacFadyen
ba4f77468f chore(release): 4.2.0-dev 2020-05-07 01:34:03 -07:00
Jesse MacFadyen
adf15799d9 Updated version and RELEASENOTES.md for release (Camera-4.2.0) 2020-05-07 00:45:53 -07:00
Jesse MacFadyen
e9aba07926
Merge pull request #588 from purplecabbage/AndroidCacheLocal
[Android] Cache images in device storage, devices have enough space now.
2020-04-30 10:46:21 -07:00
Jesse MacFadyen
df14414203 Cache images in device storage, devices have enough space now. 2020-04-28 13:57:00 -07:00
Norman Breau
2a750f0fa1
Merge pull request #508 from rtpHarry/patch-1
docs(readme): app renamed to Google Photos
2020-04-24 23:09:49 -03:00
Erisu
06ac9b3dc9
chore(asf): update git notification settings 2020-04-22 13:39:50 +09:00
jcesarmobile
bfa38fed2c
fix(ios): return copy of video when picking from gallery on iOS 13 (#580) 2020-04-15 11:41:21 +02:00
Niklas Merz
7611960ef2 Update CONTRIBUTING.md 2020-04-13 14:27:10 +02:00
exxbrain
f4067e22d9
Fix UI API called on a background thread (#550, #530, #447) (#551)
Co-authored-by: Denis Zakharov <d.zakharov@bssys.com>
2020-04-09 13:54:26 +00:00
Tim Brust
f33ade83e4
ci: updates Node.js versions (#576) 2020-03-28 12:11:08 +00:00
Tim Brust
6f25420ce7
chore(npm): adds ignore list (#575) 2020-03-28 12:10:44 +00:00
jcesarmobile
6feaa95559
docs(README): remove confusing comment (#513) 2019-10-20 01:05:19 +02:00
jcesarmobile
c887c229f0
docs(README): remove orphan windows phone 7 note (#512) 2019-10-19 17:07:21 +02:00
Matthew Harris
6900fbaefa
docs(readme): app renamed to Google Photos 2019-09-25 22:04:31 +07:00
klodha
a9436b1a18 ImagePicker returning same image (#306)
ImagePicker returning same image

Co-authored-by: Jan Piotrowski <piotrowski+github@gmail.com>
Co-authored-by: Tim Brust <github@timbrust.de>
2019-09-16 11:40:54 +02:00
38 changed files with 2215 additions and 781 deletions

View File

@ -12,12 +12,16 @@ image:
- Visual Studio 2017
environment:
nodejs_version: "6"
matrix:
- PLATFORM: windows-10-store
- nodejs_version: "10"
- nodejs_version: "12"
platform:
- x86
- x64
install:
- ps: Install-Product node $env:nodejs_version
- node --version
- npm install -g github:apache/cordova-paramedic
- npm install -g cordova
@ -25,4 +29,4 @@ install:
build: off
test_script:
- cordova-paramedic --config pr\%PLATFORM% --plugin . --justBuild
- cordova-paramedic --config pr\windows-10-store --plugin . --justBuild

22
.asf.yaml Normal file
View File

@ -0,0 +1,22 @@
# 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.
notifications:
commits: commits@cordova.apache.org
issues: issues@cordova.apache.org
pullrequests_status: issues@cordova.apache.org
pullrequests_comment: issues@cordova.apache.org

3
.npmignore Normal file
View File

@ -0,0 +1,3 @@
.*
appveyor.yml
tests

View File

@ -1,4 +1,4 @@
# This Travis configuration file is built after a Cordova Paramedic
# This Travis configuration file is built after a Cordova Paramedic
# specific template with minimal modifications and adaptations:
# https://github.com/apache/cordova-paramedic/blob/master/.travis.yml
@ -12,18 +12,18 @@ addons:
env:
global:
- SAUCE_USERNAME=snay
- TRAVIS_NODE_VERSION=8
- TRAVIS_NODE_VERSION=12
- ANDROID_API_LEVEL=28
- ANDROID_BUILD_TOOLS_VERSION=28.0.3
language: node_js
node_js: 8
node_js: 12
# yaml anchor/alias: https://medium.com/@tommyvn/travis-yml-dry-with-anchors-8b6a3ac1b027
_ios: &_ios
os: osx
osx_image: xcode10.2
osx_image: xcode10.3
_android: &_android
language: android
@ -35,9 +35,9 @@ _android: &_android
- build-tools-$ANDROID_BUILD_TOOLS_VERSION
- android-$ANDROID_API_LEVEL
licenses:
- 'android-sdk-preview-license-.+'
- 'android-sdk-license-.+'
- 'google-gdk-license-.+'
- "android-sdk-preview-license-.+"
- "android-sdk-license-.+"
- "google-gdk-license-.+"
matrix:
include:
@ -81,13 +81,13 @@ matrix:
before_install:
# manually install Node for `language: android`
- if [[ "$PLATFORM" =~ android ]]; then nvm install $TRAVIS_NODE_VERSION; fi
- if [[ "$PLATFORM" =~ android ]]; then nvm install $TRAVIS_NODE_VERSION; fi
- node --version
- if [[ "$PLATFORM" =~ android ]]; then gradle --version; fi
- if [[ "$PLATFORM" =~ ios ]]; then npm install -g ios-deploy; fi
- npm install -g cordova
# install paramedic if not running on paramedic repo
- if ! [[ "$TRAVIS_REPO_SLUG" =~ cordova-paramedic ]]; then npm install -g github:apache/cordova-paramedic; fi
- if ! [[ "$TRAVIS_REPO_SLUG" =~ cordova-paramedic ]]; then npm install -g github:apache/cordova-paramedic; fi
install:
- npm install
@ -106,11 +106,12 @@ before_script:
PARAMEDIC_COMMAND="cordova-paramedic"
fi
- PARAMEDIC_BUILDNAME=travis-$TRAVIS_REPO_SLUG-$TRAVIS_JOB_NUMBER
script:
- $TEST_COMMAND
- if [[ "$ADDITIONAL_TESTS_DIR" != "" ]];
- |
if [[ "$ADDITIONAL_TESTS_DIR" != "" ]];
then cd $ADDITIONAL_TESTS_DIR && npm install && npm test;
else
$PARAMEDIC_COMMAND --config ./pr/$PLATFORM --plugin $PARAMEDIC_PLUGIN_TO_TEST --buildName $PARAMEDIC_BUILDNAME;
else
$PARAMEDIC_COMMAND --config ./pr/$PLATFORM --plugin $PARAMEDIC_PLUGIN_TO_TEST --buildName $PARAMEDIC_BUILDNAME;
fi

View File

@ -25,13 +25,13 @@ Anyone can contribute to Cordova. And we need your contributions.
There are multiple ways to contribute: report bugs, improve the docs, and
contribute code.
For instructions on this, start with the
For instructions on this, start with the
[contribution overview](http://cordova.apache.org/contribute/).
The details are explained there, but the important items are:
- Sign and submit an Apache ICLA (Contributor License Agreement).
- Have a Jira issue open that corresponds to your contribution.
- Check for Github issues that corresponds to your contribution and link or create them if necessary.
- Run the tests so your patch doesn't break existing functionality.
We look forward to your contributions!

724
README.md
View File

@ -2,718 +2,24 @@
title: Camera
description: Take pictures with the device camera.
---
<!---
# license: Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# 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.
-->
# cordova-plugin-splashscreen
|AppVeyor|Travis CI|
|:-:|:-:|
|[![Build status](https://ci.appveyor.com/api/projects/status/github/apache/cordova-plugin-camera?branch=master)](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-camera)|[![Build Status](https://travis-ci.org/apache/cordova-plugin-camera.svg?branch=master)](https://travis-ci.org/apache/cordova-plugin-camera)|
# cordova-plugin-camera
This plugin defines a global `navigator.camera` object, which provides an API for taking pictures and for choosing images from
the system's image library.
Although the object is attached to the global scoped `navigator`, it is not available until after the `deviceready` event.
document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
console.log(navigator.camera);
}
在 [cordova-plugin-camera](https://github.com/apache/cordova-plugin-camera.git) 的基础上修改了ios端的代码为[customCamera](https://github.com/geneanet/customCamera.git)的ios的代码,防止ios疯狂调用造成的webview卡死的问题
- [Installation](#installation)
- [Preferences](#preferences)
- [Other](#other)
## Installation
This requires cordova 5.0+
cordova plugin add cordova-plugin-camera
Older versions of cordova can still install via the __deprecated__ id
cordova plugin add org.apache.cordova.camera
It is also possible to install via repo url directly ( unstable )
cordova plugin add https://github.com/apache/cordova-plugin-camera.git
## How to Contribute
Contributors are welcome! And we need your contributions to keep the project moving forward. You can[report bugs, 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).
**Have a solution?** Send a [Pull Request](https://github.com/apache/cordova-plugin-camera/pulls).
In order for your changes to be accepted, you need to sign and submit an Apache [ICLA](http://www.apache.org/licenses/#clas) (Individual Contributor License Agreement). Then your name will appear on the list of CLAs signed by [non-committers](https://people.apache.org/committer-index.html#unlistedclas) or [Cordova committers](http://people.apache.org/committers-by-project.html#cordova).
**And don't forget to test and document your code.**
### iOS Quirks
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.
This plugins requires the following usage descriptions:
- `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 these entries into the `info.plist`, you can use the `edit-config` tag in the `config.xml` like this:
```
<edit-config target="NSCameraUsageDescription" file="*-Info.plist" mode="merge">
<string>need camera access to take pictures</string>
</edit-config>
```
```
<edit-config target="NSPhotoLibraryUsageDescription" file="*-Info.plist" mode="merge">
<string>need photo library access to get pictures from there</string>
</edit-config>
```
```
<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>
```
---
# API Reference <a name="reference"></a>
* [camera](#module_camera)
* [.getPicture(successCallback, errorCallback, options)](#module_camera.getPicture)
* [.cleanup()](#module_camera.cleanup)
* [.onError](#module_camera.onError) : <code>function</code>
* [.onSuccess](#module_camera.onSuccess) : <code>function</code>
* [.CameraOptions](#module_camera.CameraOptions) : <code>Object</code>
* [Camera](#module_Camera)
* [.DestinationType](#module_Camera.DestinationType) : <code>enum</code>
* [.EncodingType](#module_Camera.EncodingType) : <code>enum</code>
* [.MediaType](#module_Camera.MediaType) : <code>enum</code>
* [.PictureSourceType](#module_Camera.PictureSourceType) : <code>enum</code>
* [.PopoverArrowDirection](#module_Camera.PopoverArrowDirection) : <code>enum</code>
* [.Direction](#module_Camera.Direction) : <code>enum</code>
* [CameraPopoverHandle](#module_CameraPopoverHandle)
* [CameraPopoverOptions](#module_CameraPopoverOptions)
---
<a name="module_camera"></a>
## camera
<a name="module_camera.getPicture"></a>
### camera.getPicture(successCallback, errorCallback, options)
Takes a photo using the camera, or retrieves a photo from the device's
image gallery. The image is passed to the success callback as a
Base64-encoded `String`, or as the URI for the image file.
The `camera.getPicture` function opens the device's default camera
application that allows users to snap pictures by default - this behavior occurs,
when `Camera.sourceType` equals [`Camera.PictureSourceType.CAMERA`](#module_Camera.PictureSourceType).
Once the user snaps the photo, the camera application closes and the application is restored.
If `Camera.sourceType` is `Camera.PictureSourceType.PHOTOLIBRARY` or
`Camera.PictureSourceType.SAVEDPHOTOALBUM`, then a dialog displays
that allows users to select an existing image.
The return value is sent to the [`cameraSuccess`](#module_camera.onSuccess) callback function, in
one of the following formats, depending on the specified
`cameraOptions`:
- A `String` containing the Base64-encoded photo image.
- A `String` representing the image file location on local storage (default).
You can do whatever you want with the encoded image or URI, for
example:
- Render the image in an `<img>` tag, as in the example below
- Save the data locally (`LocalStorage`, [Lawnchair](http://brianleroux.github.com/lawnchair/), etc.)
- Post the data to a remote server
__NOTE__: Photo resolution on newer devices is quite good. Photos
selected from the device's gallery are not downscaled to a lower
quality, even if a `quality` parameter is specified. To avoid common
memory problems, set `Camera.destinationType` to `FILE_URI` rather
than `DATA_URL`.
__Supported Platforms__
- Android
- Browser
- iOS
- Windows
- OSX
More examples [here](#camera-getPicture-examples). Quirks [here](#camera-getPicture-quirks).
**Kind**: static method of <code>[camera](#module_camera)</code>
| Param | Type | Description |
| --- | --- | --- |
| successCallback | <code>[onSuccess](#module_camera.onSuccess)</code> | |
| errorCallback | <code>[onError](#module_camera.onError)</code> | |
| options | <code>[CameraOptions](#module_camera.CameraOptions)</code> | CameraOptions |
**Example**
```js
navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
```
<a name="module_camera.cleanup"></a>
### camera.cleanup()
Removes intermediate image files that are kept in temporary storage
after calling [`camera.getPicture`](#module_camera.getPicture). Applies only when the value of
`Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the
`Camera.destinationType` equals `Camera.DestinationType.FILE_URI`.
__Supported Platforms__
- iOS
**Kind**: static method of <code>[camera](#module_camera)</code>
**Example**
```js
navigator.camera.cleanup(onSuccess, onFail);
function onSuccess() {
console.log("Camera cleanup success.")
}
function onFail(message) {
alert('Failed because: ' + message);
}
```
<a name="module_camera.onError"></a>
### camera.onError : <code>function</code>
Callback function that provides an error message.
**Kind**: static typedef of <code>[camera](#module_camera)</code>
| Param | Type | Description |
| --- | --- | --- |
| message | <code>string</code> | The message is provided by the device's native code. |
<a name="module_camera.onSuccess"></a>
### camera.onSuccess : <code>function</code>
Callback function that provides the image data.
**Kind**: static typedef of <code>[camera](#module_camera)</code>
| Param | Type | Description |
| --- | --- | --- |
| imageData | <code>string</code> | Base64 encoding of the image data, _or_ the image file URI, depending on [`cameraOptions`](#module_camera.CameraOptions) in effect. |
**Example**
```js
// Show image
//
function cameraCallback(imageData) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + imageData;
}
```
<a name="module_camera.CameraOptions"></a>
### camera.CameraOptions : <code>Object</code>
Optional parameters to customize the camera settings.
* [Quirks](#CameraOptions-quirks)
**Kind**: static typedef of <code>[camera](#module_camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| quality | <code>number</code> | <code>50</code> | Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. (Note that information about the camera's resolution is unavailable.) |
| destinationType | <code>[DestinationType](#module_Camera.DestinationType)</code> | <code>FILE_URI</code> | Choose the format of the return value. |
| sourceType | <code>[PictureSourceType](#module_Camera.PictureSourceType)</code> | <code>CAMERA</code> | Set the source of the picture. |
| allowEdit | <code>Boolean</code> | <code>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. |
| targetWidth | <code>number</code> | | Width in pixels to scale image. Must be used with `targetHeight`. Aspect ratio remains constant. |
| targetHeight | <code>number</code> | | Height in pixels to scale image. Must be used with `targetWidth`. Aspect ratio remains constant. |
| mediaType | <code>[MediaType](#module_Camera.MediaType)</code> | <code>PICTURE</code> | Set the type of media to select from. Only works when `PictureSourceType` is `PHOTOLIBRARY` or `SAVEDPHOTOALBUM`. |
| correctOrientation | <code>Boolean</code> | | Rotate the image to correct for the orientation of the device during capture. |
| saveToPhotoAlbum | <code>Boolean</code> | | Save the image to the photo album on the device after capture. |
| popoverOptions | <code>[CameraPopoverOptions](#module_CameraPopoverOptions)</code> | | iOS-only options that specify popover location in iPad. |
| cameraDirection | <code>[Direction](#module_Camera.Direction)</code> | <code>BACK</code> | Choose the camera to use (front- or back-facing). |
---
<a name="module_Camera"></a>
## Camera
<a name="module_Camera.DestinationType"></a>
### Camera.DestinationType : <code>enum</code>
Defines the output format of `Camera.getPicture` call.
_Note:_ On iOS passing `DestinationType.NATIVE_URI` along with
`PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM` will
disable any image modifications (resize, quality change, cropping, etc.) due
to implementation specific.
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| DATA_URL | <code>number</code> | <code>0</code> | Return base64 encoded string. DATA_URL can be very memory intensive and cause app crashes or out of memory errors. Use FILE_URI or NATIVE_URI if possible |
| FILE_URI | <code>number</code> | <code>1</code> | Return file uri (content://media/external/images/media/2 for Android) |
| NATIVE_URI | <code>number</code> | <code>2</code> | Return native uri (eg. asset-library://... for iOS) |
<a name="module_Camera.EncodingType"></a>
### Camera.EncodingType : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| JPEG | <code>number</code> | <code>0</code> | Return JPEG encoded image |
| PNG | <code>number</code> | <code>1</code> | Return PNG encoded image |
<a name="module_Camera.MediaType"></a>
### Camera.MediaType : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| PICTURE | <code>number</code> | <code>0</code> | Allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType |
| VIDEO | <code>number</code> | <code>1</code> | Allow selection of video only, ONLY RETURNS URL |
| ALLMEDIA | <code>number</code> | <code>2</code> | Allow selection from all media types |
<a name="module_Camera.PictureSourceType"></a>
### Camera.PictureSourceType : <code>enum</code>
Defines the output format of `Camera.getPicture` call.
_Note:_ On iOS passing `PictureSourceType.PHOTOLIBRARY` or `PictureSourceType.SAVEDPHOTOALBUM`
along with `DestinationType.NATIVE_URI` will disable any image modifications (resize, quality
change, cropping, etc.) due to implementation specific.
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| PHOTOLIBRARY | <code>number</code> | <code>0</code> | Choose image from the device's photo library (same as SAVEDPHOTOALBUM for Android) |
| CAMERA | <code>number</code> | <code>1</code> | Take picture from camera |
| SAVEDPHOTOALBUM | <code>number</code> | <code>2</code> | Choose image only from the device's Camera Roll album (same as PHOTOLIBRARY for Android) |
<a name="module_Camera.PopoverArrowDirection"></a>
### Camera.PopoverArrowDirection : <code>enum</code>
Matches iOS UIPopoverArrowDirection constants to specify arrow location on popover.
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default |
| --- | --- | --- |
| ARROW_UP | <code>number</code> | <code>1</code> |
| ARROW_DOWN | <code>number</code> | <code>2</code> |
| ARROW_LEFT | <code>number</code> | <code>4</code> |
| ARROW_RIGHT | <code>number</code> | <code>8</code> |
| ARROW_ANY | <code>number</code> | <code>15</code> |
<a name="module_Camera.Direction"></a>
### Camera.Direction : <code>enum</code>
**Kind**: static enum property of <code>[Camera](#module_Camera)</code>
**Properties**
| Name | Type | Default | Description |
| --- | --- | --- | --- |
| BACK | <code>number</code> | <code>0</code> | Use the back-facing camera |
| FRONT | <code>number</code> | <code>1</code> | Use the front-facing camera |
---
<a name="module_CameraPopoverOptions"></a>
## CameraPopoverOptions
iOS-only parameters that specify the anchor element location and arrow
direction of the popover when selecting images from an iPad's library
or album.
Note that the size of the popover may change to adjust to the
direction of the arrow and orientation of the screen. Make sure to
account for orientation changes when specifying the anchor element
location.
| Param | Type | Default | Description |
| --- | --- | --- | --- |
| [x] | <code>Number</code> | <code>0</code> | x pixel coordinate of screen element onto which to anchor the popover. |
| [y] | <code>Number</code> | <code>32</code> | y pixel coordinate of screen element onto which to anchor the popover. |
| [width] | <code>Number</code> | <code>320</code> | width, in pixels, of the screen element onto which to anchor the popover. |
| [height] | <code>Number</code> | <code>480</code> | height, in pixels, of the screen element onto which to anchor the popover. |
| [arrowDir] | <code>[PopoverArrowDirection](#module_Camera.PopoverArrowDirection)</code> | <code>ARROW_ANY</code> | Direction the arrow on the popover should point. |
| [popoverWidth] | <code>Number</code> | <code>0</code> | width of the popover (0 or not specified will use apple's default width). |
| [popoverHeight] | <code>Number</code> | <code>0</code> | height of the popover (0 or not specified will use apple's default height). |
---
<a name="module_CameraPopoverHandle"></a>
## CameraPopoverHandle
A handle to an image picker popover.
__Supported Platforms__
- iOS
**Example**
```js
navigator.camera.getPicture(onSuccess, onFail,
{
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
popoverOptions: new CameraPopoverOptions(300, 300, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY, 300, 600)
});
// Reposition the popover if the orientation changes.
window.onorientationchange = function() {
var cameraPopoverHandle = new CameraPopoverHandle();
var cameraPopoverOptions = new CameraPopoverOptions(0, 0, 100, 100, Camera.PopoverArrowDirection.ARROW_ANY, 400, 500);
cameraPopoverHandle.setPosition(cameraPopoverOptions);
}
```
---
## `camera.getPicture` Errata
#### Example <a name="camera-getPicture-examples"></a>
Take a photo and retrieve the image's file location:
navigator.camera.getPicture(onSuccess, onFail, { quality: 50,
destinationType: Camera.DestinationType.FILE_URI });
function onSuccess(imageURI) {
var image = document.getElementById('myImage');
image.src = imageURI;
}
function onFail(message) {
alert('Failed because: ' + message);
}
Take a photo and retrieve it as a Base64-encoded image:
/**
* Warning: Using DATA_URL is not recommended! The DATA_URL destination
* type is very memory intensive, even with a low quality setting. Using it
* can result in out of memory errors and application crashes. Use FILE_URI
* or NATIVE_URI instead.
*/
navigator.camera.getPicture(onSuccess, onFail, { quality: 25,
destinationType: Camera.DestinationType.DATA_URL
});
function onSuccess(imageData) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + imageData;
}
function onFail(message) {
alert('Failed because: ' + message);
}
#### Preferences (iOS)
- __CameraUsesGeolocation__ (boolean, defaults to false). For capturing JPEGs, set to true to get geolocation data in the EXIF header. This will trigger a request for geolocation permissions if set to true.
<preference name="CameraUsesGeolocation" value="false" />
#### Android Quirks
Android uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the result from the plugin call will be delivered via the resume event.
See [the Android Lifecycle guide][android_lifecycle]
for more information. The `pendingResult.result` value will contain the value that
would be passed to the callbacks (either the URI/URL or an error message). Check
the `pendingResult.pluginStatus` to determine whether or not the call was
successful.
#### Browser Quirks
Can only return photos as Base64-encoded image.
#### iOS Quirks
Including a JavaScript `alert()` in either of the callback functions
can cause problems. Wrap the alert within a `setTimeout()` to allow
the iOS image picker or popover to fully close before the alert
displays:
setTimeout(function() {
// do your thing here!
}, 0);
#### Windows Phone 7 Quirks
Invoking the native camera application while the device is connected
via Zune does not work, and triggers an error callback.
#### Windows quirks
On Windows Phone 8.1 using `SAVEDPHOTOALBUM` or `PHOTOLIBRARY` as a source type causes application to suspend until file picker returns the selected image and
then restore with start page as defined in app's `config.xml`. In case when `camera.getPicture` was called from different page, this will lead to reloading
start page from scratch and success and error callbacks will never be called.
To avoid this we suggest using SPA pattern or call `camera.getPicture` only from your app's start page.
More information about Windows Phone 8.1 picker APIs is here: [How to continue your Windows Phone app after calling a file picker](https://msdn.microsoft.com/en-us/library/windows/apps/dn720490.aspx)
## `CameraOptions` Errata <a name="CameraOptions-quirks"></a>
#### Android Quirks
- Any `cameraDirection` value results in a back-facing photo. (= You can only use the back camera)
- **`allowEdit` is unpredictable on Android and it should not be used!** The Android implementation of this plugin tries to find and use an application on the user's device to do image cropping. The plugin has no control over what application the user selects to perform the image cropping and it is very possible that the user could choose an incompatible option and cause the plugin to fail. This sometimes works because most devices come with an application that handles cropping in a way that is compatible with this plugin (Google Plus Photos), but it is unwise to rely on that being the case. If image editing is essential to your application, consider seeking a third party library or plugin that provides its own image editing utility for a more robust solution.
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
- Ignores the `encodingType` parameter if the image is unedited (i.e. `quality` is 100, `correctOrientation` is false, and no `targetHeight` or `targetWidth` are specified). The `CAMERA` source will always return the JPEG file given by the native camera and the `PHOTOLIBRARY` and `SAVEDPHOTOALBUM` sources will return the selected file in its existing encoding.
#### iOS Quirks
- When using `destinationType.FILE_URI`, photos are saved in the application's temporary directory. The contents of the application's temporary directory is deleted when the application ends.
- When using `destinationType.NATIVE_URI` and `sourceType.CAMERA`, photos are saved in the saved photo album regardless on the value of `saveToPhotoAlbum` parameter.
- When using `destinationType.NATIVE_URI` and `sourceType.PHOTOLIBRARY` or `sourceType.SAVEDPHOTOALBUM`, all editing options are ignored and link is returned to original picture.
[android_lifecycle]: http://cordova.apache.org/docs/en/dev/guide/platforms/android/lifecycle.html
## Sample: Take Pictures, Select Pictures from the Picture Library, and Get Thumbnails <a name="sample"></a>
The Camera plugin allows you to do things like open the device's Camera app and take a picture, or open the file picker and select one. The code snippets in this section demonstrate different tasks including:
* Open the Camera app and [take a Picture](#takePicture)
* Take a picture and [return thumbnails](#getThumbnails) (resized picture)
* Take a picture and [generate a FileEntry object](#convert)
* [Select a file](#selectFile) from the picture library
* Select a JPEG image and [return thumbnails](#getFileThumbnails) (resized image)
* Select an image and [generate a FileEntry object](#convert)
## Take a Picture <a name="takePicture"></a>
Before you can take a picture, you need to set some Camera plugin options to pass into the Camera plugin's `getPicture` function. Here is a common set of recommendations. In this example, you create the object that you will use for the Camera options, and set the `sourceType` dynamically to support both the Camera app and the file picker.
```js
function setOptions(srcType) {
var options = {
// Some common settings are 20, 50, and 100
quality: 50,
destinationType: Camera.DestinationType.FILE_URI,
// In this app, dynamically set the picture source, Camera or photo gallery
sourceType: srcType,
encodingType: Camera.EncodingType.JPEG,
mediaType: Camera.MediaType.PICTURE,
allowEdit: true,
correctOrientation: true //Corrects Android orientation quirks
}
return options;
}
```
Typically, you want to use a FILE_URI instead of a DATA_URL to avoid most memory issues. JPEG is the recommended encoding type for Android.
You take a picture by passing in the options object to `getPicture`, which takes a CameraOptions object as the third argument. When you call `setOptions`, pass `Camera.PictureSourceType.CAMERA` as the picture source.
```js
function openCamera(selection) {
var srcType = Camera.PictureSourceType.CAMERA;
var options = setOptions(srcType);
var func = createNewFileEntry;
navigator.camera.getPicture(function cameraSuccess(imageUri) {
displayImage(imageUri);
// You may choose to copy the picture, save it somewhere, or upload.
func(imageUri);
}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");
}, options);
}
```
Once you take the picture, you can display it or do something else. In this example, call the app's `displayImage` function from the preceding code.
```js
function displayImage(imgUri) {
var elem = document.getElementById('imageFile');
elem.src = imgUri;
}
```
To display the image on some platforms, you might need to include the main part of the URI in the Content-Security-Policy `<meta>` element in index.html. For example, on Windows 10, you can include `ms-appdata:` in your `<meta>` element. Here is an example.
```html
<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: ms-appdata: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">
```
## Take a Picture and Return Thumbnails (Resize the Picture) <a name="getThumbnails"></a>
To get smaller images, you can return a resized image by passing both `targetHeight` and `targetWidth` values with your CameraOptions object. In this example, you resize the returned image to fit in a 100px by 100px box (the aspect ratio is maintained, so 100px is either the height or width, whichever is greater in the source).
```js
function openCamera(selection) {
var srcType = Camera.PictureSourceType.CAMERA;
var options = setOptions(srcType);
var func = createNewFileEntry;
if (selection == "camera-thmb") {
options.targetHeight = 100;
options.targetWidth = 100;
}
navigator.camera.getPicture(function cameraSuccess(imageUri) {
// Do something
}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");
}, options);
}
```
## Select a File from the Picture Library <a name="selectFile"></a>
When selecting a file using the file picker, you also need to set the CameraOptions object. In this example, set the `sourceType` to `Camera.PictureSourceType.SAVEDPHOTOALBUM`. To open the file picker, call `getPicture` just as you did in the previous example, passing in the success and error callbacks along with CameraOptions object.
```js
function openFilePicker(selection) {
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
var options = setOptions(srcType);
var func = createNewFileEntry;
navigator.camera.getPicture(function cameraSuccess(imageUri) {
// Do something
}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");
}, options);
}
```
## Select an Image and Return Thumbnails (resized images) <a name="getFileThumbnails"></a>
Resizing a file selected with the file picker works just like resizing using the Camera app; set the `targetHeight` and `targetWidth` options.
```js
function openFilePicker(selection) {
var srcType = Camera.PictureSourceType.SAVEDPHOTOALBUM;
var options = setOptions(srcType);
var func = createNewFileEntry;
if (selection == "picker-thmb") {
// To downscale a selected image,
// Camera.EncodingType (e.g., JPEG) must match the selected image type.
options.targetHeight = 100;
options.targetWidth = 100;
}
navigator.camera.getPicture(function cameraSuccess(imageUri) {
// Do something with image
}, function cameraError(error) {
console.debug("Unable to obtain picture: " + error, "app");
}, options);
}
```
## Take a picture and get a FileEntry Object <a name="convert"></a>
If you want to do something like copy the image to another location, or upload it somewhere using the FileTransfer plugin, you need to get a FileEntry object for the returned picture. To do that, call `window.resolveLocalFileSystemURL` on the file URI returned by the Camera app. If you need to use a FileEntry object, set the `destinationType` to `Camera.DestinationType.FILE_URI` in your CameraOptions object (this is also the default value).
>*Note* You need the [File plugin](https://www.npmjs.com/package/cordova-plugin-file) to call `window.resolveLocalFileSystemURL`.
Here is the call to `window.resolveLocalFileSystemURL`. The image URI is passed to this function from the success callback of `getPicture`. The success handler of `resolveLocalFileSystemURL` receives the FileEntry object.
```js
function getFileEntry(imgUri) {
window.resolveLocalFileSystemURL(imgUri, function success(fileEntry) {
// Do something with the FileEntry object, like write to it, upload it, etc.
// writeFile(fileEntry, imgUri);
console.log("got file: " + fileEntry.fullPath);
// displayFileData(fileEntry.nativeURL, "Native URL");
}, function () {
// If don't get the FileEntry (which may happen when testing
// on some emulators), copy to a new FileEntry.
createNewFileEntry(imgUri);
});
}
```
In the example shown in the preceding code, you call the app's `createNewFileEntry` function if you don't get a valid FileEntry object. The image URI returned from the Camera app should result in a valid FileEntry, but platform behavior on some emulators may be different for files returned from the file picker.
>*Note* To see an example of writing to a FileEntry, see the [File plugin README](https://www.npmjs.com/package/cordova-plugin-file).
The code shown here creates a file in your app's cache (in sandboxed storage) named `tempFile.jpeg`. With the new FileEntry object, you can copy the image to the file or do something else like upload it.
```js
function createNewFileEntry(imgUri) {
window.resolveLocalFileSystemURL(cordova.file.cacheDirectory, function success(dirEntry) {
// JPEG file
dirEntry.getFile("tempFile.jpeg", { create: true, exclusive: false }, function (fileEntry) {
// Do something with it, like write to it, upload it, etc.
// writeFile(fileEntry, imgUri);
console.log("got file: " + fileEntry.fullPath);
// displayFileData(fileEntry.fullPath, "File copied to");
}, onErrorCreateFile);
}, onErrorResolveUrl);
}
```bash
# 安装前请先卸载 `cordova-plugin-camera``org.geneanet.customCamera` 插件
cordova plugin rm cordova-plugin-camera
cordova plugin rm org.geneanet.customCamera
# 安装插件
cordova plugin add https://gitee.com/shuto/cordova-plugin-camera.git
```
## Preferences
- iOS端由于代码调整,导致原插件仅支持 `quality` `saveToPhotoAlbum` 参数,返回数据格式为 **base64**;
## Other
- 其他配置及插件使用方法请移步[cordova-plugin-camera](https://github.com/apache/cordova-plugin-camera.git)

View File

@ -20,6 +20,19 @@
-->
# Release Notes
### 4.2.0 (May 07, 2020)
* Cache images in device storage, devices have enough space now.
* docs(readme): app renamed to Google Photos
* chore(asf): update git notification settings
* fix(ios): return copy of video when picking from gallery on **iOS** 13 (#580)
* Update CONTRIBUTING.md
* Fix UI API called on a background thread (#550, #530, #447) (#551)
* ci: updates Node.js versions (#576)
* chore(npm): adds ignore list (#575)
* docs(README): remove confusing comment (#513)
* docs(README): remove orphan **Windows** phone 7 note (#512)
* ImagePicker returning same image (#306)
### 4.1.0 (Jun 27, 2019)
- docs: remove outdated test docs translations ([`06dc38f`](https://github.com/apache/cordova-plugin-camera/commit/06dc38f))

View File

@ -1,6 +1,6 @@
{
"name": "cordova-plugin-camera",
"version": "4.1.1-dev",
"version": "4.2.0-dev",
"description": "Cordova Camera Plugin",
"types": "./types/index.d.ts",
"cordova": {

View File

@ -21,7 +21,7 @@
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
xmlns:android="http://schemas.android.com/apk/res/android"
id="cordova-plugin-camera"
version="4.1.1-dev">
version="4.2.0-dev">
<name>Camera</name>
<description>Cordova Camera Plugin</description>
<license>Apache 2.0</license>
@ -59,7 +59,7 @@
<config-file target="AndroidManifest.xml" parent="application">
<provider
android:name="org.apache.cordova.camera.FileProvider"
android:authorities="${applicationId}.provider"
android:authorities="${applicationId}.camera.provider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
@ -84,8 +84,8 @@
</platform>
<!-- ios -->
<platform name="ios">
<!-- old plugin's ios -->
<!-- <platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="Camera">
<param name="ios-package" value="CDVCamera" />
@ -112,8 +112,71 @@
<framework src="CoreGraphics.framework" />
<framework src="AVFoundation.framework" />
</platform>
</platform> -->
<!-- ios -->
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="Camera">
<param name="ios-package" value="CustomCamera" />
</feature>
</config-file>
<config-file target="config.xml" parent="/*">
<feature name="LocalStorage">
<param name="ios-package" value="CDVLocalStorage" />
</feature>
</config-file>
<header-file src="src/ios/UIImage+CropScaleOrientation.h" />
<source-file src="src/ios/UIImage+CropScaleOrientation.m" />
<header-file src="src/ios/classes/CustomCamera.h" />
<source-file src="src/ios/classes/CustomCamera.m" />
<header-file src="src/ios/classes/AVCamPreviewView.h" />
<source-file src="src/ios/classes/AVCamPreviewView.m" />
<header-file src="src/ios/classes/AVCamViewController.h" />
<source-file src="src/ios/classes/AVCamViewController.m" />
<header-file src="src/ios/classes/CameraParameter.h" />
<source-file src="src/ios/classes/CameraParameter.m" />
<resource-file src="src/ios/classes/AVCamViewController_iPhone.xib" />
<resource-file src="src/ios/classes/AVCamViewController_iPad.xib" />
<resource-file src="src/ios/image/icon_back.png" />
<resource-file src="src/ios/image/icon_capture_pressed.png" />
<resource-file src="src/ios/image/icon_capture.png" />
<resource-file src="src/ios/image/icon_delete.png" />
<resource-file src="src/ios/image/icon_flash_auto.png" />
<resource-file src="src/ios/image/icon_flash_off.png" />
<resource-file src="src/ios/image/icon_flash.png" />
<resource-file src="src/ios/image/icon_flip.png" />
<resource-file src="src/ios/image/icon_max.png" />
<resource-file src="src/ios/image/icon_min.png" />
<resource-file src="src/ios/image/icon_submit.png" />
<resource-file src="src/ios/image/sample.png" />
<framework src="CoreGraphics.framework" weak="true" />
<framework src="AssetsLibrary.framework" weak="true" />
<framework src="AVFoundation.framework" weak="true" />
<framework src="CoreAudio.framework" weak="true" />
<framework src="CoreLocation.framework" weak="true" />
<framework src="MobileCoreServices.framework" weak="true" />
<preference name="CAMERA_USAGE_DESCRIPTION" default="This app requires access to your camera to take pictures" />
<config-file target="*-Info.plist" parent="NSCameraUsageDescription">
<string>$CAMERA_USAGE_DESCRIPTION</string>
</config-file>
<preference name="MICROPHONE_USAGE_DESCRIPTION" default="This app requires access to your microphone to take pictures" />
<config-file target="*-Info.plist" parent="NSMicrophoneUsageDescription">
<string>$MICROPHONE_USAGE_DESCRIPTION</string>
</config-file>
<preference name="PHOTO_LIBRARY_ADD_USAGE_DESCRIPTION" default="This app requires access to your photo library to save your pictures" />
<config-file target="*-Info.plist" parent="NSPhotoLibraryAddUsageDescription">
<string>$PHOTO_LIBRARY_ADD_USAGE_DESCRIPTION</string>
</config-file>
</platform>
<!-- browser -->
<platform name="browser">
<config-file target="config.xml" parent="/*">

View File

@ -37,7 +37,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v4.content.FileProvider;
import android.util.Base64;
import org.apache.cordova.BuildHelper;
@ -155,7 +154,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
this.targetWidth = 0;
this.encodingType = JPEG;
this.mediaType = PICTURE;
this.mQuality = 50;
this.mQuality = 100;
//Take the values from the arguments if they're not already defined (this is tricky)
this.destType = args.getInt(1);
@ -220,17 +219,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
//--------------------------------------------------------------------------
private String getTempDirectoryPath() {
File cache = null;
// SD Card Mounted
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
cache = cordova.getActivity().getExternalCacheDir();
}
// Use internal storage
else {
cache = cordova.getActivity().getCacheDir();
}
File cache = cordova.getActivity().getCacheDir();
// Create the cache directory if it doesn't exist
cache.mkdirs();
return cache.getAbsolutePath();
@ -300,8 +289,8 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
// Specify file so that large image is captured and returned
File photo = createCaptureFile(encodingType);
this.imageUri = new CordovaUri(FileProvider.getUriForFile(cordova.getActivity(),
applicationId + ".provider",
this.imageUri = new CordovaUri(cordova.getActivity().getCacheDir() ,FileProvider.getUriForFile(cordova.getActivity(),
applicationId + ".camera.provider",
photo));
intent.putExtra(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
@ -799,7 +788,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
try {
if (this.allowEdit) {
Uri tmpFile = FileProvider.getUriForFile(cordova.getActivity(),
applicationId + ".provider",
applicationId + ".camera.provider",
createCaptureFile(this.encodingType));
performCrop(tmpFile, destType, intent);
} else {
@ -1375,7 +1364,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
if (state.containsKey(IMAGE_URI_KEY)) {
//I have no idea what type of URI is being passed in
this.imageUri = new CordovaUri(Uri.parse(state.getString(IMAGE_URI_KEY)));
this.imageUri = new CordovaUri(cordova.getActivity().getCacheDir(),Uri.parse(state.getString(IMAGE_URI_KEY)));
}
this.callbackContext = callbackContext;

View File

@ -43,13 +43,13 @@ public class CordovaUri {
* We always expect a FileProvider string to be passed in for the file that we create
*
*/
CordovaUri (Uri inputUri)
CordovaUri (File dir,Uri inputUri)
{
//Determine whether the file is a content or file URI
if(inputUri.getScheme().equals("content"))
{
androidUri = inputUri;
fileName = getFileNameFromUri(androidUri);
fileName = getFileNameFromUri(dir,androidUri);
fileUri = Uri.parse("file://" + fileName);
}
else
@ -93,10 +93,9 @@ public class CordovaUri {
* we own the context in this case.
*/
private String getFileNameFromUri(Uri uri) {
private String getFileNameFromUri(File external_storage,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;

View File

@ -17,5 +17,5 @@
-->
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
<cache-path name="external_files" path="."/>
</paths>

View File

@ -63,7 +63,7 @@ static NSString* toBase64(NSData* data) {
{
CDVPictureOptions* pictureOptions = [[CDVPictureOptions alloc] init];
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(50)];
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(100)];
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(UIImagePickerControllerSourceTypeCamera)] unsignedIntegerValue];
@ -185,16 +185,16 @@ static NSString* toBase64(NSData* data) {
- (void)showCameraPicker:(NSString*)callbackId withOptions:(CDVPictureOptions *) pictureOptions
{
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
self.pickerController = cameraPicker;
cameraPicker.delegate = self;
cameraPicker.callbackId = callbackId;
// we need to capture this state for memory warnings that dealloc this object
cameraPicker.webView = self.webView;
// Perform UI operations on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
self.pickerController = cameraPicker;
cameraPicker.delegate = self;
cameraPicker.callbackId = callbackId;
// we need to capture this state for memory warnings that dealloc this object
cameraPicker.webView = self.webView;
// If a popover is already open, close it; we only want one at a time.
if (([[self pickerController] pickerPopoverController] != nil) && [[[self pickerController] pickerPopoverController] isPopoverVisible]) {
[[[self pickerController] pickerPopoverController] dismissPopoverAnimated:YES];
@ -374,7 +374,7 @@ static NSString* toBase64(NSData* data) {
self.metadata = [[NSMutableDictionary alloc] init];
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
if (EXIFDictionary) {
if (EXIFDictionary) {
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
}
@ -399,10 +399,11 @@ static NSString* toBase64(NSData* data) {
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by Apple (vs [NSFileManager defaultManager]) to be threadsafe
NSString* filePath;
// generate unique file name
int i = 1;
// unique file name
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
NSNumber *timeStampObj = [NSNumber numberWithDouble: timeStamp];
do {
filePath = [NSString stringWithFormat:@"%@/%@%03d.%@", docsPath, CDV_PHOTO_PREFIX, i++, extension];
filePath = [NSString stringWithFormat:@"%@/%@%ld.%@", docsPath, CDV_PHOTO_PREFIX, [timeStampObj longValue], extension];
} while ([fileMgr fileExistsAtPath:filePath]);
return filePath;
@ -512,9 +513,22 @@ static NSString* toBase64(NSData* data) {
- (CDVPluginResult*)resultForVideo:(NSDictionary*)info
{
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString];
// On iOS 13 the movie path becomes inaccessible, create and return a copy
if (IsAtLeastiOSVersion(@"13.0")) {
moviePath = [self createTmpVideo:[[info objectForKey:UIImagePickerControllerMediaURL] path]];
}
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath];
}
- (NSString *) createTmpVideo:(NSString *) moviePath {
NSString* moviePathExtension = [moviePath pathExtension];
NSString* copyMoviePath = [self tempFilePath:moviePathExtension];
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError *error;
[fileMgr copyItemAtPath:moviePath toPath:copyMoviePath error:&error];
return [[NSURL fileURLWithPath:copyMoviePath] absoluteString];
}
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
@ -586,15 +600,15 @@ static NSString* toBase64(NSData* data) {
- (CLLocationManager*)locationManager
{
if (locationManager != nil) {
return locationManager;
}
if (locationManager != nil) {
return locationManager;
}
locationManager = [[CLLocationManager alloc] init];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager setDelegate:self];
locationManager = [[CLLocationManager alloc] init];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager setDelegate:self];
return locationManager;
return locationManager;
}
- (void)locationManager:(CLLocationManager*)manager didUpdateToLocation:(CLLocation*)newLocation fromLocation:(CLLocation*)oldLocation

View File

@ -0,0 +1,9 @@
#import <UIKit/UIKit.h>
@class AVCaptureSession;
@interface AVCamPreviewView : UIView
@property (nonatomic) AVCaptureSession *session;
@end

View File

@ -0,0 +1,21 @@
#import "AVCamPreviewView.h"
#import <AVFoundation/AVFoundation.h>
@implementation AVCamPreviewView
+ (Class)layerClass
{
return [AVCaptureVideoPreviewLayer class];
}
- (AVCaptureSession *)session
{
return [(AVCaptureVideoPreviewLayer *)[self layer] session];
}
- (void)setSession:(AVCaptureSession *)session
{
[(AVCaptureVideoPreviewLayer *)[self layer] setSession:session];
}
@end

View File

@ -0,0 +1,35 @@
#import <UIKit/UIKit.h>
#import "CameraParameter.h"
@interface AVCamViewController : UIViewController
{
NSData *_imageData;
void (^_callback)(UIImage *, NSString *, NSString *);
UIPinchGestureRecognizer *twoFingerPinch;
CGRect frameBtnThumb;
UIImage *capturedImage;
NSData *capturedImageData;
BOOL isRotated;
CGFloat fDist;
}
@property (weak, nonatomic) IBOutlet UIView *saveBgPanel;
@property (weak, nonatomic) IBOutlet UIView *topBgPanel;
@property (weak, nonatomic) IBOutlet UIImageView *capturedImageView;
@property (weak, nonatomic) IBOutlet UIButton *btnBigDeletePicture;
@property (weak, nonatomic) IBOutlet UIButton *btnDeletePicture;
@property (weak, nonatomic) IBOutlet UIButton *btnSaveImage;
@property (weak, nonatomic) IBOutlet UIButton *btnBigSaveImage;
@property (weak, nonatomic) IBOutlet UISlider *opacitySlider;
@property (nonatomic, retain) CameraParameter *params;
- (id)initWithParams:(CameraParameter *)parameter WithCallback:(void (^)(UIImage *, NSString *, NSString *))callback;
@end

View File

@ -0,0 +1,811 @@
#import "AVCamViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import "AVCamPreviewView.h"
static void *CapturingStillImageContext = &CapturingStillImageContext;
static void *RecordingContext = &RecordingContext;
static void *SessionRunningAndDeviceAuthorizedContext = &SessionRunningAndDeviceAuthorizedContext;
@interface AVCamViewController () <AVCaptureFileOutputRecordingDelegate>
// For use in the storyboards.
@property (nonatomic, weak) IBOutlet AVCamPreviewView *previewView;
@property (nonatomic, weak) IBOutlet UIButton *recordButton;
@property (nonatomic, weak) IBOutlet UIButton *cameraButton;
@property (nonatomic, weak) IBOutlet UIButton *stillButton;
@property (weak, nonatomic) IBOutlet UIButton *btnThumb;
@property (weak, nonatomic) IBOutlet UIButton *btnFlash;
@property (weak, nonatomic) IBOutlet UIButton *btnBack;
@property (nonatomic, weak) IBOutlet UIImageView *imgSmallThumbNail;
@property (nonatomic, weak) IBOutlet UIImageView *imgBigThumbNail;
- (IBAction)onTapThumb:(id)sender;
- (IBAction)onTapCameraFlash:(id)sender;
- (IBAction)onBack:(id)sender;
- (IBAction)toggleMovieRecording:(id)sender;
- (IBAction)changeCamera:(id)sender;
- (IBAction)snapStillImage:(id)sender;
- (IBAction)focusAndExposeTap:(UIGestureRecognizer *)gestureRecognizer;
// Session management.
@property (nonatomic) dispatch_queue_t sessionQueue; // Communicate with the session and other session objects on this queue.
@property (nonatomic) AVCaptureSession *session;
@property (nonatomic) AVCaptureDeviceInput *videoDeviceInput;
@property (nonatomic) AVCaptureMovieFileOutput *movieFileOutput;
@property (nonatomic) AVCaptureStillImageOutput *stillImageOutput;
// Utilities.
@property (nonatomic) UIBackgroundTaskIdentifier backgroundRecordingID;
@property (nonatomic, getter = isDeviceAuthorized) BOOL deviceAuthorized;
@property (nonatomic, readonly, getter = isSessionRunningAndDeviceAuthorized) BOOL sessionRunningAndDeviceAuthorized;
@property (nonatomic) BOOL lockInterfaceRotation;
@property (nonatomic) id runtimeErrorHandlingObserver;
@end
@implementation AVCamViewController
@synthesize params;
- (BOOL)isSessionRunningAndDeviceAuthorized {
return [[self session] isRunning] && [self isDeviceAuthorized];
}
+ (NSSet *)keyPathsForValuesAffectingSessionRunningAndDeviceAuthorized {
return [NSSet setWithObjects:@"session.running", @"deviceAuthorized", nil];
}
- (id)initWithParams:(CameraParameter *)parameter WithCallback:(void (^)(UIImage *, NSString *, NSString *))callback {
self = [super initWithNibName:nil bundle:nil];
self.params = parameter;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self = [super initWithNibName:@"AVCamViewController_iPad" bundle:nil];
}
else {
self = [super initWithNibName:@"AVCamViewController_iPhone" bundle:nil];
}
if (self) {
_callback = callback;
}
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Create the AVCaptureSession
AVCaptureSession *session = [[AVCaptureSession alloc] init];
[self setSession:session];
// Setup the preview view
[[self previewView] setSession:session];
// Check for device authorization
[self checkDeviceAuthorizationStatus];
// In general it is not safe to mutate an AVCaptureSession or any of its inputs, outputs, or connections from multiple threads at the same time.
// Why not do all of this on the main queue?
// -[AVCaptureSession startRunning] is a blocking call which can take a long time. We dispatch session setup to the sessionQueue so that the main queue isn't blocked (which keeps the UI responsive).
dispatch_queue_t sessionQueue = dispatch_queue_create("session queue", DISPATCH_QUEUE_SERIAL);
[self setSessionQueue:sessionQueue];
dispatch_async(sessionQueue, ^{
[self setBackgroundRecordingID:UIBackgroundTaskInvalid];
NSError *error = nil;
AVCaptureDevice *videoDevice;
if (params.nDefaultCamera == 0) {
videoDevice = [AVCamViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionBack];
}
else {
videoDevice = [AVCamViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:AVCaptureDevicePositionFront];
}
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&error];
dispatch_async(dispatch_get_main_queue(), ^{
if (![videoDevice hasTorch]) {
self.btnFlash.hidden = YES;
// self.cameraButton.center = self.btnThumb.center;
// self.btnThumb.center = self.btnFlash.center;
}
});
if (error) {
NSLog(@"%@", error);
}
if ([session canAddInput:videoDeviceInput]) {
[session addInput:videoDeviceInput];
[self setVideoDeviceInput:videoDeviceInput];
dispatch_async(dispatch_get_main_queue(), ^{
// Why are we dispatching this to the main queue?
// Because AVCaptureVideoPreviewLayer is the backing layer for AVCamPreviewView and UIView can only be manipulated on main thread.
// Note: As an exception to the above rule, it is not necessary to serialize video orientation changes on the AVCaptureVideoPreviewLayers connection with other session manipulation.
[[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] setVideoOrientation:(AVCaptureVideoOrientation)[[UIApplication sharedApplication] statusBarOrientation]];
});
}
AVCaptureDevice *audioDevice = [[AVCaptureDevice devicesWithMediaType:AVMediaTypeAudio] firstObject];
AVCaptureDeviceInput *audioDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
if (error) {
NSLog(@"%@", error);
}
if ([session canAddInput:audioDeviceInput]) {
[session addInput:audioDeviceInput];
}
AVCaptureMovieFileOutput *movieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
if ([session canAddOutput:movieFileOutput]) {
[session addOutput:movieFileOutput];
AVCaptureConnection *connection = [movieFileOutput connectionWithMediaType:AVMediaTypeVideo];
if ([connection isVideoStabilizationSupported])
//connection.preferredVideoStabilizationMode = AVCaptureVideoStabilizationModeAuto;
//[connection setEnablesVideoStabilizationWhenAvailable:YES];
[self setMovieFileOutput:movieFileOutput];
}
AVCaptureStillImageOutput *stillImageOutput = [[AVCaptureStillImageOutput alloc] init];
if ([session canAddOutput:stillImageOutput]) {
[stillImageOutput setOutputSettings:@{ AVVideoCodecKey : AVVideoCodecJPEG }];
[session addOutput:stillImageOutput];
[self setStillImageOutput:stillImageOutput];
}
});
[self initialize];
}
- (void)viewWillAppear:(BOOL)animated {
dispatch_async([self sessionQueue], ^{
[self addObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:SessionRunningAndDeviceAuthorizedContext];
[self addObserver:self forKeyPath:@"stillImageOutput.capturingStillImage" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:CapturingStillImageContext];
[self addObserver:self forKeyPath:@"movieFileOutput.recording" options:(NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew) context:RecordingContext];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:[[self videoDeviceInput] device]];
__weak AVCamViewController *weakSelf = self;
[self setRuntimeErrorHandlingObserver:[[NSNotificationCenter defaultCenter] addObserverForName:AVCaptureSessionRuntimeErrorNotification object:[self session] queue:nil usingBlock: ^(NSNotification *note) {
AVCamViewController *strongSelf = weakSelf;
dispatch_async([strongSelf sessionQueue], ^{
// Manually restarting the session since it must have been stopped due to an error.
[[strongSelf session] startRunning];
[[strongSelf recordButton] setTitle:NSLocalizedString(@"Record", @"Recording button record title") forState:UIControlStateNormal];
});
}]];
[[self session] startRunning];
});
}
- (void)viewDidDisappear:(BOOL)animated {
dispatch_async([self sessionQueue], ^{
[[self session] stopRunning];
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:[[self videoDeviceInput] device]];
[[NSNotificationCenter defaultCenter] removeObserver:[self runtimeErrorHandlingObserver]];
[self removeObserver:self forKeyPath:@"sessionRunningAndDeviceAuthorized" context:SessionRunningAndDeviceAuthorizedContext];
[self removeObserver:self forKeyPath:@"stillImageOutput.capturingStillImage" context:CapturingStillImageContext];
[self removeObserver:self forKeyPath:@"movieFileOutput.recording" context:RecordingContext];
});
}
- (BOOL)prefersStatusBarHidden {
return YES;
}
- (BOOL)shouldAutorotate {
// Disable autorotation of the interface when recording is in progress.
return ![self lockInterfaceRotation];
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskAll;
// return UIInterfaceOrientationMaskPortrait;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] setVideoOrientation:(AVCaptureVideoOrientation)toInterfaceOrientation];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == CapturingStillImageContext) {
BOOL isCapturingStillImage = [change[NSKeyValueChangeNewKey] boolValue];
if (isCapturingStillImage) {
[self runStillImageCaptureAnimation];
}
}
else if (context == RecordingContext) {
BOOL isRecording = [change[NSKeyValueChangeNewKey] boolValue];
dispatch_async(dispatch_get_main_queue(), ^{
if (isRecording) {
[[self cameraButton] setEnabled:NO];
[[self recordButton] setTitle:NSLocalizedString(@"Stop", @"Recording button stop title") forState:UIControlStateNormal];
[[self recordButton] setEnabled:YES];
}
else {
[[self cameraButton] setEnabled:YES];
[[self recordButton] setTitle:NSLocalizedString(@"Record", @"Recording button record title") forState:UIControlStateNormal];
[[self recordButton] setEnabled:YES];
}
});
}
else if (context == SessionRunningAndDeviceAuthorizedContext) {
BOOL isRunning = [change[NSKeyValueChangeNewKey] boolValue];
dispatch_async(dispatch_get_main_queue(), ^{
if (isRunning) {
[[self cameraButton] setEnabled:YES];
[[self recordButton] setEnabled:YES];
[[self stillButton] setEnabled:YES];
}
else {
[[self cameraButton] setEnabled:NO];
[[self recordButton] setEnabled:NO];
[[self stillButton] setEnabled:NO];
}
});
}
else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
#pragma mark Actions
- (IBAction)toggleMovieRecording:(id)sender {
[[self recordButton] setEnabled:NO];
dispatch_async([self sessionQueue], ^{
if (![[self movieFileOutput] isRecording]) {
[self setLockInterfaceRotation:YES];
if ([[UIDevice currentDevice] isMultitaskingSupported]) {
// Setup background task. This is needed because the captureOutput:didFinishRecordingToOutputFileAtURL: callback is not received until AVCam returns to the foreground unless you request background execution time. This also ensures that there will be time to write the file to the assets library when AVCam is backgrounded. To conclude this background execution, -endBackgroundTask is called in -recorder:recordingDidFinishToOutputFileURL:error: after the recorded file has been saved.
[self setBackgroundRecordingID:[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil]];
}
// Update the orientation on the movie file output video connection before starting recording.
[[[self movieFileOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] videoOrientation]];
// Turning OFF flash for video recording
[AVCamViewController setFlashMode:AVCaptureFlashModeOff forDevice:[[self videoDeviceInput] device]];
// Start recording to a temporary file.
NSString *outputFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[@"movie" stringByAppendingPathExtension:@"mov"]];
[[self movieFileOutput] startRecordingToOutputFileURL:[NSURL fileURLWithPath:outputFilePath] recordingDelegate:self];
}
else {
[[self movieFileOutput] stopRecording];
}
});
}
- (IBAction)changeCamera:(id)sender {
[[self cameraButton] setEnabled:NO];
[[self recordButton] setEnabled:NO];
[[self stillButton] setEnabled:NO];
dispatch_async([self sessionQueue], ^{
AVCaptureDevice *currentVideoDevice = [[self videoDeviceInput] device];
AVCaptureDevicePosition preferredPosition = AVCaptureDevicePositionUnspecified;
AVCaptureDevicePosition currentPosition = [currentVideoDevice position];
switch (currentPosition) {
case AVCaptureDevicePositionUnspecified:
preferredPosition = AVCaptureDevicePositionBack;
break;
case AVCaptureDevicePositionBack:
preferredPosition = AVCaptureDevicePositionFront;
break;
case AVCaptureDevicePositionFront:
preferredPosition = AVCaptureDevicePositionBack;
break;
}
AVCaptureDevice *videoDevice = [AVCamViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:preferredPosition];
AVCaptureDeviceInput *videoDeviceInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
[self.btnFlash setImage:[UIImage imageNamed:@"icon_flash_auto.png"] forState:UIControlStateNormal];
self.btnFlash.tag = 0;
if ([videoDevice hasTorch] && [videoDevice hasFlash]) {
[videoDevice lockForConfiguration:nil];
[videoDevice setTorchMode:NO];
[videoDevice setFlashMode:AVCaptureFlashModeOn];
[videoDevice unlockForConfiguration];
[self.btnFlash setImage:[UIImage imageNamed:@"icon_flash_auto.png"] forState:UIControlStateNormal];
self.btnFlash.tag = 0;
self.btnFlash.hidden = NO;
return;
}
if (![videoDevice hasTorch]) {
self.btnFlash.hidden = YES;
}
else if ([videoDevice hasTorch] && params.bSwitchFlash) {
self.btnFlash.hidden = NO;
}
});
[[self session] beginConfiguration];
[[self session] removeInput:[self videoDeviceInput]];
if ([[self session] canAddInput:videoDeviceInput]) {
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVCaptureDeviceSubjectAreaDidChangeNotification object:currentVideoDevice];
[AVCamViewController setFlashMode:AVCaptureFlashModeAuto forDevice:videoDevice];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(subjectAreaDidChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotification object:videoDevice];
[[self session] addInput:videoDeviceInput];
[self setVideoDeviceInput:videoDeviceInput];
}
else {
[[self session] addInput:[self videoDeviceInput]];
}
[[self session] commitConfiguration];
dispatch_async(dispatch_get_main_queue(), ^{
[[self cameraButton] setEnabled:YES];
[[self recordButton] setEnabled:YES];
[[self stillButton] setEnabled:YES];
});
});
}
- (IBAction)snapStillImage:(id)sender {
dispatch_async([self sessionQueue], ^{
// Update the orientation on the still image output video connection before capturing.
[[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo] setVideoOrientation:[[(AVCaptureVideoPreviewLayer *)[[self previewView] layer] connection] videoOrientation]];
// Flash set to Auto for Still Capture
[AVCamViewController setFlashMode:AVCaptureFlashModeAuto forDevice:[[self videoDeviceInput] device]];
// Capture a still image.
[[self stillImageOutput] captureStillImageAsynchronouslyFromConnection:[[self stillImageOutput] connectionWithMediaType:AVMediaTypeVideo] completionHandler: ^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
if (imageDataSampleBuffer) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
capturedImage = [[UIImage alloc] initWithData:imageData];
capturedImageData = imageData;
[self takePicture];
}
}];
});
}
- (IBAction)focusAndExposeTap:(UIGestureRecognizer *)gestureRecognizer {
CGPoint devicePoint = [(AVCaptureVideoPreviewLayer *)[[self previewView] layer] captureDevicePointOfInterestForPoint:[gestureRecognizer locationInView:[gestureRecognizer view]]];
[self focusWithMode:AVCaptureFocusModeAutoFocus exposeWithMode:AVCaptureExposureModeAutoExpose atDevicePoint:devicePoint monitorSubjectAreaChange:YES];
}
- (void)subjectAreaDidChange:(NSNotification *)notification {
CGPoint devicePoint = CGPointMake(.5, .5);
[self focusWithMode:AVCaptureFocusModeContinuousAutoFocus exposeWithMode:AVCaptureExposureModeContinuousAutoExposure atDevicePoint:devicePoint monitorSubjectAreaChange:NO];
}
#pragma mark File Output Delegate
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error {
if (error)
NSLog(@"%@", error);
[self setLockInterfaceRotation:NO];
// Note the backgroundRecordingID for use in the ALAssetsLibrary completion handler to end the background task associated with this recording. This allows a new recording to be started, associated with a new UIBackgroundTaskIdentifier, once the movie file output's -isRecording is back to NO which happens sometime after this method returns.
UIBackgroundTaskIdentifier backgroundRecordingID = [self backgroundRecordingID];
[self setBackgroundRecordingID:UIBackgroundTaskInvalid];
[[[ALAssetsLibrary alloc] init] writeVideoAtPathToSavedPhotosAlbum:outputFileURL completionBlock: ^(NSURL *assetURL, NSError *error) {
if (error)
NSLog(@"%@", error);
[[NSFileManager defaultManager] removeItemAtURL:outputFileURL error:nil];
if (backgroundRecordingID != UIBackgroundTaskInvalid)
[[UIApplication sharedApplication] endBackgroundTask:backgroundRecordingID];
}];
}
#pragma mark Device Configuration
- (void)focusWithMode:(AVCaptureFocusMode)focusMode exposeWithMode:(AVCaptureExposureMode)exposureMode atDevicePoint:(CGPoint)point monitorSubjectAreaChange:(BOOL)monitorSubjectAreaChange {
dispatch_async([self sessionQueue], ^{
AVCaptureDevice *device = [[self videoDeviceInput] device];
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
if ([device isFocusPointOfInterestSupported] && [device isFocusModeSupported:focusMode]) {
[device setFocusMode:focusMode];
[device setFocusPointOfInterest:point];
}
if ([device isExposurePointOfInterestSupported] && [device isExposureModeSupported:exposureMode]) {
[device setExposureMode:exposureMode];
[device setExposurePointOfInterest:point];
}
[device setSubjectAreaChangeMonitoringEnabled:monitorSubjectAreaChange];
[device unlockForConfiguration];
}
else {
NSLog(@"%@", error);
}
});
}
+ (void)setFlashMode:(AVCaptureFlashMode)flashMode forDevice:(AVCaptureDevice *)device {
if ([device hasFlash] && [device isFlashModeSupported:flashMode]) {
NSError *error = nil;
if ([device lockForConfiguration:&error]) {
[device setFlashMode:flashMode];
[device unlockForConfiguration];
}
else {
NSLog(@"%@", error);
}
}
}
+ (AVCaptureDevice *)deviceWithMediaType:(NSString *)mediaType preferringPosition:(AVCaptureDevicePosition)position {
NSArray *devices = [AVCaptureDevice devicesWithMediaType:mediaType];
AVCaptureDevice *captureDevice = [devices firstObject];
for (AVCaptureDevice *device in devices) {
if ([device position] == position) {
captureDevice = device;
break;
}
}
return captureDevice;
}
#pragma mark UI
- (void)runStillImageCaptureAnimation {
dispatch_async(dispatch_get_main_queue(), ^{
[[[self previewView] layer] setOpacity:0.0];
[UIView animateWithDuration:.25 animations: ^{
[[[self previewView] layer] setOpacity:1.0];
}];
});
}
- (void)checkDeviceAuthorizationStatus {
NSString *mediaType = AVMediaTypeVideo;
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler: ^(BOOL granted) {
if (granted) {
//Granted access to mediaType
[self setDeviceAuthorized:YES];
}
else {
//Not granted access to mediaType
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:@"AVCam!"
message:@"AVCam doesn't have permission to use Camera, please change privacy settings"
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil] show];
[self setDeviceAuthorized:NO];
});
}
}];
}
- (IBAction)onTapThumb:(id)sender {
UIButton *btnThumb = (UIButton *)sender;
self.imgSmallThumbNail.hidden = btnThumb.selected;
self.imgBigThumbNail.hidden = !btnThumb.selected;
btnThumb.selected = !btnThumb.selected;
}
- (IBAction)onTapCameraFlash:(id)sender {
UIButton *btnCameraFlash = (UIButton *)sender;
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (btnCameraFlash.tag == 0) {
[btnCameraFlash setImage:[UIImage imageNamed:@"icon_flash.png"] forState:UIControlStateNormal];
btnCameraFlash.tag = 1;
if ([device hasTorch] && [device hasFlash]) {
[device lockForConfiguration:nil];
[device setTorchMode:!device.torchActive];
[device setFlashMode:AVCaptureFlashModeOn];
[device unlockForConfiguration];
}
return;
}
else if (btnCameraFlash.tag == 1) {
[btnCameraFlash setImage:[UIImage imageNamed:@"icon_flash_auto.png"] forState:UIControlStateNormal];
btnCameraFlash.tag = 0;
if ([device hasTorch] && [device hasFlash]) {
[device lockForConfiguration:nil];
[device setTorchMode:!device.torchActive];
[device setFlashMode:AVCaptureFlashModeOff];
[device unlockForConfiguration];
}
return;
}
}
- (IBAction)onBack:(id)sender {
_callback(nil, @"3", @"Camera closed before takin a picture.");
}
- (void)addPinchGesture {
twoFingerPinch = [[UIPinchGestureRecognizer alloc]
initWithTarget:self
action:@selector(twoFingerPinch:)];
[self.view addGestureRecognizer:twoFingerPinch];
}
- (void)addOpacitySlider {
CGAffineTransform trans = CGAffineTransformMakeRotation(M_PI_2 * (-1));
self.opacitySlider.transform = trans;
[self.opacitySlider addTarget:self action:@selector(onChangeOpacitySlider) forControlEvents:UIControlEventValueChanged];
self.opacitySlider.value = 1;
}
- (void)initialize {
fDist = self.btnThumb.center.x - self.cameraButton.center.x;
capturedImage = [[UIImage alloc] init];
capturedImageData = [[NSData alloc] init];
[self addOpacitySlider];
[self addPinchGesture];
self.capturedImageView.hidden = YES;
self.saveBgPanel.hidden = YES;
self.btnBigDeletePicture.hidden = self.btnDeletePicture.hidden = YES;
self.btnBigSaveImage.hidden = self.btnSaveImage.hidden = YES;
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGFloat screenScale = [[UIScreen mainScreen] scale];
CGSize screenSize = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);
NSInteger max = (screenSize.width > screenSize.height) ? screenSize.width : screenSize.height;
UIImage *newImage = [self imageWithImage:[UIImage imageWithData:self.params.bgImageData] scaledToMaxWidth:max maxHeight:max];
self.imgBigThumbNail.image = newImage;
self.imgSmallThumbNail.image = [UIImage imageWithData:self.params.bgImageData];
self.btnThumb.hidden = !params.bMiniature;
self.btnFlash.hidden = !params.bSwitchFlash;
self.cameraButton.hidden = !params.bSwitchCamera;
self.opacitySlider.hidden = !params.bOpacity;
if (!params.bgImageData) {
self.imgBigThumbNail.hidden = YES;
self.imgSmallThumbNail.hidden = YES;
}
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if (params.nDefaultFlash == 1) {
[self.btnFlash setImage:[UIImage imageNamed:@"icon_flash.png"] forState:UIControlStateNormal];
self.btnFlash.tag = 1;
if ([device hasTorch] && [device hasFlash]) {
[device lockForConfiguration:nil];
[device setTorchMode:YES];
[device setFlashMode:AVCaptureFlashModeOn];
[device unlockForConfiguration];
}
}
else {
[self.btnFlash setImage:[UIImage imageNamed:@"icon_flash_auto.png"] forState:UIControlStateNormal];
self.btnFlash.tag = 0;
if ([device hasTorch] && [device hasFlash]) {
[device lockForConfiguration:nil];
[device setTorchMode:NO];
[device setFlashMode:AVCaptureFlashModeOn];
[device unlockForConfiguration];
}
}
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
if (params.bgImageData1) {
isRotated = !isRotated;
CGRect screenBounds = [[UIScreen mainScreen] bounds];
CGFloat screenScale = [[UIScreen mainScreen] scale];
CGSize screenSize = CGSizeMake(screenBounds.size.width * screenScale, screenBounds.size.height * screenScale);
NSInteger max = (screenSize.width > screenSize.height) ? screenSize.width : screenSize.height;
if (isRotated) {
UIImage *newImage = [self imageWithImage:[UIImage imageWithData:self.params.bgImageData1] scaledToMaxWidth:max maxHeight:max];
self.imgBigThumbNail.image = newImage;
self.imgSmallThumbNail.image = [UIImage imageWithData:params.bgImageData1];
}
else {
UIImage *newImage = [self imageWithImage:[UIImage imageWithData:self.params.bgImageData] scaledToMaxWidth:max maxHeight:max];
self.imgBigThumbNail.image = newImage;
self.imgSmallThumbNail.image = [UIImage imageWithData:params.bgImageData];
}
}
}
- (void)takePicture {
[self setLockInterfaceRotation:YES];
self.capturedImageView.image = capturedImage;
self.capturedImageView.hidden = NO;
self.saveBgPanel.hidden = NO;
self.btnBigDeletePicture.hidden = self.btnDeletePicture.hidden = NO;
self.btnBigSaveImage.hidden = self.btnSaveImage.hidden = NO;
self.stillButton.hidden = self.btnFlash.hidden = self.cameraButton.hidden = YES;
frameBtnThumb = self.btnThumb.frame;
self.btnThumb.frame = self.btnFlash.frame;
self.imgSmallThumbNail.frame = CGRectOffset(self.imgSmallThumbNail.frame, 0, -self.saveBgPanel.frame.size.height);
}
- (IBAction)onDeletePicture:(id)sender {
[self setLockInterfaceRotation:NO];
capturedImage = nil;
self.capturedImageView.image = nil;
self.capturedImageView.hidden = YES;
self.btnBigDeletePicture.hidden = self.btnDeletePicture.hidden = YES;
self.btnBigSaveImage.hidden = self.btnSaveImage.hidden = YES;
self.saveBgPanel.hidden = YES;
self.btnThumb.frame = frameBtnThumb;
self.stillButton.hidden = self.btnFlash.hidden = NO;
//control camera button
self.cameraButton.hidden = !params.bSwitchCamera;
[self.cameraButton setEnabled:params.bSwitchCamera];
//control flash button
if (params.bSwitchFlash) {
AVCaptureDevice *currentVideoDevice = [[self videoDeviceInput] device];
AVCaptureDevicePosition currentPosition = [currentVideoDevice position];
AVCaptureDevice *videoDevice = [AVCamViewController deviceWithMediaType:AVMediaTypeVideo preferringPosition:currentPosition];
switch (currentPosition) {
case AVCaptureDevicePositionUnspecified:
case AVCaptureDevicePositionBack:
{
if (self.btnFlash.tag == 0) {
[self.btnFlash setImage:[UIImage imageNamed:@"icon_flash_auto.png"] forState:UIControlStateNormal];
if ([videoDevice hasTorch] && [videoDevice hasFlash]) {
[videoDevice lockForConfiguration:nil];
[videoDevice setTorchMode:videoDevice.torchActive];
[videoDevice setFlashMode:AVCaptureFlashModeOff];
[videoDevice unlockForConfiguration];
}
}
else if (self.btnFlash.tag == 1) {
[self.btnFlash setImage:[UIImage imageNamed:@"icon_flash.png"] forState:UIControlStateNormal];
if ([videoDevice hasTorch] && [videoDevice hasFlash]) {
[videoDevice lockForConfiguration:nil];
[videoDevice setTorchMode:videoDevice.torchActive];
[videoDevice setFlashMode:AVCaptureFlashModeOn];
[videoDevice unlockForConfiguration];
}
}
break;
}
case AVCaptureDevicePositionFront:
break;
}
if (![videoDevice hasTorch] || ![videoDevice hasFlash]) {
self.btnFlash.hidden = YES;
}
else {
self.btnFlash.hidden = !params.bSwitchFlash;
[self.btnFlash setEnabled:params.bSwitchFlash];
}
}
else {
self.btnFlash.hidden = YES;
}
self.imgSmallThumbNail.frame = CGRectOffset(self.imgSmallThumbNail.frame, 0, self.saveBgPanel.frame.size.height);
}
- (IBAction)onSaveImage:(id)sender {
UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.center = self.view.center;
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
if (params.bSaveInGallery) {
[[[ALAssetsLibrary alloc] init] writeImageToSavedPhotosAlbum:[capturedImage CGImage] orientation:(ALAssetOrientation)[capturedImage imageOrientation] completionBlock:nil];
}
[self.view setUserInteractionEnabled:NO];
_callback([UIImage imageWithData:capturedImageData], nil, nil);
}
- (void)onChangeOpacitySlider {
self.imgSmallThumbNail.alpha = self.opacitySlider.value;
self.imgBigThumbNail.alpha = self.opacitySlider.value;
}
- (void)twoFingerPinch:(UIPinchGestureRecognizer *)recognizer {
if (self.lockInterfaceRotation) {
return;
}
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
CGFloat fMaxZoomFactor = device.activeFormat.videoMaxZoomFactor;
if (fMaxZoomFactor > 5)
fMaxZoomFactor = 5;
CGFloat fNewScale = recognizer.scale * device.videoZoomFactor;
if (fNewScale > 1.0f && fNewScale < fMaxZoomFactor) {
[device lockForConfiguration:nil];
[device rampToVideoZoomFactor:fNewScale withRate:3];
[device unlockForConfiguration];
}
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation {
return orientation == UIDeviceOrientationPortrait;
}
#pragma mark -Scale image
- (UIImage *)imageWithImage:(UIImage *)image scaledToMaxWidth:(CGFloat)width maxHeight:(CGFloat)height {
CGFloat oldWidth = image.size.width;
CGFloat oldHeight = image.size.height;
CGFloat scaleFactor = (oldWidth > oldHeight) ? width / oldWidth : height / oldHeight;
CGFloat newHeight = oldHeight * scaleFactor;
CGFloat newWidth = oldWidth * scaleFactor;
CGSize newSize = CGSizeMake(newWidth, newHeight);
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(newSize, NO, [[UIScreen mainScreen] scale]);
}
else {
UIGraphicsBeginImageContext(newSize);
}
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end

View File

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="6254" systemVersion="14A389" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AVCamViewController">
<connections>
<outlet property="btnBigDeletePicture" destination="IqJ-oc-995" id="T1H-10-ESB"/>
<outlet property="btnBigSaveImage" destination="EFe-fE-P8a" id="P0Z-Xm-thh"/>
<outlet property="btnDeletePicture" destination="S1N-v6-cZw" id="1TW-yQ-2Ul"/>
<outlet property="btnFlash" destination="KMs-9J-eHV" id="pdI-VZ-buj"/>
<outlet property="btnSaveImage" destination="JJc-KF-LjK" id="att-go-Xq7"/>
<outlet property="btnThumb" destination="NW3-H0-02v" id="9az-rc-6zl"/>
<outlet property="cameraButton" destination="br3-lo-te3" id="9er-L2-Q49"/>
<outlet property="capturedImageView" destination="aNd-OH-3PR" id="FB6-Dl-sSA"/>
<outlet property="imgBigThumbNail" destination="hb3-4z-EqI" id="bXK-S1-JY5"/>
<outlet property="imgSmallThumbNail" destination="7Tf-kP-S1C" id="yk1-9p-mnP"/>
<outlet property="opacitySlider" destination="D3e-gE-Un5" id="Vl5-37-0Gj"/>
<outlet property="previewView" destination="vHe-RW-WQc" id="WqV-SM-Xgc"/>
<outlet property="saveBgPanel" destination="xaA-dP-8ce" id="FGL-y1-FJb"/>
<outlet property="stillButton" destination="cxS-Qn-d9p" id="Joi-8C-0gM"/>
<outlet property="topBgPanel" destination="kso-6k-eTa" id="sZi-gu-mzH"/>
<outlet property="view" destination="iN0-l3-epB" id="88I-Oa-O0G"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" id="vHe-RW-WQc" customClass="AVCamPreviewView">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="aNd-OH-3PR">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sample.png" id="hb3-4z-EqI">
<rect key="frame" x="0.0" y="0.0" width="768" height="1024"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="cxS-Qn-d9p">
<rect key="frame" x="344" y="941" width="80" height="76"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<state key="normal" image="icon_capture.png"/>
<state key="selected" image="icon_capture_pressed.png"/>
<state key="highlighted" image="icon_capture_pressed.png"/>
<connections>
<action selector="snapStillImage:" destination="-1" eventType="touchUpInside" id="LsD-dw-Fw0"/>
</connections>
</button>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sample.png" id="7Tf-kP-S1C">
<rect key="frame" x="20" y="934" width="90" height="90"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</imageView>
<view alpha="0.5" contentMode="scaleToFill" id="xaA-dP-8ce">
<rect key="frame" x="0.0" y="934" width="768" height="90"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="IqJ-oc-995">
<rect key="frame" x="0.0" y="0.0" width="376" height="90"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onDeletePicture:" destination="-1" eventType="touchUpInside" id="XkX-0N-K4g"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="EFe-fE-P8a">
<rect key="frame" x="384" y="0.0" width="384" height="90"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onSaveImage:" destination="-1" eventType="touchUpInside" id="pMu-f6-poC"/>
</connections>
</button>
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="S1N-v6-cZw">
<rect key="frame" x="147" y="20" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<state key="normal" image="icon_delete.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onDeletePicture:" destination="-1" eventType="touchUpInside" id="7ty-gh-NHC"/>
</connections>
</button>
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="JJc-KF-LjK">
<rect key="frame" x="566" y="20" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<state key="normal" image="icon_submit.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onSaveImage:" destination="-1" eventType="touchUpInside" id="mry-0F-fqI"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<view alpha="0.5" contentMode="scaleToFill" id="kso-6k-eTa">
<rect key="frame" x="0.0" y="0.0" width="768" height="90"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="7Wz-zM-ysY">
<rect key="frame" x="20" y="20" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_back.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onBack:" destination="-1" eventType="touchUpInside" id="zpX-8h-Pcs"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="br3-lo-te3">
<rect key="frame" x="480" y="20" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_flip.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="changeCamera:" destination="-1" eventType="touchUpInside" id="2JV-vM-qU3"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="NW3-H0-02v">
<rect key="frame" x="597" y="25" width="40" height="40"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_min.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="icon_max.png"/>
<connections>
<action selector="onTapThumb:" destination="-1" eventType="touchUpInside" id="kJr-cv-LaQ"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="KMs-9J-eHV">
<rect key="frame" x="690" y="20" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_flash_auto.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onTapCameraFlash:" destination="-1" eventType="touchUpInside" id="hq4-uE-TO5"/>
</connections>
</button>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" id="D3e-gE-Un5">
<rect key="frame" x="479" y="497" width="512" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</slider>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</objects>
<resources>
<image name="icon_back.png" width="60" height="60"/>
<image name="icon_capture.png" width="128" height="128"/>
<image name="icon_capture_pressed.png" width="128" height="128"/>
<image name="icon_delete.png" width="60" height="60"/>
<image name="icon_flash_auto.png" width="60" height="60"/>
<image name="icon_flip.png" width="60" height="60"/>
<image name="icon_max.png" width="60" height="60"/>
<image name="icon_min.png" width="60" height="60"/>
<image name="icon_submit.png" width="60" height="60"/>
<image name="sample.png" width="483" height="263"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination"/>
</simulatedMetricsContainer>
</document>

View File

@ -0,0 +1,187 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6254" systemVersion="14A389" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="AVCamViewController">
<connections>
<outlet property="btnBack" destination="JSx-s3-uHS" id="gtj-U7-7eS"/>
<outlet property="btnBigDeletePicture" destination="9ml-dK-r4P" id="v1z-D4-2aN"/>
<outlet property="btnBigSaveImage" destination="P2D-hm-Kcg" id="81p-6x-fIz"/>
<outlet property="btnDeletePicture" destination="J6B-b3-9jL" id="Odf-du-kBy"/>
<outlet property="btnFlash" destination="BRk-mk-jo0" id="JMg-ha-GzP"/>
<outlet property="btnSaveImage" destination="gCj-9O-Lhz" id="Tg2-gh-nlr"/>
<outlet property="btnThumb" destination="OWo-yv-W9g" id="0S3-x9-HAa"/>
<outlet property="cameraButton" destination="jnK-sC-Roz" id="yhA-LL-bdK"/>
<outlet property="capturedImageView" destination="mfI-LM-QPE" id="aXs-aV-ggH"/>
<outlet property="imgBigThumbNail" destination="GAn-hK-ROm" id="dc7-tm-wNM"/>
<outlet property="imgSmallThumbNail" destination="MJV-kJ-EX5" id="HgP-9H-c4t"/>
<outlet property="opacitySlider" destination="gf9-Ju-OcL" id="jn8-NJ-iVO"/>
<outlet property="previewView" destination="wgh-5M-pgU" id="03T-sp-Ynl"/>
<outlet property="saveBgPanel" destination="AJN-IB-tiU" id="epU-gC-TWb"/>
<outlet property="stillButton" destination="usa-GD-lMO" id="hXu-yT-Tuj"/>
<outlet property="topBgPanel" destination="qKn-KP-yGM" id="bL1-dO-Na2"/>
<outlet property="view" destination="iN0-l3-epB" id="98Z-ee-Lmg"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" id="wgh-5M-pgU" customClass="AVCamPreviewView">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" id="mfI-LM-QPE">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sample.png" id="GAn-hK-ROm">
<rect key="frame" x="0.0" y="0.0" width="320" height="568"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" enabled="NO" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="usa-GD-lMO">
<rect key="frame" x="135" y="463" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<state key="normal" image="icon_capture.png"/>
<state key="selected" image="icon_capture_pressed.png"/>
<state key="highlighted" image="icon_capture_pressed.png"/>
<connections>
<action selector="snapStillImage:" destination="-1" eventType="touchUpInside" id="l6I-0I-IBm"/>
</connections>
</button>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="sample.png" id="MJV-kJ-EX5">
<rect key="frame" x="10" y="508" width="60" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</imageView>
<view alpha="0.5" contentMode="scaleToFill" id="AJN-IB-tiU">
<rect key="frame" x="0.0" y="488" width="320" height="80"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="9ml-dK-r4P">
<rect key="frame" x="0.0" y="0.0" width="160" height="60"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onDeletePicture:" destination="-1" eventType="touchUpInside" id="MyT-W6-Zcm"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="P2D-hm-Kcg">
<rect key="frame" x="160" y="0.0" width="160" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onSaveImage:" destination="-1" eventType="touchUpInside" id="HXF-We-dau"/>
</connections>
</button>
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="gCj-9O-Lhz">
<rect key="frame" x="245" y="5" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<state key="normal" image="icon_submit.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onSaveImage:" destination="-1" eventType="touchUpInside" id="hLE-rb-Fpv"/>
</connections>
</button>
<button opaque="NO" userInteractionEnabled="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="J6B-b3-9jL">
<rect key="frame" x="45" y="5" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<state key="normal" image="icon_delete.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onDeletePicture:" destination="-1" eventType="touchUpInside" id="vby-qq-Okp"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<view alpha="0.5" contentMode="scaleToFill" id="qKn-KP-yGM">
<rect key="frame" x="0.0" y="0.0" width="320" height="60"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="OWo-yv-W9g">
<rect key="frame" x="234" y="17" width="25" height="25"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_min.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="icon_max.png"/>
<connections>
<action selector="onTapThumb:" destination="-1" eventType="touchUpInside" id="zSf-cq-fIV"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="jnK-sC-Roz">
<rect key="frame" x="230" y="15" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_flip.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="changeCamera:" destination="-1" eventType="touchUpInside" id="UhB-S7-CvX"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="JSx-s3-uHS">
<rect key="frame" x="10" y="15" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_back.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onBack:" destination="-1" eventType="touchUpInside" id="dpX-Bq-HAw"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="BRk-mk-jo0">
<rect key="frame" x="280" y="15" width="30" height="30"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<state key="normal" image="icon_flash_auto.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onTapCameraFlash:" destination="-1" eventType="touchUpInside" id="mKZ-w5-NhW"/>
</connections>
</button>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" id="gf9-Ju-OcL">
<rect key="frame" x="199" y="269" width="200" height="31"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
</slider>
</subviews>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="calibratedWhite"/>
<gestureRecognizers/>
</view>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<nil key="simulatedStatusBarMetrics"/>
</view>
</objects>
<resources>
<image name="icon_back.png" width="60" height="60"/>
<image name="icon_capture.png" width="128" height="128"/>
<image name="icon_capture_pressed.png" width="128" height="128"/>
<image name="icon_delete.png" width="60" height="60"/>
<image name="icon_flash_auto.png" width="60" height="60"/>
<image name="icon_flip.png" width="60" height="60"/>
<image name="icon_max.png" width="60" height="60"/>
<image name="icon_min.png" width="60" height="60"/>
<image name="icon_submit.png" width="60" height="60"/>
<image name="sample.png" width="483" height="263"/>
</resources>
<simulatedMetricsContainer key="defaultSimulatedMetrics">
<simulatedStatusBarMetrics key="statusBar"/>
<simulatedOrientationMetrics key="orientation"/>
<simulatedScreenMetrics key="destination" type="retina4"/>
</simulatedMetricsContainer>
</document>

View File

@ -0,0 +1,33 @@
#import <Foundation/Foundation.h>
#import <Cordova/CDV.h>
@interface CameraParameter : NSObject
{
}
@property(nonatomic, retain) NSData *bgImageData;
@property(nonatomic, retain) NSData *bgImageData1;
@property(nonatomic, assign) BOOL bMiniature;
@property(nonatomic, assign) BOOL bSaveInGallery;
@property(nonatomic, assign) int nCameraFlashMode;
@property(nonatomic, retain) NSString* strCameraBGColor;
@property(nonatomic, retain) NSString* strCameraPressedBG;
@property(nonatomic, assign) CGFloat fQuality;
@property(nonatomic, assign) BOOL bOpacity;
@property(nonatomic, assign) int nDefaultFlash;
@property(nonatomic, assign) BOOL bSwitchFlash;
@property(nonatomic, assign) int targetWidth;
@property(nonatomic, assign) int targetHeight;
@property(nonatomic, assign) int nDefaultCamera;
@property(nonatomic, assign) BOOL bSwitchCamera;
-(id) initWithCommand :(CDVInvokedUrlCommand *)command;
@end

View File

@ -0,0 +1,66 @@
#import "CameraParameter.h"
@implementation CameraParameter
{
}
@synthesize bgImageData;
@synthesize bgImageData1;
@synthesize bMiniature;
@synthesize bSaveInGallery;
@synthesize nCameraFlashMode;
@synthesize strCameraBGColor;
@synthesize strCameraPressedBG;
@synthesize fQuality;
@synthesize targetWidth;
@synthesize targetHeight;
@synthesize bOpacity;
@synthesize nDefaultFlash;
@synthesize bSwitchFlash;
@synthesize nDefaultCamera;
@synthesize bSwitchCamera;
- (id)initWithCommand:(CDVInvokedUrlCommand *)command {
// [quality, destinationType, sourceType, targetWidth, targetHeight, encodingType,
// mediaType, allowEdit, correctOrientation, saveToPhotoAlbum, popoverOptions, cameraDirection];
if (self = [super init]) {
// NSString *strData = [command argumentAtIndex:0];
// if (strData) {
// bgImageData = [[NSData alloc] initWithBase64EncodedString:strData options:0];
// }
// else {
bgImageData = nil;
// }
// NSString *strData1 = [command argumentAtIndex:1];
// if (strData1) {
// bgImageData1 = [[NSData alloc] initWithBase64EncodedString:strData1 options:0];
// }
// else {
bgImageData1 = nil;
// }
bMiniature = false; //[[command argumentAtIndex:2] boolValue];
bSaveInGallery = [[command argumentAtIndex:9] boolValue];
strCameraBGColor = @"#e26760"; //[command argumentAtIndex:4];
strCameraPressedBG = @"#dc453d"; //[command argumentAtIndex:5];
fQuality = [[command argumentAtIndex:0] intValue];
targetWidth = [[command argumentAtIndex:3] intValue];
targetHeight = [[command argumentAtIndex:4] intValue];
bOpacity = false; //[[command argumentAtIndex:7] boolValue];
nDefaultFlash = 2; //[[command argumentAtIndex:8] intValue];
bSwitchFlash = true; //[[command argumentAtIndex:9] boolValue];
nDefaultCamera = 0; //[[command argumentAtIndex:10] intValue];
bSwitchCamera = true; //[[command argumentAtIndex:11] boolValue];
}
return self;
}
@end

93
src/ios/classes/CustomCamera.h Executable file
View File

@ -0,0 +1,93 @@
#import <Cordova/CDV.h>
#import <CoreLocation/CoreLocation.h>
#import <CoreLocation/CLLocationManager.h>
enum CDVDestinationType {
DestinationTypeDataUrl = 0,
DestinationTypeFileUri,
DestinationTypeNativeUri
};
typedef NSUInteger CDVDestinationType;
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) UIImagePickerControllerSourceType 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;
@property (strong) NSDictionary* popoverOptions;
@property (assign) UIImagePickerControllerCameraDevice cameraDirection;
@property (assign) BOOL popoverSupported;
@property (assign) BOOL usesGeolocation;
@property (assign) BOOL cropToSize;
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand*)command;
@end
@interface CDVCameraPicker : UIImagePickerController
@property (strong) CDVPictureOptions* pictureOptions;
@property (copy) NSString* callbackId;
@property (copy) NSString* postUrl;
@property (strong) UIPopoverController* pickerPopoverController;
@property (assign) BOOL cropToSize;
@property (strong) UIView* webView;
+ (instancetype) createFromPictureOptions:(CDVPictureOptions*)options;
@end
@interface CustomCamera : CDVPlugin<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
{
CDVInvokedUrlCommand *lastCommand;
int nSourceType;
int nDestType;
NSData *bgImageData;
NSData *bgImageData1;
BOOL miniature;
BOOL saveInGallery;
int nCameraFlashMode;
NSString* clrCameraBG;
NSString* clrCameraPressedBG;
CGFloat quality;
BOOL opacity;
int defaultFlash;
BOOL switchFlash;
int defaultCamera;
BOOL switchCamera;
NSString *filename;
}
@property (strong) NSData* data;
@property (strong) NSMutableDictionary *metadata;
@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong) CDVCameraPicker* pickerController;
- (void)startCamera:(CDVInvokedUrlCommand*)command;
- (void)takePicture:(CDVInvokedUrlCommand*)command;
- (void)showCameraPicker:(NSString*)callbackId withOptions:(CDVPictureOptions *) pictureOptions;
@end

584
src/ios/classes/CustomCamera.m Executable file
View File

@ -0,0 +1,584 @@
#import "CustomCamera.h"
#import "AVCamViewController.h"
#import "CameraParameter.h"
#import <MobileCoreServices/UTCoreTypes.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import "UIImage+CropScaleOrientation.h"
#import <objc/message.h>
#import <AVFoundation/AVFoundation.h>
#ifndef __CORDOVA_4_0_0
#import <Cordova/NSData+Base64.h>
#endif
#define CDV_PHOTO_PREFIX @"cdv_photo_"
static NSSet* org_apache_cordova_validArrowDirections;
static NSString* toBase64(NSData* data) {
SEL s1 = NSSelectorFromString(@"cdv_base64EncodedString");
SEL s2 = NSSelectorFromString(@"base64EncodedString");
SEL s3 = NSSelectorFromString(@"base64EncodedStringWithOptions:");
if ([data respondsToSelector:s1]) {
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s1];
return func(data, s1);
} else if ([data respondsToSelector:s2]) {
NSString* (*func)(id, SEL) = (void *)[data methodForSelector:s2];
return func(data, s2);
} else if ([data respondsToSelector:s3]) {
NSString* (*func)(id, SEL, NSUInteger) = (void *)[data methodForSelector:s3];
return func(data, s3, 0);
} else {
return nil;
}
}
@implementation CDVPictureOptions
+ (instancetype) createFromTakePictureArguments:(CDVInvokedUrlCommand*)command
{
CDVPictureOptions* pictureOptions = [[CDVPictureOptions alloc] init];
pictureOptions.quality = [command argumentAtIndex:0 withDefault:@(100)];
pictureOptions.destinationType = [[command argumentAtIndex:1 withDefault:@(DestinationTypeFileUri)] unsignedIntegerValue];
pictureOptions.sourceType = [[command argumentAtIndex:2 withDefault:@(UIImagePickerControllerSourceTypeCamera)] 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];
pictureOptions.popoverOptions = [command argumentAtIndex:10 withDefault:nil];
pictureOptions.cameraDirection = [[command argumentAtIndex:11 withDefault:@(UIImagePickerControllerCameraDeviceRear)] unsignedIntegerValue];
pictureOptions.popoverSupported = NO;
pictureOptions.usesGeolocation = NO;
return pictureOptions;
}
@end
@implementation CDVCameraPicker
- (BOOL)prefersStatusBarHidden
{
return YES;
}
- (UIViewController*)childViewControllerForStatusBarHidden
{
return nil;
}
- (void)viewWillAppear:(BOOL)animated
{
SEL sel = NSSelectorFromString(@"setNeedsStatusBarAppearanceUpdate");
if ([self respondsToSelector:sel]) {
[self performSelector:sel withObject:nil afterDelay:0];
}
[super viewWillAppear:animated];
}
+ (instancetype) createFromPictureOptions:(CDVPictureOptions*)pictureOptions;
{
CDVCameraPicker* cameraPicker = [[CDVCameraPicker alloc] init];
cameraPicker.pictureOptions = pictureOptions;
cameraPicker.sourceType = pictureOptions.sourceType;
cameraPicker.allowsEditing = pictureOptions.allowsEditing;
if (cameraPicker.sourceType == UIImagePickerControllerSourceTypeCamera) {
// We only allow taking pictures (no video) in this API.
cameraPicker.mediaTypes = @[(NSString*)kUTTypeImage];
// We can only set the camera device if we're actually using the camera.
cameraPicker.cameraDevice = pictureOptions.cameraDirection;
} else if (pictureOptions.mediaType == MediaTypeAll) {
cameraPicker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:cameraPicker.sourceType];
} else {
NSArray* mediaArray = @[(NSString*)(pictureOptions.mediaType == MediaTypeVideo ? kUTTypeMovie : kUTTypeImage)];
cameraPicker.mediaTypes = mediaArray;
}
return cameraPicker;
}
@end
@interface CustomCamera ()
@property (readwrite, assign) BOOL hasPendingOperation;
@end
@implementation CustomCamera
+ (void)initialize
{
org_apache_cordova_validArrowDirections = [[NSSet alloc] initWithObjects:[NSNumber numberWithInt:UIPopoverArrowDirectionUp], [NSNumber numberWithInt:UIPopoverArrowDirectionDown], [NSNumber numberWithInt:UIPopoverArrowDirectionLeft], [NSNumber numberWithInt:UIPopoverArrowDirectionRight], [NSNumber numberWithInt:UIPopoverArrowDirectionAny], nil];
}
@synthesize hasPendingOperation, pickerController;
- (void)takePicture:(CDVInvokedUrlCommand *)command {
self.hasPendingOperation = YES;
CDVPictureOptions* pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
if (pictureOptions.sourceType == 1) {
[self startCamera: command];
} else {
[self showCameraPicker:command.callbackId withOptions:pictureOptions];
}
}
- (BOOL)popoverSupported
{
return (NSClassFromString(@"UIPopoverController") != nil) &&
(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad);
}
- (NSInteger)integerValueForKey:(NSDictionary*)dict key:(NSString*)key defaultValue:(NSInteger)defaultValue
{
NSInteger value = defaultValue;
NSNumber* val = [dict valueForKey:key]; // value is an NSNumber
if (val != nil) {
value = [val integerValue];
}
return value;
}
- (void)displayPopover:(NSDictionary*)options
{
NSInteger x = 0;
NSInteger y = 32;
NSInteger width = 320;
NSInteger height = 480;
UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
if (options) {
x = [self integerValueForKey:options key:@"x" defaultValue:0];
y = [self integerValueForKey:options key:@"y" defaultValue:32];
width = [self integerValueForKey:options key:@"width" defaultValue:320];
height = [self integerValueForKey:options key:@"height" defaultValue:480];
arrowDirection = [self integerValueForKey:options key:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny];
if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithUnsignedInteger:arrowDirection]]) {
arrowDirection = UIPopoverArrowDirectionAny;
}
}
[[[self pickerController] pickerPopoverController] setDelegate:self];
[[[self pickerController] pickerPopoverController] presentPopoverFromRect:CGRectMake(x, y, width, height)
inView:[self.webView superview]
permittedArrowDirections:arrowDirection
animated:YES];
}
- (void)showCameraPicker:(NSString*)callbackId withOptions:(CDVPictureOptions *) pictureOptions
{
// Perform UI operations on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
self.pickerController = cameraPicker;
cameraPicker.delegate = self;
cameraPicker.callbackId = callbackId;
// we need to capture this state for memory warnings that dealloc this object
cameraPicker.webView = self.webView;
// If a popover is already open, close it; we only want one at a time.
if (([[self pickerController] pickerPopoverController] != nil) && [[[self pickerController] pickerPopoverController] isPopoverVisible]) {
[[[self pickerController] pickerPopoverController] dismissPopoverAnimated:YES];
[[[self pickerController] pickerPopoverController] setDelegate:nil];
[[self pickerController] setPickerPopoverController:nil];
}
if ([self popoverSupported] && (pictureOptions.sourceType != UIImagePickerControllerSourceTypeCamera)) {
if (cameraPicker.pickerPopoverController == nil) {
cameraPicker.pickerPopoverController = [[NSClassFromString(@"UIPopoverController") alloc] initWithContentViewController:cameraPicker];
}
[self displayPopover:pictureOptions.popoverOptions];
self.hasPendingOperation = NO;
} else {
cameraPicker.modalPresentationStyle = UIModalPresentationCurrentContext;
[self.viewController presentViewController:cameraPicker animated:YES completion:^{
self.hasPendingOperation = NO;
}];
}
});
}
- (UIImage*)retrieveImage:(NSDictionary*)info options:(CDVPictureOptions*)options
{
// get the image
UIImage* image = nil;
if (options.allowsEditing && [info objectForKey:UIImagePickerControllerEditedImage]) {
image = [info objectForKey:UIImagePickerControllerEditedImage];
} else {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
if (options.correctOrientation) {
image = [image imageCorrectedForCaptureOrientation];
}
UIImage* scaledImage = nil;
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 (options.cropToSize) {
scaledImage = [image imageByScalingAndCroppingForSize:options.targetSize];
} else {
scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
}
}
return (scaledImage == nil ? image : scaledImage);
}
- (NSURL*) urlTransformer:(NSURL*)url
{
NSURL* urlToTransform = url;
// for backwards compatibility - we check if this property is there
SEL sel = NSSelectorFromString(@"urlTransformer");
if ([self.commandDelegate respondsToSelector:sel]) {
// grab the block from the commandDelegate
NSURL* (^urlTransformer)(NSURL*) = ((id(*)(id, SEL))objc_msgSend)(self.commandDelegate, sel);
// if block is not null, we call it
if (urlTransformer) {
urlToTransform = urlTransformer(url);
}
}
return urlToTransform;
}
- (NSData*)processImage:(UIImage*)image info:(NSDictionary*)info options:(CDVPictureOptions*)options
{
NSData* data = nil;
switch (options.encodingType) {
case EncodingTypePNG:
data = UIImagePNGRepresentation(image);
break;
case EncodingTypeJPEG:
{
if ((options.allowsEditing == NO) && (options.targetSize.width <= 0) && (options.targetSize.height <= 0) && (options.correctOrientation == NO) && (([options.quality integerValue] == 100) || (options.sourceType != UIImagePickerControllerSourceTypeCamera))){
// use image unedited as requested , don't resize
data = UIImageJPEGRepresentation(image, 1.0);
} else {
data = UIImageJPEGRepresentation(image, [options.quality floatValue] / 100.0f);
}
if (options.usesGeolocation) {
NSDictionary* controllerMetadata = [info objectForKey:@"UIImagePickerControllerMediaMetadata"];
if (controllerMetadata) {
self.data = data;
self.metadata = [[NSMutableDictionary alloc] init];
NSMutableDictionary* EXIFDictionary = [[controllerMetadata objectForKey:(NSString*)kCGImagePropertyExifDictionary]mutableCopy];
if (EXIFDictionary) {
[self.metadata setObject:EXIFDictionary forKey:(NSString*)kCGImagePropertyExifDictionary];
}
if (IsAtLeastiOSVersion(@"8.0")) {
[[self locationManager] performSelector:NSSelectorFromString(@"requestWhenInUseAuthorization") withObject:nil afterDelay:0];
}
[[self locationManager] startUpdatingLocation];
}
}
}
break;
default:
break;
};
return data;
}
- (NSString*)tempFilePath:(NSString*)extension
{
NSString* docsPath = [NSTemporaryDirectory()stringByStandardizingPath];
NSFileManager* fileMgr = [[NSFileManager alloc] init]; // recommended by Apple (vs [NSFileManager defaultManager]) to be threadsafe
NSString* filePath;
// unique file name
NSTimeInterval timeStamp = [[NSDate date] timeIntervalSince1970];
NSNumber *timeStampObj = [NSNumber numberWithDouble: timeStamp];
do {
filePath = [NSString stringWithFormat:@"%@/%@%ld.%@", docsPath, CDV_PHOTO_PREFIX, [timeStampObj longValue], extension];
} while ([fileMgr fileExistsAtPath:filePath]);
return filePath;
}
- (void)resultForImage:(CDVPictureOptions*)options info:(NSDictionary*)info completion:(void (^)(CDVPluginResult* res))completion
{
CDVPluginResult* result = nil;
BOOL saveToPhotoAlbum = options.saveToPhotoAlbum;
UIImage* image = nil;
switch (options.destinationType) {
case DestinationTypeNativeUri:
{
NSURL* url = [info objectForKey:UIImagePickerControllerReferenceURL];
saveToPhotoAlbum = NO;
// If, for example, we use sourceType = Camera, URL might be nil because image is stored in memory.
// In this case we must save image to device before obtaining an URI.
if (url == nil) {
image = [self retrieveImage:info options:options];
ALAssetsLibrary* library = [ALAssetsLibrary new];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:^(NSURL *assetURL, NSError *error) {
CDVPluginResult* resultToReturn = nil;
if (error) {
resultToReturn = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[error localizedDescription]];
} else {
NSString* nativeUri = [[self urlTransformer:assetURL] absoluteString];
resultToReturn = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
}
completion(resultToReturn);
}];
return;
} else {
NSString* nativeUri = [[self urlTransformer:url] absoluteString];
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nativeUri];
}
}
break;
case DestinationTypeFileUri:
{
image = [self retrieveImage:info options:options];
NSData* data = [self processImage:image info:info options:options];
if (data) {
NSString* extension = options.encodingType == EncodingTypePNG? @"png" : @"jpg";
NSString* filePath = [self tempFilePath:extension];
NSError* err = nil;
// save file
if (![data writeToFile:filePath options:NSAtomicWrite error:&err]) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_IO_EXCEPTION messageAsString:[err localizedDescription]];
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:[[self urlTransformer:[NSURL fileURLWithPath:filePath]] absoluteString]];
}
}
}
break;
case DestinationTypeDataUrl:
{
image = [self retrieveImage:info options:options];
NSData* data = [self processImage:image info:info options:options];
if (data) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:toBase64(data)];
}
}
break;
default:
break;
};
if (saveToPhotoAlbum && image) {
ALAssetsLibrary* library = [ALAssetsLibrary new];
[library writeImageToSavedPhotosAlbum:image.CGImage orientation:(ALAssetOrientation)(image.imageOrientation) completionBlock:nil];
}
completion(result);
}
- (NSString *) createTmpVideo:(NSString *) moviePath {
NSString* moviePathExtension = [moviePath pathExtension];
NSString* copyMoviePath = [self tempFilePath:moviePathExtension];
NSFileManager* fileMgr = [[NSFileManager alloc] init];
NSError *error;
[fileMgr copyItemAtPath:moviePath toPath:copyMoviePath error:&error];
return [[NSURL fileURLWithPath:copyMoviePath] absoluteString];
}
- (CDVPluginResult*)resultForVideo:(NSDictionary*)info
{
NSString* moviePath = [[info objectForKey:UIImagePickerControllerMediaURL] absoluteString];
// On iOS 13 the movie path becomes inaccessible, create and return a copy
if (IsAtLeastiOSVersion(@"13.0")) {
moviePath = [self createTmpVideo:[[info objectForKey:UIImagePickerControllerMediaURL] path]];
}
return [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:moviePath];
}
- (BOOL)usesGeolocation
{
id useGeo = [self.commandDelegate.settings objectForKey:[@"CameraUsesGeolocation" lowercaseString]];
return [(NSNumber*)useGeo boolValue];
}
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingMediaWithInfo:(NSDictionary*)info
{
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
__weak CustomCamera* weakSelf = self;
dispatch_block_t invoke = ^(void) {
__block CDVPluginResult* result = nil;
NSString* mediaType = [info objectForKey:UIImagePickerControllerMediaType];
if ([mediaType isEqualToString:(NSString*)kUTTypeImage]) {
[weakSelf resultForImage:cameraPicker.pictureOptions info:info completion:^(CDVPluginResult* res) {
if (![self usesGeolocation] || picker.sourceType != UIImagePickerControllerSourceTypeCamera) {
[weakSelf.commandDelegate sendPluginResult:res callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
}
}];
}
else {
result = [weakSelf resultForVideo:info];
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
}
};
if (cameraPicker.pictureOptions.popoverSupported && (cameraPicker.pickerPopoverController != nil)) {
[cameraPicker.pickerPopoverController dismissPopoverAnimated:YES];
cameraPicker.pickerPopoverController.delegate = nil;
cameraPicker.pickerPopoverController = nil;
invoke();
} else {
[[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:invoke];
}
}
// older api calls newer didFinishPickingMediaWithInfo
- (void)imagePickerController:(UIImagePickerController*)picker didFinishPickingImage:(UIImage*)image editingInfo:(NSDictionary*)editingInfo
{
NSDictionary* imageInfo = [NSDictionary dictionaryWithObject:image forKey:UIImagePickerControllerOriginalImage];
[self imagePickerController:picker didFinishPickingMediaWithInfo:imageInfo];
}
- (void)imagePickerControllerDidCancel:(UIImagePickerController*)picker
{
__weak CDVCameraPicker* cameraPicker = (CDVCameraPicker*)picker;
__weak CustomCamera* weakSelf = self;
dispatch_block_t invoke = ^ (void) {
CDVPluginResult* result;
if (picker.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to camera"];
} else if (picker.sourceType != UIImagePickerControllerSourceTypeCamera && ! IsAtLeastiOSVersion(@"11.0") && [ALAssetsLibrary authorizationStatus] != ALAuthorizationStatusAuthorized) {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"];
} else {
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No Image Selected"];
}
[weakSelf.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
weakSelf.hasPendingOperation = NO;
weakSelf.pickerController = nil;
};
[[cameraPicker presentingViewController] dismissViewControllerAnimated:YES completion:invoke];
}
- (void)startCamera:(CDVInvokedUrlCommand *)command {
lastCommand = command;
NSString *guid = [[NSUUID new] UUIDString];
NSString *uniqueFileName = [NSString stringWithFormat:@"%@.jpg", guid];
filename = uniqueFileName;
nSourceType = 1;
nDestType = 0;
CameraParameter *param = [[CameraParameter alloc] initWithCommand:lastCommand];
if (nSourceType == 0) {
UIImagePickerController *imagePickerController = [[UIImagePickerController alloc] init];
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
imagePickerController.delegate = self;
[self.viewController presentViewController:imagePickerController animated:YES completion:nil];
}
else {
if (![UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceRear]) {
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"No rear camera detected"];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
else if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"Camera is not accessible"];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
else {
AVCamViewController *cameraViewController = [[AVCamViewController alloc] initWithParams:param WithCallback: ^(UIImage *image, NSString *errorCode, NSString *message) {
@autoreleasepool {
if (image) {
if (nDestType == 0) {
CDVPictureOptions* options = [CDVPictureOptions createFromTakePictureArguments:command];
UIImage* scaledImage = nil;
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 (options.cropToSize) {
scaledImage = [image imageByScalingAndCroppingForSize:options.targetSize];
} else {
scaledImage = [image imageByScalingNotCroppingForSize:options.targetSize];
}
}
image= (scaledImage == nil ? image : scaledImage);
NSData *imageData = UIImageJPEGRepresentation(image, quality / 100);
NSString *strEncodeData = [imageData base64EncodedStringWithOptions:0];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:strEncodeData];
[self.viewController dismissViewControllerAnimated:YES completion:nil];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
else {
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *imagePath = [documentsDirectory stringByAppendingPathComponent:filename];
NSData *imageData = UIImageJPEGRepresentation(image, quality / 100);
[imageData writeToFile:imagePath atomically:YES];
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:[[NSURL fileURLWithPath:imagePath] absoluteString]];
[self.viewController dismissViewControllerAnimated:YES completion:nil];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
}
else {
//error
NSDictionary *error = @{ @"code":errorCode, @"message":message };
CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsDictionary:error];
[self.viewController dismissViewControllerAnimated:YES completion:nil];
[self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
}
}
}];
if (@available(iOS 13.0, *)){
cameraViewController.modalPresentationStyle = UIModalPresentationFullScreen;
}
[self.viewController presentViewController:cameraViewController animated:YES completion:nil];
}
}
}
- (void)deleteFileWithName:(NSString *)fileName {
NSFileManager *manager = [NSFileManager defaultManager];
// Need to check if the to be deleted file exists.
if ([manager fileExistsAtPath:fileName]) {
NSError *error = nil;
// This function also returnsYES if the item was removed successfully or if path was nil.
// Returns NO if an error occurred.
[manager removeItemAtPath:fileName error:&error];
if (error) {
NSLog(@"There is an Error: %@", error);
}
}
else {
NSLog(@"File %@ doesn't exists", fileName);
}
}
@end

BIN
src/ios/image/icon_back.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
src/ios/image/icon_delete.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/ios/image/icon_flash.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/ios/image/icon_flash_auto.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/ios/image/icon_flash_off.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
src/ios/image/icon_flip.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
src/ios/image/icon_max.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/ios/image/icon_min.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
src/ios/image/icon_submit.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
src/ios/image/sample.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

View File

@ -1,6 +1,6 @@
{
"name": "cordova-plugin-camera-tests",
"version": "4.1.1-dev",
"version": "4.2.0-dev",
"description": "",
"cordova": {
"id": "cordova-plugin-camera-tests",

View File

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

View File

@ -136,7 +136,7 @@ cameraExport.getPicture = function (successCallback, errorCallback, options) {
options = options || {};
var getValue = argscheck.getValue;
var quality = getValue(options.quality, 50);
var quality = getValue(options.quality, 100);
var destinationType = getValue(options.destinationType, Camera.DestinationType.FILE_URI);
var sourceType = getValue(options.sourceType, Camera.PictureSourceType.CAMERA);
var targetWidth = getValue(options.targetWidth, -1);