Compare commits

...

15 Commits

Author SHA1 Message Date
Murat Sutunc 0d37267eb4 Update docs. This closes #100 2015-06-09 17:46:49 -07:00
Raymond Camden 88ee99011c attempt to fix npm markdown issue 2015-06-02 10:42:32 -05:00
Murat Sutunc a6cc9271a0 CB-8883 fix picture rotation issue 2015-05-26 12:32:53 -07:00
Jesse MacFadyen b3430e6f80 one more alias 2015-05-20 16:31:59 -07:00
Jesse MacFadyen 888a9661fb Fixed some nit white-space issues, aliased a little more 2015-05-20 16:17:57 -07:00
Jesse MacFadyen 15f66bf5cd major refactor : readability 2015-05-18 21:38:34 -07:00
Jesse MacFadyen 33ffb17c97 Patch for CB-8498, this closes #64 2015-05-18 17:32:14 -07:00
Murat Sutunc a830b3e08b CB-8879 fix stripe issue with correct aspect ratio 2015-05-13 11:50:03 -07:00
Shazron Abdullah 706a3aa73e CB-8601 - iOS camera unit tests broken 2015-05-11 17:39:40 -07:00
Dan Polivy 9f9e5ef4a9 CB-7667 iOS8: Handle case where camera is not authorized (closes #49)
In iOS 7+, when the app does not have access to the camera, show a
prompt notifying the user so they're not puzzled by looking at a black
screen.

In iOS 8+, include a link on the dialog to open the Setting app to allow
the user to change their Camera privacy setting.

Signed-off-by: Shazron Abdullah <shazron@apache.org>
2015-05-11 17:26:00 -07:00
Jesse MacFadyen b8b53c6254 add missing license header 2015-05-07 16:04:19 -07:00
Steve Gill 0436928275 CB-8959 Incremented plugin version. 2015-05-06 17:37:22 -07:00
Steve Gill 19feee9cb0 CB-8959 Updated version and RELEASENOTES.md for release 1.1.0 2015-05-06 17:07:39 -07:00
Murat Sutunc 69c687e0cf CB-8943 fix PickAndContinue issue on Win10Phone 2015-05-06 12:05:36 -07:00
Alan Kinzie b0ee9dd905 CB-8253 Fix potential unreleased resources
There was a place (~line 701) in CameraLauncher.java where there was the
potential for input and output streams to never be closed if an exception
occurs at the wrong time.   There were some other places where an
InputStream was used anonymously, and so would never be closed.

This change introduces try/finally blocks to ensure that the streams will
always end up closed.

Change-Id: I479bceddcd631bfec45c3f5ee7e88ddb04c59073

Signed-off-by: Joe Bowser <bowserj@apache.org>

(Closes #90)
2015-05-06 10:17:43 -07:00
28 changed files with 610 additions and 348 deletions
+99 -110
View File
@@ -1,20 +1,20 @@
<!---
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.
# 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-camera
@@ -35,6 +35,16 @@ Although the object is attached to the global scoped `navigator`, it is not avai
cordova plugin add cordova-plugin-camera
## API
- Camera
- navigator.camera.getPicture(success, fail, options)
- CameraOptions
- CameraPopoverHandle
- CameraPopoverOptions
- navigator.camera.cleanup
## navigator.camera.getPicture
Takes a photo using the camera, or retrieves a photo from the device's
@@ -43,9 +53,9 @@ base64-encoded `String`, or as the URI for the image file. The method
itself returns a `CameraPopoverHandle` object that can be used to
reposition the file selection popover.
navigator.camera.getPicture( cameraSuccess, cameraError, cameraOptions );
navigator.camera.getPicture(cameraSuccess, cameraError, cameraOptions);
### Description
#### Description
The `camera.getPicture` function opens the device's default camera
application that allows users to snap pictures. This behavior occurs
@@ -83,69 +93,11 @@ quality, even if a `quality` parameter is specified. To avoid common
memory problems, set `Camera.destinationType` to `FILE_URI` rather
than `DATA_URL`.
### Supported Platforms
#### Supported Platforms
- Amazon Fire OS
- Android
- BlackBerry 10
- Browser
- Firefox OS
- iOS
- Tizen
- Windows Phone 7 and 8
- Windows 8
- Windows
![](doc/img/android-success.png) ![](doc/img/blackberry-success.png) ![](doc/img/browser-success.png) ![](doc/img/firefox-success.png) ![](doc/img/fireos-success.png) ![](doc/img/ios-success.png) ![](doc/img/windows-success.png) ![](doc/img/wp8-success.png) ![](doc/img/ubuntu-success.png)
### Preferences (iOS)
- __CameraUsesGeolocation__ (boolean, defaults to false). For capturing JPEGs, set to true to get geolocation data in the EXIF header. This will trigger a request for geolocation permissions if set to true.
<preference name="CameraUsesGeolocation" value="false" />
### Amazon Fire OS Quirks
Amazon Fire OS uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the cordova activity is restored.
### Android Quirks
Android uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the Cordova activity is restored.
### Browser Quirks
Can only return photos as base64-encoded image.
### Firefox OS Quirks
Camera plugin is currently implemented using [Web Activities](https://hacks.mozilla.org/2013/01/introducing-web-activities/).
### iOS Quirks
Including a JavaScript `alert()` in either of the callback functions
can cause problems. Wrap the alert within a `setTimeout()` to allow
the iOS image picker or popover to fully close before the alert
displays:
setTimeout(function() {
// do your thing here!
}, 0);
### Windows Phone 7 Quirks
Invoking the native camera application while the device is connected
via Zune does not work, and triggers an error callback.
### Tizen Quirks
Tizen only supports a `destinationType` of
`Camera.DestinationType.FILE_URI` and a `sourceType` of
`Camera.PictureSourceType.PHOTOLIBRARY`.
### Example
#### Example
Take a photo and retrieve it as a base64-encoded image:
@@ -176,6 +128,54 @@ Take a photo and retrieve the image's file location:
alert('Failed because: ' + message);
}
#### Preferences (iOS)
- __CameraUsesGeolocation__ (boolean, defaults to false). For capturing JPEGs, set to true to get geolocation data in the EXIF header. This will trigger a request for geolocation permissions if set to true.
<preference name="CameraUsesGeolocation" value="false" />
#### Amazon Fire OS Quirks
Amazon Fire OS uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the cordova activity is restored.
#### Android Quirks
Android uses intents to launch the camera activity on the device to capture
images, and on phones with low memory, the Cordova activity may be killed. In this
scenario, the image may not appear when the Cordova activity is restored.
#### Browser Quirks
Can only return photos as base64-encoded image.
#### Firefox OS Quirks
Camera plugin is currently implemented using [Web Activities](https://hacks.mozilla.org/2013/01/introducing-web-activities/).
#### iOS Quirks
Including a JavaScript `alert()` in either of the callback functions
can cause problems. Wrap the alert within a `setTimeout()` to allow
the iOS image picker or popover to fully close before the alert
displays:
setTimeout(function() {
// do your thing here!
}, 0);
#### Windows Phone 7 Quirks
Invoking the native camera application while the device is connected
via Zune does not work, and triggers an error callback.
#### Tizen Quirks
Tizen only supports a `destinationType` of
`Camera.DestinationType.FILE_URI` and a `sourceType` of
`Camera.PictureSourceType.PHOTOLIBRARY`.
## CameraOptions
Optional parameters to customize the camera settings.
@@ -190,8 +190,6 @@ Optional parameters to customize the camera settings.
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false };
### Options
- __quality__: Quality of the saved image, expressed as a range of 0-100, where 100 is typically full resolution with no loss from file compression. The default is 50. _(Number)_ (Note that information about the camera's resolution is unavailable.)
- __destinationType__: Choose the format of the return value. The default is FILE_URI. Defined in `navigator.camera.DestinationType` _(Number)_
@@ -244,7 +242,7 @@ Optional parameters to customize the camera settings.
FRONT : 1 // Use the front-facing camera
};
### Amazon Fire OS Quirks
#### Amazon Fire OS Quirks
- Any `cameraDirection` value results in a back-facing photo.
@@ -252,7 +250,7 @@ Optional parameters to customize the camera settings.
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
### Android Quirks
#### Android Quirks
- Any `cameraDirection` value results in a back-facing photo.
@@ -261,7 +259,7 @@ with the Google Plus Photos application. Other crops may not work.
- `Camera.PictureSourceType.PHOTOLIBRARY` and `Camera.PictureSourceType.SAVEDPHOTOALBUM` both display the same photo album.
### BlackBerry 10 Quirks
#### BlackBerry 10 Quirks
- Ignores the `quality` parameter.
@@ -273,7 +271,7 @@ with the Google Plus Photos application. Other crops may not work.
- Ignores the `cameraDirection` parameter.
### Firefox OS Quirks
#### Firefox OS Quirks
- Ignores the `quality` parameter.
@@ -293,19 +291,19 @@ with the Google Plus Photos application. Other crops may not work.
- Ignores the `cameraDirection` parameter.
### iOS Quirks
#### iOS Quirks
- Set `quality` below 50 to avoid memory errors on some devices.
- When using `destinationType.FILE_URI`, photos are saved in the application's temporary directory. The contents of the application's temporary directory is deleted when the application ends.
### Tizen Quirks
#### Tizen Quirks
- options not supported
- always returns a FILE URI
### Windows Phone 7 and 8 Quirks
#### Windows Phone 7 and 8 Quirks
- Ignores the `allowEdit` parameter.
@@ -327,7 +325,7 @@ onError callback function that provides an error message.
// Show a helpful message
}
### Parameters
#### Description
- __message__: The message is provided by the device's native code. _(String)_
@@ -340,11 +338,11 @@ onSuccess callback function that provides the image data.
// Do something with the image
}
### Parameters
#### Description
- __imageData__: Base64 encoding of the image data, _or_ the image file URI, depending on `cameraOptions` in effect. _(String)_
### Example
#### Example
// Show image
//
@@ -358,23 +356,15 @@ onSuccess callback function that provides the image data.
A handle to the popover dialog created by `navigator.camera.getPicture`.
### Methods
#### Description
- __setPosition__: Set the position of the popover.
- __setPosition__: Set the position of the popover. Takes the `CameraPopoverOptions` that specify the new position.
### Supported Platforms
#### Supported Platforms
- iOS
![](doc/img/android-fail.png) ![](doc/img/blackberry-fail.png) ![](doc/img/browser-fail.png) ![](doc/img/firefox-fail.png) ![](doc/img/fireos-fail.png) ![](doc/img/ios-success.png) ![](doc/img/windows-fail.png) ![](doc/img/wp8-fail.png) ![](doc/img/ubuntu-fail.png)
### setPosition
Set the position of the popover.
__Parameters__:
- `cameraPopoverOptions`: the `CameraPopoverOptions` that specify the new position
### Example
#### Example
var cameraPopoverHandle = navigator.camera.getPicture(onSuccess, onFail,
{ destinationType: Camera.DestinationType.FILE_URI,
@@ -388,7 +378,6 @@ __Parameters__:
cameraPopoverHandle.setPosition(cameraPopoverOptions);
}
## CameraPopoverOptions
iOS-only parameters that specify the anchor element location and arrow
@@ -402,7 +391,7 @@ or album.
arrowDir : Camera.PopoverArrowDirection.ARROW_ANY
};
### CameraPopoverOptions
#### Description
- __x__: x pixel coordinate of screen element onto which to anchor the popover. _(Number)_
@@ -434,18 +423,18 @@ storage.
navigator.camera.cleanup( cameraSuccess, cameraError );
### Description
#### Description
Removes intermediate image files that are kept in temporary storage
after calling `camera.getPicture`. Applies only when the value of
`Camera.sourceType` equals `Camera.PictureSourceType.CAMERA` and the
`Camera.destinationType` equals `Camera.DestinationType.FILE_URI`.
### Supported Platforms
#### Supported Platforms
- iOS
![](doc/img/android-fail.png) ![](doc/img/blackberry-fail.png) ![](doc/img/browser-fail.png) ![](doc/img/firefox-fail.png) ![](doc/img/fireos-fail.png) ![](doc/img/ios-success.png) ![](doc/img/windows-fail.png) ![](doc/img/wp8-fail.png) ![](doc/img/ubuntu-fail.png)
### Example
#### Example
navigator.camera.cleanup(onSuccess, onFail);
+9
View File
@@ -197,3 +197,12 @@
* docs: added 'Windows' to supported platforms
* CB-8653 Updated Readme
* CB-8659: ios: 4.0.x Compatibility: Remove use of deprecated headers
### 1.1.0 (May 06, 2015)
* CB-8943 fix `PickAndContinue` issue on *Win10Phone*
* CB-8253 Fix potential unreleased resources
* CB-8909: Remove unused import from File
* CB-8404 typo fix `cameraproxy.js`
* CB-8404 Rotate camera feed with device orientation
* CB-8054 Support taking pictures from file for *WP8*
* CB-8405 Use `z-index` instead of `z-order`
Binary file not shown.

