Compare commits
32 Commits
janpio-par
...
master
Author | SHA1 | Date | |
---|---|---|---|
4afa471fb6 | |||
a19ba493b3 | |||
c95f4f7501 | |||
6c38d126ec | |||
ffaef73016 | |||
4d59795df1 | |||
6e82b526a2 | |||
f13d7c7aa4 | |||
2c804cea55 | |||
![]() |
6d76d1defe | ||
![]() |
47da7a7c29 | ||
![]() |
739cb20679 | ||
![]() |
9782d52be4 | ||
![]() |
852531344a | ||
![]() |
07e7444ee1 | ||
![]() |
ad603fcdaa | ||
![]() |
0a2c778c5f | ||
![]() |
ba4f77468f | ||
![]() |
adf15799d9 | ||
![]() |
e9aba07926 | ||
![]() |
df14414203 | ||
![]() |
2a750f0fa1 | ||
![]() |
06ac9b3dc9 | ||
![]() |
bfa38fed2c | ||
![]() |
7611960ef2 | ||
![]() |
f4067e22d9 | ||
![]() |
f33ade83e4 | ||
![]() |
6f25420ce7 | ||
![]() |
6feaa95559 | ||
![]() |
c887c229f0 | ||
![]() |
6900fbaefa | ||
![]() |
a9436b1a18 |
@ -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
@ -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
@ -0,0 +1,3 @@
|
||||
.*
|
||||
appveyor.yml
|
||||
tests
|
27
.travis.yml
@ -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
|
||||
|
@ -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
@ -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|
|
||||
|:-:|:-:|
|
||||
|[](https://ci.appveyor.com/project/ApacheSoftwareFoundation/cordova-plugin-camera)|[](https://travis-ci.org/apache/cordova-plugin-camera)|
|
||||
|
||||
# cordova-plugin-camera
|
||||
|
||||
This plugin defines a global `navigator.camera` object, which provides an API for taking pictures and for choosing images from
|
||||
the system's image library.
|
||||
|
||||
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)
|
@ -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))
|
||||
|
@ -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": {
|
||||
|
73
plugin.xml
@ -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="/*">
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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>
|
@ -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
|
||||
|
9
src/ios/classes/AVCamPreviewView.h
Normal file
@ -0,0 +1,9 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class AVCaptureSession;
|
||||
|
||||
@interface AVCamPreviewView : UIView
|
||||
|
||||
@property (nonatomic) AVCaptureSession *session;
|
||||
|
||||
@end
|
21
src/ios/classes/AVCamPreviewView.m
Normal 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
|
35
src/ios/classes/AVCamViewController.h
Executable 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
|
811
src/ios/classes/AVCamViewController.m
Executable 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 AVCaptureVideoPreviewLayer’s 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
|
181
src/ios/classes/AVCamViewController_iPad.xib
Executable 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>
|
187
src/ios/classes/AVCamViewController_iPhone.xib
Executable 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>
|
33
src/ios/classes/CameraParameter.h
Normal 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
|
66
src/ios/classes/CameraParameter.m
Normal 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
@ -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
@ -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
After Width: | Height: | Size: 3.1 KiB |
BIN
src/ios/image/icon_capture.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/ios/image/icon_capture_pressed.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
src/ios/image/icon_delete.png
Executable file
After Width: | Height: | Size: 3.0 KiB |
BIN
src/ios/image/icon_flash.png
Executable file
After Width: | Height: | Size: 3.4 KiB |
BIN
src/ios/image/icon_flash_auto.png
Executable file
After Width: | Height: | Size: 3.5 KiB |
BIN
src/ios/image/icon_flash_off.png
Executable file
After Width: | Height: | Size: 3.8 KiB |
BIN
src/ios/image/icon_flip.png
Executable file
After Width: | Height: | Size: 3.7 KiB |
BIN
src/ios/image/icon_max.png
Executable file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/ios/image/icon_min.png
Executable file
After Width: | Height: | Size: 3.1 KiB |
BIN
src/ios/image/icon_submit.png
Executable file
After Width: | Height: | Size: 3.2 KiB |
BIN
src/ios/image/sample.png
Normal file
After Width: | Height: | Size: 256 KiB |
@ -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",
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
|