After

Width:  |  Height:  |  Size: 753 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 716 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 984 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 802 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 770 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 573 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 649 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 714 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 679 B

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "cordova-plugin-camera",
"version": "1.0.1-dev",
"version": "1.1.1-dev",
"description": "Cordova Camera Plugin",
"cordova": {
"id": "cordova-plugin-camera",
+2 -1
View File
@@ -22,7 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rim="http://www.blackberry.com/ns/widgets"
id="cordova-plugin-camera"
version="1.0.1-dev">
version="1.1.1-dev">
<name>Camera</name>
<description>Cordova Camera Plugin</description>
<license>Apache 2.0</license>
@@ -146,6 +146,7 @@
<framework src="AssetsLibrary.framework" />
<framework src="MobileCoreServices.framework" />
<framework src="CoreGraphics.framework" />
<framework src="AVFoundation.framework" />
<config-file target="*-Info.plist" parent="NSLocationWhenInUseUsageDescription">
<string></string>
+68 -12
View File
@@ -24,6 +24,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.text.SimpleDateFormat;
@@ -761,16 +762,33 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
*/
private void writeUncompressedImage(Uri uri) throws FileNotFoundException,
IOException {
FileInputStream fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
FileInputStream fis = null;
OutputStream os = null;
try {
fis = new FileInputStream(FileHelper.stripFileProtocol(imageUri.toString()));
os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
byte[] buffer = new byte[4096];
int len;
while ((len = fis.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
os.flush();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
LOG.d(LOG_TAG,"Exception while closing output stream.");
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
LOG.d(LOG_TAG,"Exception while closing file input stream.");
}
}
}
os.flush();
os.close();
fis.close();
}
/**
@@ -806,13 +824,39 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
private Bitmap getScaledBitmap(String imageUrl) throws IOException {
// If no new width or height were specified return the original bitmap
if (this.targetWidth <= 0 && this.targetHeight <= 0) {
return BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova));
InputStream fileStream = null;
Bitmap image = null;
try {
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
image = BitmapFactory.decodeStream(fileStream);
} finally {
if (fileStream != null) {
try {
fileStream.close();
} catch (IOException e) {
LOG.d(LOG_TAG,"Exception while closing file input stream.");
}
}
}
return image;
}
// figure out the original width and height of the image
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
InputStream fileStream = null;
try {
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
BitmapFactory.decodeStream(fileStream, null, options);
} finally {
if (fileStream != null) {
try {
fileStream.close();
} catch (IOException e) {
LOG.d(LOG_TAG,"Exception while closing file input stream.");
}
}
}
//CB-2292: WTF? Why is the width null?
if(options.outWidth == 0 || options.outHeight == 0)
@@ -826,7 +870,19 @@ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
// Load in the smallest bitmap possible that is closest to the size we want
options.inJustDecodeBounds = false;
options.inSampleSize = calculateSampleSize(options.outWidth, options.outHeight, this.targetWidth, this.targetHeight);
Bitmap unscaledBitmap = BitmapFactory.decodeStream(FileHelper.getInputStreamFromUriString(imageUrl, cordova), null, options);
Bitmap unscaledBitmap = null;
try {
fileStream = FileHelper.getInputStreamFromUriString(imageUrl, cordova);
unscaledBitmap = BitmapFactory.decodeStream(fileStream, null, options);
} finally {
if (fileStream != null) {
try {
fileStream.close();
} catch (IOException e) {
LOG.d(LOG_TAG,"Exception while closing file input stream.");
}
}
}
if (unscaledBitmap == null) {
return null;
}
+43 -1
View File
@@ -24,6 +24,7 @@
#import <ImageIO/CGImageProperties.h>
#import <AssetsLibrary/ALAssetRepresentation.h>
#import <AssetsLibrary/AssetsLibrary.h>
#import <AVFoundation/AVFoundation.h>
#import <ImageIO/CGImageSource.h>
#import <ImageIO/CGImageProperties.h>
#import <ImageIO/CGImageDestination.h>
@@ -141,7 +142,29 @@ static NSString* toBase64(NSData* data) {
[weakSelf.commandDelegate sendPluginResult:result callbackId:command.callbackId];
return;
}
// Validate the app has permission to access the camera
if (pictureOptions.sourceType == UIImagePickerControllerSourceTypeCamera && [AVCaptureDevice respondsToSelector:@selector(authorizationStatusForMediaType:)]) {
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
if (authStatus == AVAuthorizationStatusDenied ||
authStatus == AVAuthorizationStatusRestricted) {
// If iOS 8+, offer a link to the Settings app
NSString* settingsButton = (&UIApplicationOpenSettingsURLString != NULL)
? NSLocalizedString(@"Settings", nil)
: nil;
// Denied; show an alert
dispatch_async(dispatch_get_main_queue(), ^{
[[[UIAlertView alloc] initWithTitle:[[NSBundle mainBundle]
objectForInfoDictionaryKey:@"CFBundleDisplayName"]
message:NSLocalizedString(@"Access to the camera has been prohibited; please enable it in the Settings app to continue.", nil)
delegate:self
cancelButtonTitle:NSLocalizedString(@"OK", nil)
otherButtonTitles:settingsButton, nil] show];
});
}
}
CDVCameraPicker* cameraPicker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
weakSelf.pickerController = cameraPicker;
@@ -174,6 +197,25 @@ static NSString* toBase64(NSData* data) {
}];
}
// Delegate for camera permission UIAlertView
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
// If Settings button (on iOS 8), open the settings app
if (buttonIndex == 1) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
// Dismiss the view
[[self.pickerController presentingViewController] dismissViewControllerAnimated:YES completion:nil];
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to camera"]; // error callback expects string ATM
[self.commandDelegate sendPluginResult:result callbackId:self.pickerController.callbackId];
self.hasPendingOperation = NO;
self.pickerController = nil;
}
- (void)repositionPopover:(CDVInvokedUrlCommand*)command
{
NSDictionary* options = [command argumentAtIndex:0 withDefault:nil];
+352 -217
View File
@@ -25,6 +25,19 @@
var Camera = require('./Camera');
var getAppData = function () {
return Windows.Storage.ApplicationData.current;
};
var encodeToBase64String = function (buffer) {
return Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
};
var OptUnique = Windows.Storage.CreationCollisionOption.generateUniqueName;
var CapMSType = Windows.Media.Capture.MediaStreamType;
var webUIApp = Windows.UI.WebUI.WebUIApplication;
var fileIO = Windows.Storage.FileIO;
var pickerLocId = Windows.Storage.Pickers.PickerLocationId;
module.exports = {
// args will contain :
@@ -57,6 +70,9 @@ module.exports = {
var windowsVideoContainers = [".avi", ".flv", ".asx", ".asf", ".mov", ".mp4", ".mpg", ".rm", ".srt", ".swf", ".wmv", ".vob"];
var windowsPhoneVideoContainers = [".avi", ".3gp", ".3g2", ".wmv", ".3gp", ".3g2", ".mp4", ".m4v"];
// Default aspect ratio 1.78 (16:9 hd video standard)
var DEFAULT_ASPECT_RATIO = '1.8';
// Resize method
function resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType) {
var tempPhotoFileName = "";
@@ -66,11 +82,13 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
tempPhotoFileName = "camera_cordova_temp_return.jpg";
}
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
var storageFolder = getAppData().localFolder;
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting)
.then(function (storageFile) { return Windows.Storage.FileIO.readBufferAsync(storageFile); })
.then(function (storageFile) {
return fileIO.readBufferAsync(storageFile);
})
.then(function(buffer) {
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
var strBase64 = encodeToBase64String(buffer);
var imageData = "data:" + file.contentType + ";base64," + strBase64;
var image = new Image();
image.src = imageData;
@@ -87,13 +105,13 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
var fileContent = canvas.toDataURL(file.contentType).split(',')[1];
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
var storageFolder = getAppData().localFolder;
storageFolder.createFileAsync(tempPhotoFileName, Windows.Storage.CreationCollisionOption.generateUniqueName)
storageFolder.createFileAsync(tempPhotoFileName, OptUnique)
.then(function (storagefile) {
var content = Windows.Security.Cryptography.CryptographicBuffer.decodeFromBase64String(fileContent);
storageFileName = storagefile.name;
return Windows.Storage.FileIO.writeBufferAsync(storagefile, content);
return fileIO.writeBufferAsync(storagefile, content);
})
.done(function () {
successCallback("ms-appdata:///local/" + storageFileName);
@@ -108,8 +126,8 @@ function resizeImage(successCallback, errorCallback, file, targetWidth, targetHe
// Because of asynchronous method, so let the successCallback be called in it.
function resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight) {
Windows.Storage.FileIO.readBufferAsync(file).done( function(buffer) {
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
fileIO.readBufferAsync(file).done( function(buffer) {
var strBase64 = encodeToBase64String(buffer);
var imageData = "data:" + file.contentType + ";base64," + strBase64;
var image = new Image();
@@ -163,7 +181,7 @@ function takePictureFromFileWP(successCallback, errorCallback, args) {
var file = eventArgs.files[0];
if (!file) {
errorCallback("User didn't choose a file.");
Windows.UI.WebUI.WebUIApplication.removeEventListener("activated", filePickerActivationHandler);
webUIApp.removeEventListener("activated", filePickerActivationHandler);
return;
}
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
@@ -171,9 +189,14 @@ function takePictureFromFileWP(successCallback, errorCallback, args) {
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
}
else {
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
var storageFolder = getAppData().localFolder;
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
successCallback(URL.createObjectURL(storageFile));
if(destinationType == Camera.DestinationType.NATIVE_URI) {
successCallback("ms-appdata:///local/" + storageFile.name);
}
else {
successCallback(URL.createObjectURL(storageFile));
}
}, function () {
errorCallback("Can't access localStorage folder.");
});
@@ -183,31 +206,31 @@ function takePictureFromFileWP(successCallback, errorCallback, args) {
if (targetHeight > 0 && targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
} else {
Windows.Storage.FileIO.readBufferAsync(file).done(function (buffer) {
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
fileIO.readBufferAsync(file).done(function (buffer) {
var strBase64 =encodeToBase64String(buffer);
successCallback(strBase64);
}, errorCallback);
}
}
Windows.UI.WebUI.WebUIApplication.removeEventListener("activated", filePickerActivationHandler);
webUIApp.removeEventListener("activated", filePickerActivationHandler);
}
};
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
if (mediaType == Camera.MediaType.PICTURE) {
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
}
else if (mediaType == Camera.MediaType.VIDEO) {
fileOpenPicker.fileTypeFilter.replaceAll(windowsPhoneVideoContainers);
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.videosLibrary;
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
}
else {
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
}
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", filePickerActivationHandler);
webUIApp.addEventListener("activated", filePickerActivationHandler);
fileOpenPicker.pickSingleFileAndContinue();
}
@@ -221,15 +244,15 @@ function takePictureFromFileWindows(successCallback, errorCallback, args) {
var fileOpenPicker = new Windows.Storage.Pickers.FileOpenPicker();
if (mediaType == Camera.MediaType.PICTURE) {
fileOpenPicker.fileTypeFilter.replaceAll([".png", ".jpg", ".jpeg"]);
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
fileOpenPicker.suggestedStartLocation = pickerLocId.picturesLibrary;
}
else if (mediaType == Camera.MediaType.VIDEO) {
fileOpenPicker.fileTypeFilter.replaceAll(windowsVideoContainers);
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.videosLibrary;
fileOpenPicker.suggestedStartLocation = pickerLocId.videosLibrary;
}
else {
fileOpenPicker.fileTypeFilter.replaceAll(["*"]);
fileOpenPicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.documentsLibrary;
fileOpenPicker.suggestedStartLocation = pickerLocId.documentsLibrary;
}
fileOpenPicker.pickSingleFileAsync().done(function (file) {
@@ -242,7 +265,7 @@ function takePictureFromFileWindows(successCallback, errorCallback, args) {
resizeImage(successCallback, errorCallback, file, targetWidth, targetHeight, encodingType);
}
else {
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
var storageFolder = getAppData().localFolder;
file.copyAsync(storageFolder, file.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function (storageFile) {
successCallback(URL.createObjectURL(storageFile));
}, function () {
@@ -254,8 +277,8 @@ function takePictureFromFileWindows(successCallback, errorCallback, args) {
if (targetHeight > 0 && targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, file, targetWidth, targetHeight);
} else {
Windows.Storage.FileIO.readBufferAsync(file).done(function (buffer) {
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
fileIO.readBufferAsync(file).done(function (buffer) {
var strBase64 =encodeToBase64String(buffer);
successCallback(strBase64);
}, errorCallback);
}
@@ -287,18 +310,8 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
captureCancelButton = null,
capture = null,
captureSettings = null,
CaptureNS = Windows.Media.Capture;
function cameraPreviewOrientation(arg) {
// Rotate the cam since WP8.1 MediaCapture outputs video stream rotated 90° CCW
if (screen.msOrientation === "portrait-primary" || screen.msOrientation === "portrait-secondary") {
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise90Degrees);
} else if (screen.msOrientation === "landscape-secondary") {
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.clockwise180Degrees);
} else {
capture.setPreviewRotation(Windows.Media.Capture.VideoRotation.none);
}
}
CaptureNS = Windows.Media.Capture,
sensor = null;
var createCameraUI = function () {
// Create fullscreen preview
@@ -322,9 +335,10 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
var startCameraPreview = function () {
// Search for available camera devices
// This is necessary to detect which camera (front or back) we should use
var expectedPanel = cameraDirection === 1 ? Windows.Devices.Enumeration.Panel.front : Windows.Devices.Enumeration.Panel.back;
Windows.Devices.Enumeration.DeviceInformation.findAllAsync(Windows.Devices.Enumeration.DeviceClass.videoCapture)
.done(function (devices) {
var DeviceEnum = Windows.Devices.Enumeration;
var expectedPanel = cameraDirection === 1 ? DeviceEnum.Panel.front : DeviceEnum.Panel.back;
DeviceEnum.DeviceInformation.findAllAsync(DeviceEnum.DeviceClass.videoCapture).then(function (devices) {
if (devices.length <= 0) {
destroyCameraPreview();
errorCallback('Camera not found');
@@ -337,32 +351,63 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
}
});
capture.initializeAsync(captureSettings).done(function () {
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
capturePreview.msZoom = true;
capturePreview.src = URL.createObjectURL(capture);
capturePreview.play();
captureSettings.photoCaptureSource = Windows.Media.Capture.PhotoCaptureSource.photo;
// Insert preview frame and controls into page
document.body.appendChild(capturePreview);
document.body.appendChild(captureCancelButton);
return capture.initializeAsync(captureSettings);
}).then(function () {
// msdn.microsoft.com/en-us/library/windows/apps/hh452807.aspx
capturePreview.msZoom = true;
capturePreview.src = URL.createObjectURL(capture);
capturePreview.play();
// Bind events to controls
window.addEventListener('deviceorientation', cameraPreviewOrientation, false);
capturePreview.addEventListener('click', captureAction);
captureCancelButton.addEventListener('click', function () {
destroyCameraPreview();
errorCallback('Cancelled');
}, false);
}, function (err) {
// Bind events to controls
sensor = Windows.Devices.Sensors.SimpleOrientationSensor.getDefault();
if (sensor !== null) {
sensor.addEventListener("orientationchanged", onOrientationChange);
}
capturePreview.addEventListener('click', captureAction);
captureCancelButton.addEventListener('click', function () {
destroyCameraPreview();
errorCallback('Camera intitialization error ' + err);
});
}, errorCallback);
errorCallback('Cancelled');
}, false);
// Change default orientation
if (sensor) {
setPreviewRotation(sensor.getCurrentOrientation());
} else {
setPreviewRotation(Windows.Graphics.Display.DisplayInformation.getForCurrentView().currentOrientation);
}
// Get available aspect ratios
var aspectRatios = getAspectRatios(capture);
// Couldn't find a good ratio
if (aspectRatios.length === 0) {
destroyCameraPreview();
errorCallback('There\'s not a good aspect ratio available');
return;
}
// Insert preview frame and controls into page
document.body.appendChild(capturePreview);
document.body.appendChild(captureCancelButton);
if (aspectRatios.indexOf(DEFAULT_ASPECT_RATIO) > -1) {
return setAspectRatio(capture, DEFAULT_ASPECT_RATIO);
} else {
// Doesn't support 16:9 - pick next best
return setAspectRatio(capture, aspectRatios[0]);
}
}).done(null, function (err) {
destroyCameraPreview();
errorCallback('Camera intitialization error ' + err);
});
};
var destroyCameraPreview = function () {
window.removeEventListener('deviceorientation', cameraPreviewOrientation, false);
if (sensor !== null) {
sensor.removeEventListener('orientationchanged', onOrientationChange);
}
capturePreview.pause();
capturePreview.src = null;
[capturePreview, captureCancelButton].forEach(function(elem) {
@@ -380,8 +425,7 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
var encodingProperties,
fileName,
generateUniqueCollisionOption = Windows.Storage.CreationCollisionOption.generateUniqueName,
tempFolder = Windows.Storage.ApplicationData.current.temporaryFolder;
tempFolder = getAppData().temporaryFolder;
if (encodingType == Camera.EncodingType.PNG) {
fileName = 'photo.png';
@@ -391,118 +435,171 @@ function takePictureFromCameraWP(successCallback, errorCallback, args) {
encodingProperties = Windows.Media.MediaProperties.ImageEncodingProperties.createJpeg();
}
tempFolder.createFileAsync(fileName, generateUniqueCollisionOption)
tempFolder.createFileAsync(fileName, OptUnique)
.then(function(tempCapturedFile) {
return new WinJS.Promise(function (complete) {
var imgStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
capture.capturePhotoToStreamAsync(encodingProperties, imgStream)
var photoStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
var finalStream = new Windows.Storage.Streams.InMemoryRandomAccessStream();
capture.capturePhotoToStreamAsync(encodingProperties, photoStream)
.then(function() {
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(imgStream);
return Windows.Graphics.Imaging.BitmapDecoder.createAsync(photoStream);
})
.then(function(dec) {
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(imgStream, dec);
finalStream.size = 0; // BitmapEncoder requires the output stream to be empty
return Windows.Graphics.Imaging.BitmapEncoder.createForTranscodingAsync(finalStream, dec);
})
.then(function(enc) {
// We need to rotate the photo 90° CW because by default wp8.1 takes 90° CCW rotated photos.
enc.bitmapTransform.rotation = Windows.Graphics.Imaging.BitmapRotation.clockwise90Degrees;
// We need to rotate the photo wrt sensor orientation
enc.bitmapTransform.rotation = orientationToRotation(sensor.getCurrentOrientation());
return enc.flushAsync();
})
.then(function() {
return tempCapturedFile.openAsync(Windows.Storage.FileAccessMode.readWrite);
})
.then(function(fileStream) {
return Windows.Storage.Streams.RandomAccessStream.copyAsync(imgStream, fileStream);
return Windows.Storage.Streams.RandomAccessStream.copyAsync(finalStream, fileStream);
})
.done(function() {
imgStream.close();
photoStream.close();
finalStream.close();
complete(tempCapturedFile);
}, function() {
imgStream.close();
photoStream.close();
finalStream.close();
throw new Error("An error has occured while capturing the photo.");
});
})
});
})
.done(function(capturedFile) {
destroyCameraPreview();
// success callback for capture operation
var success = function(capturedPhoto) {
if (destinationType == Camera.DestinationType.FILE_URI || destinationType == Camera.DestinationType.NATIVE_URI) {
if (targetHeight > 0 && targetWidth > 0) {
resizeImage(successCallback, errorCallback, capturedPhoto, targetWidth, targetHeight, encodingType);
} else {
capturedPhoto.copyAsync(Windows.Storage.ApplicationData.current.localFolder, capturedPhoto.name, generateUniqueCollisionOption).done(function(copiedfile) {
successCallback("ms-appdata:///local/" + copiedfile.name);
}, errorCallback);
}
} else {
if (targetHeight > 0 && targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, capturedPhoto, targetWidth, targetHeight);
} else {
Windows.Storage.FileIO.readBufferAsync(capturedPhoto).done(function(buffer) {
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
capturedPhoto.deleteAsync().done(function() {
successCallback(strBase64);
}, function(err) {
errorCallback(err);
});
}, errorCallback);
}
}
};
if (!saveToPhotoAlbum) {
success(capturedFile);
return;
}
/*
Need to add and remove an event listener to catch activation state
Using FileSavePicker will suspend the app and it's required to catch the pickSaveFileContinuation
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
*/
var cameraActivationHandler = function(eventArgs) {
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickSaveFileContinuation) {
var file = eventArgs.file;
if (file) {
// Prevent updates to the remote version of the file until we're done
Windows.Storage.CachedFileManager.deferUpdates(file);
capturedFile.moveAndReplaceAsync(file)
.then(function() {
// Let Windows know that we're finished changing the file so
// the other app can update the remote version of the file.
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
})
.done(function(updateStatus) {
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
success(capturedFile);
} else {
errorCallback();
}
}, errorCallback);
} else {
errorCallback();
}
Windows.UI.WebUI.WebUIApplication.removeEventListener("activated", cameraActivationHandler);
}
};
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
if (encodingType === Camera.EncodingType.PNG) {
savePicker.fileTypeChoices.insert("PNG", [".png"]);
} else {
savePicker.fileTypeChoices.insert("JPEG", [".jpg"]);
}
savePicker.suggestedFileName = fileName;
Windows.UI.WebUI.WebUIApplication.addEventListener("activated", cameraActivationHandler);
savePicker.pickSaveFileAndContinue();
savePhoto(capturedFile, {
destinationType: destinationType,
targetHeight: targetHeight,
targetWidth: targetWidth,
encodingType: encodingType,
saveToPhotoAlbum: saveToPhotoAlbum
}, successCallback, errorCallback);
}, function(err) {
destroyCameraPreview();
errorCallback(err);
});
};
var getAspectRatios = function (capture) {
var videoDeviceController = capture.videoDeviceController;
var photoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo).map(function (element) {
return (element.width / element.height).toFixed(1);
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
var videoAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoRecord).map(function (element) {
return (element.width / element.height).toFixed(1);
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
var videoPreviewAspectRatios = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoPreview).map(function (element) {
return (element.width / element.height).toFixed(1);
}).filter(function (element, index, array) { return (index === array.indexOf(element)); });
var allAspectRatios = [].concat(photoAspectRatios, videoAspectRatios, videoPreviewAspectRatios);
var aspectObj = allAspectRatios.reduce(function (map, item) {
if (!map[item]) {
map[item] = 0;
}
map[item]++;
return map;
}, {});
return Object.keys(aspectObj).filter(function (k) {
return aspectObj[k] === 3;
});
};
var setAspectRatio = function (capture, aspect) {
// Max photo resolution with desired aspect ratio
var videoDeviceController = capture.videoDeviceController;
var photoResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.photo)
.filter(function (elem) {
return ((elem.width / elem.height).toFixed(1) === aspect);
})
.reduce(function (prop1, prop2) {
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
});
// Max video resolution with desired aspect ratio
var videoRecordResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoRecord)
.filter(function (elem) {
return ((elem.width / elem.height).toFixed(1) === aspect);
})
.reduce(function (prop1, prop2) {
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
});
// Max video preview resolution with desired aspect ratio
var videoPreviewResolution = videoDeviceController.getAvailableMediaStreamProperties(CapMSType.videoPreview)
.filter(function (elem) {
return ((elem.width / elem.height).toFixed(1) === aspect);
})
.reduce(function (prop1, prop2) {
return (prop1.width * prop1.height) > (prop2.width * prop2.height) ? prop1 : prop2;
});
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.photo, photoResolution)
.then(function () {
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoPreview, videoPreviewResolution);
})
.then(function () {
return videoDeviceController.setMediaStreamPropertiesAsync(CapMSType.videoRecord, videoRecordResolution);
});
};
/**
* When the phone orientation change, get the event and change camera preview rotation
* @param {Object} e - SimpleOrientationSensorOrientationChangedEventArgs
*/
var onOrientationChange = function (e) {
setPreviewRotation(e.orientation);
};
/**
* Converts SimpleOrientation to a VideoRotation to remove difference between camera sensor orientation
* and video orientation
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
* @return {number} - Windows.Media.Capture.VideoRotation
*/
var orientationToRotation = function (orientation) {
// VideoRotation enumerable and BitmapRotation enumerable have the same values
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.media.capture.videorotation.aspx
// https://msdn.microsoft.com/en-us/library/windows/apps/windows.graphics.imaging.bitmaprotation.aspx
switch (orientation) {
// portrait
case Windows.Devices.Sensors.SimpleOrientation.notRotated:
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
// landscape
case Windows.Devices.Sensors.SimpleOrientation.rotated90DegreesCounterclockwise:
return Windows.Media.Capture.VideoRotation.none;
// portrait-flipped (not supported by WinPhone Apps)
case Windows.Devices.Sensors.SimpleOrientation.rotated180DegreesCounterclockwise:
// Falling back to portrait default
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
// landscape-flipped
case Windows.Devices.Sensors.SimpleOrientation.rotated270DegreesCounterclockwise:
return Windows.Media.Capture.VideoRotation.clockwise180Degrees;
// faceup & facedown
default:
// Falling back to portrait default
return Windows.Media.Capture.VideoRotation.clockwise90Degrees;
}
};
/**
* Rotates the current MediaCapture's video
* @param {number} orientation - Windows.Devices.Sensors.SimpleOrientation
*/
var setPreviewRotation = function(orientation) {
capture.setPreviewRotation(orientationToRotation(orientation));
};
try {
createCameraUI();
startCameraPreview();
@@ -518,74 +615,116 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
encodingType = args[5],
allowCrop = !!args[7],
saveToPhotoAlbum = args[9],
cameraCaptureUI = new Windows.Media.Capture.CameraCaptureUI();
WMCapture = Windows.Media.Capture,
cameraCaptureUI = new WMCapture.CameraCaptureUI();
cameraCaptureUI.photoSettings.allowCropping = allowCrop;
if (encodingType == Camera.EncodingType.PNG) {
cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.png;
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.png;
} else {
cameraCaptureUI.photoSettings.format = Windows.Media.Capture.CameraCaptureUIPhotoFormat.jpeg;
cameraCaptureUI.photoSettings.format = WMCapture.CameraCaptureUIPhotoFormat.jpeg;
}
// decide which max pixels should be supported by targetWidth or targetHeight.
if (targetWidth >= 1280 || targetHeight >= 960) {
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.large3M;
} else if (targetWidth >= 1024 || targetHeight >= 768) {
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga;
} else if (targetWidth >= 800 || targetHeight >= 600) {
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.mediumXga;
} else if (targetWidth >= 640 || targetHeight >= 480) {
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.smallVga;
} else if (targetWidth >= 320 || targetHeight >= 240) {
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.verySmallQvga;
} else {
cameraCaptureUI.photoSettings.maxResolution = Windows.Media.Capture.CameraCaptureUIMaxPhotoResolution.highestAvailable;
var maxRes = null;
var UIMaxRes = WMCapture.CameraCaptureUIMaxPhotoResolution;
switch (true) {
case (targetWidth >= 1280 || targetHeight >= 960) :
cameraCaptureUI.photoSettings.maxResolution = UIMaxRes.large3M;
break;
case (targetWidth >= 1024 || targetHeight >= 768) :
maxRes = UIMaxRes.mediumXga;
break;
case (targetWidth >= 800 || targetHeight >= 600) :
maxRes = UIMaxRes.mediumXga;
break;
case (targetWidth >= 640 || targetHeight >= 480) :
maxRes = UIMaxRes.smallVga;
break;
case (targetWidth >= 320 || targetHeight >= 240) :
maxRes = UIMaxRes.verySmallQvga;
break;
default :
maxRes = UIMaxRes.highestAvailable;
}
cameraCaptureUI.captureFileAsync(Windows.Media.Capture.CameraCaptureUIMode.photo).done(function(picture) {
cameraCaptureUI.photoSettings.maxResolution = maxRes;
cameraCaptureUI.captureFileAsync(WMCapture.CameraCaptureUIMode.photo).done(function(picture) {
if (!picture) {
errorCallback("User didn't capture a photo.");
return;
}
// save to photo album successCallback
var success = function() {
if (destinationType == Camera.DestinationType.FILE_URI) {
if (targetHeight > 0 && targetWidth > 0) {
resizeImage(successCallback, errorCallback, picture, targetWidth, targetHeight, encodingType);
} else {
var storageFolder = Windows.Storage.ApplicationData.current.localFolder;
picture.copyAsync(storageFolder, picture.name, Windows.Storage.NameCollisionOption.replaceExisting).done(function(storageFile) {
successCallback("ms-appdata:///local/" + storageFile.name);
}, errorCallback);
}
savePhoto(picture, {
destinationType: destinationType,
targetHeight: targetHeight,
targetWidth: targetWidth,
encodingType: encodingType,
saveToPhotoAlbum: saveToPhotoAlbum
}, successCallback, errorCallback);
}, function() {
errorCallback("Fail to capture a photo.");
});
}
function savePhoto(picture, options, successCallback, errorCallback) {
// success callback for capture operation
var success = function(picture) {
if (options.destinationType == Camera.DestinationType.FILE_URI || options.destinationType == Camera.DestinationType.NATIVE_URI) {
if (options.targetHeight > 0 && options.targetWidth > 0) {
resizeImage(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight, options.encodingType);
} else {
if (targetHeight > 0 && targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, picture, targetWidth, targetHeight);
} else {
Windows.Storage.FileIO.readBufferAsync(picture).done(function(buffer) {
var strBase64 = Windows.Security.Cryptography.CryptographicBuffer.encodeToBase64String(buffer);
picture.copyAsync(getAppData().localFolder, picture.name, OptUnique).done(function (copiedFile) {
successCallback("ms-appdata:///local/" + copiedFile.name);
},errorCallback);
}
} else {
if (options.targetHeight > 0 && options.targetWidth > 0) {
resizeImageBase64(successCallback, errorCallback, picture, options.targetWidth, options.targetHeight);
} else {
fileIO.readBufferAsync(picture).done(function(buffer) {
var strBase64 =encodeToBase64String(buffer);
picture.deleteAsync().done(function() {
successCallback(strBase64);
}, function(err) {
errorCallback(err);
});
}, errorCallback);
}
}
};
if (!options.saveToPhotoAlbum) {
success(picture);
return;
} else {
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
var saveFile = function(file) {
if (file) {
// Prevent updates to the remote version of the file until we're done
Windows.Storage.CachedFileManager.deferUpdates(file);
picture.moveAndReplaceAsync(file)
.then(function() {
// Let Windows know that we're finished changing the file so
// the other app can update the remote version of the file.
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
})
.done(function(updateStatus) {
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
success(picture);
} else {
errorCallback("File update status is not complete.");
}
}, errorCallback);
}
} else {
errorCallback("Failed to select a file.");
}
};
savePicker.suggestedStartLocation = pickerLocId.picturesLibrary;
// save to photo album errorCallback
var fail = function() {
//errorCallback("FileError, code:" + fileError.code);
errorCallback("Save fail.");
};
if (!saveToPhotoAlbum) {
success();
return;
}
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.suggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.picturesLibrary;
if (encodingType === Camera.EncodingType.PNG) {
if (options.encodingType === Camera.EncodingType.PNG) {
savePicker.fileTypeChoices.insert("PNG", [".png"]);
savePicker.suggestedFileName = "photo.png";
} else {
@@ -593,31 +732,27 @@ function takePictureFromCameraWindows(successCallback, errorCallback, args) {
savePicker.suggestedFileName = "photo.jpg";
}
savePicker.pickSaveFileAsync()
.done(function(file) {
if (file) {
// Prevent updates to the remote version of the file until we're done
Windows.Storage.CachedFileManager.deferUpdates(file);
picture.moveAndReplaceAsync(file)
.then(function() {
// Let Windows know that we're finished changing the file so
// the other app can update the remote version of the file.
return Windows.Storage.CachedFileManager.completeUpdatesAsync(file);
})
.done(function(updateStatus) {
if (updateStatus === Windows.Storage.Provider.FileUpdateStatus.complete) {
success();
} else {
fail();
}
}, fail);
} else {
fail();
// If Windows Phone 8.1 use pickSaveFileAndContinue()
if (navigator.appVersion.indexOf('Windows Phone 8.1') >= 0) {
/*
Need to add and remove an event listener to catch activation state
Using FileSavePicker will suspend the app and it's required to catch the pickSaveFileContinuation
https://msdn.microsoft.com/en-us/library/windows/apps/xaml/dn631755.aspx
*/
var fileSaveHandler = function(eventArgs) {
if (eventArgs.kind === Windows.ApplicationModel.Activation.ActivationKind.pickSaveFileContinuation) {
var file = eventArgs.file;
saveFile(file);
webUIApp.removeEventListener("activated", fileSaveHandler);
}
}, fail);
}, function() {
errorCallback("Fail to capture a photo.");
});
};
webUIApp.addEventListener("activated", fileSaveHandler);
savePicker.pickSaveFileAndContinue();
} else {
savePicker.pickSaveFileAsync()
.done(saveFile, errorCallback);
}
}
}
require("cordova/exec/proxy").add("Camera",module.exports);
@@ -65,7 +65,9 @@
// No arguments, check whether the defaults are set
args = @[];
options = [CDVPictureOptions createFromTakePictureArguments:args];
CDVInvokedUrlCommand* command = [[CDVInvokedUrlCommand alloc] initWithArguments:args callbackId:@"dummy" className:@"myclassname" methodName:@"mymethodname"];
options = [CDVPictureOptions createFromTakePictureArguments:command];
XCTAssertEqual([options.quality intValue], 50);
XCTAssertEqual(options.destinationType, (int)DestinationTypeFileUri);
@@ -99,7 +101,9 @@
popoverOptions,
@(UIImagePickerControllerCameraDeviceFront),
];
options = [CDVPictureOptions createFromTakePictureArguments:args];
command = [[CDVInvokedUrlCommand alloc] initWithArguments:args callbackId:@"dummy" className:@"myclassname" methodName:@"mymethodname"];
options = [CDVPictureOptions createFromTakePictureArguments:command];
XCTAssertEqual([options.quality intValue], 49);
XCTAssertEqual(options.destinationType, (int)DestinationTypeDataUrl);
@@ -141,7 +145,9 @@
popoverOptions,
@(UIImagePickerControllerCameraDeviceFront),
];
pictureOptions = [CDVPictureOptions createFromTakePictureArguments:args];
CDVInvokedUrlCommand* command = [[CDVInvokedUrlCommand alloc] initWithArguments:args callbackId:@"dummy" className:@"myclassname" methodName:@"mymethodname"];
pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
if ([UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType]) {
picker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
@@ -170,7 +176,9 @@
popoverOptions,
@(UIImagePickerControllerCameraDeviceFront),
];
pictureOptions = [CDVPictureOptions createFromTakePictureArguments:args];
command = [[CDVInvokedUrlCommand alloc] initWithArguments:args callbackId:@"dummy" className:@"myclassname" methodName:@"mymethodname"];
pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
if ([UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType]) {
picker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
@@ -198,7 +206,9 @@
popoverOptions,
@(UIImagePickerControllerCameraDeviceFront),
];
pictureOptions = [CDVPictureOptions createFromTakePictureArguments:args];
command = [[CDVInvokedUrlCommand alloc] initWithArguments:args callbackId:@"dummy" className:@"myclassname" methodName:@"mymethodname"];
pictureOptions = [CDVPictureOptions createFromTakePictureArguments:command];
if ([UIImagePickerController isSourceTypeAvailable:pictureOptions.sourceType]) {
picker = [CDVCameraPicker createFromPictureOptions:pictureOptions];
@@ -1,5 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
#
# 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.
#
-->
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
+1 -1
View File
@@ -22,7 +22,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:rim="http://www.blackberry.com/ns/widgets"
id="cordova-plugin-camera-tests"
version="1.0.1-dev">
version="1.1.1-dev">
<name>Cordova Camera Plugin Tests</name>
<license>Apache 2.0</license>