mirror of
https://github.com/apache/cordova-plugin-camera.git
synced 2025-01-31 12:42:50 +08:00
Merge branch 'master' of https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera
This commit is contained in:
commit
7e9f099301
16
CONTRIBUTING.md
Normal file
16
CONTRIBUTING.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Contributing to Apache Cordova
|
||||||
|
|
||||||
|
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
|
||||||
|
[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.
|
||||||
|
- Run the tests so your patch doesn't break existing functionality.
|
||||||
|
|
||||||
|
We look forward to your contributions!
|
@ -71,3 +71,12 @@
|
|||||||
|
|
||||||
### 0.2.8 (Feb 26, 2014)
|
### 0.2.8 (Feb 26, 2014)
|
||||||
* CB-1826 Catch OOM on gallery image resize
|
* CB-1826 Catch OOM on gallery image resize
|
||||||
|
|
||||||
|
### 0.2.9 (Apr 17, 2014)
|
||||||
|
* CB-6460: Update license headers
|
||||||
|
* CB-6422: [windows8] use cordova/exec/proxy
|
||||||
|
* [WP8] When only targetWidth or targetHeight is provided, use it as the only bound
|
||||||
|
* CB-4027, CB-5102, CB-2737, CB-2387: [WP] Fix camera issues, cropping, memory leaks
|
||||||
|
* CB-6212: [iOS] fix warnings compiled under arm64 64-bit
|
||||||
|
* [BlackBerry10] Add rim xml namespaces declaration
|
||||||
|
* Add NOTICE file
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
# org.apache.cordova.camera
|
# org.apache.cordova.camera
|
||||||
|
|
||||||
This plugin provides an API for taking pictures and for choosing images from
|
This plugin provides an API for taking pictures and for choosing images from
|
||||||
the system's image libarary.
|
the system's image library.
|
||||||
|
|
||||||
cordova plugin add org.apache.cordova.camera
|
cordova plugin add org.apache.cordova.camera
|
||||||
|
|
||||||
|
21
plugin.xml
21
plugin.xml
@ -1,9 +1,28 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
or more contributor license agreements. See the NOTICE file
|
||||||
|
distributed with this work for additional information
|
||||||
|
regarding copyright ownership. The ASF licenses this file
|
||||||
|
to you under the Apache License, Version 2.0 (the
|
||||||
|
"License"); you may not use this file except in compliance
|
||||||
|
with the License. You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing,
|
||||||
|
software distributed under the License is distributed on an
|
||||||
|
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
KIND, either express or implied. See the License for the
|
||||||
|
specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
-->
|
||||||
|
|
||||||
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:rim="http://www.blackberry.com/ns/widgets"
|
||||||
id="org.apache.cordova.camera"
|
id="org.apache.cordova.camera"
|
||||||
version="0.2.8">
|
version="0.2.10-dev">
|
||||||
<name>Camera</name>
|
<name>Camera</name>
|
||||||
<description>Cordova Camera Plugin</description>
|
<description>Cordova Camera Plugin</description>
|
||||||
<license>Apache 2.0</license>
|
<license>Apache 2.0</license>
|
||||||
|
@ -34,16 +34,18 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Bitmap.CompressFormat;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
import android.graphics.Bitmap.CompressFormat;
|
|
||||||
import android.media.MediaScannerConnection;
|
import android.media.MediaScannerConnection;
|
||||||
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
@ -58,7 +60,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
|
|
||||||
private static final int DATA_URL = 0; // Return base64 encoded string
|
private static final int DATA_URL = 0; // Return base64 encoded string
|
||||||
private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android)
|
private static final int FILE_URI = 1; // Return file uri (content://media/external/images/media/2 for Android)
|
||||||
private static final int NATIVE_URI = 2; // On Android, this is the same as FILE_URI
|
private static final int NATIVE_URI = 2; // On Android, this is the same as FILE_URI
|
||||||
|
|
||||||
private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
|
private static final int PHOTOLIBRARY = 0; // Choose image from picture library (same as SAVEDPHOTOALBUM for Android)
|
||||||
private static final int CAMERA = 1; // Take picture from camera
|
private static final int CAMERA = 1; // Take picture from camera
|
||||||
@ -75,6 +77,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
private static final String GET_All = "Get All";
|
private static final String GET_All = "Get All";
|
||||||
|
|
||||||
private static final String LOG_TAG = "CameraLauncher";
|
private static final String LOG_TAG = "CameraLauncher";
|
||||||
|
private static final int CROP_CAMERA = 100;
|
||||||
|
|
||||||
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
private int mQuality; // Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||||
private int targetWidth; // desired width of the image
|
private int targetWidth; // desired width of the image
|
||||||
@ -85,13 +88,14 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
|
private boolean saveToPhotoAlbum; // Should the picture be saved to the device's photo album
|
||||||
private boolean correctOrientation; // Should the pictures orientation be corrected
|
private boolean correctOrientation; // Should the pictures orientation be corrected
|
||||||
private boolean orientationCorrected; // Has the picture's orientation been corrected
|
private boolean orientationCorrected; // Has the picture's orientation been corrected
|
||||||
//private boolean allowEdit; // Should we allow the user to crop the image. UNUSED.
|
private boolean allowEdit; // Should we allow the user to crop the image.
|
||||||
|
|
||||||
public CallbackContext callbackContext;
|
public CallbackContext callbackContext;
|
||||||
private int numPics;
|
private int numPics;
|
||||||
|
|
||||||
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
private MediaScannerConnection conn; // Used to update gallery app with newly-written files
|
||||||
private Uri scanMe; // Uri of image to be added to content store
|
private Uri scanMe; // Uri of image to be added to content store
|
||||||
|
private Uri croppedUri;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the request and returns PluginResult.
|
* Executes the request and returns PluginResult.
|
||||||
@ -121,7 +125,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
this.targetHeight = args.getInt(4);
|
this.targetHeight = args.getInt(4);
|
||||||
this.encodingType = args.getInt(5);
|
this.encodingType = args.getInt(5);
|
||||||
this.mediaType = args.getInt(6);
|
this.mediaType = args.getInt(6);
|
||||||
//this.allowEdit = args.getBoolean(7); // This field is unused.
|
this.allowEdit = args.getBoolean(7);
|
||||||
this.correctOrientation = args.getBoolean(8);
|
this.correctOrientation = args.getBoolean(8);
|
||||||
this.saveToPhotoAlbum = args.getBoolean(9);
|
this.saveToPhotoAlbum = args.getBoolean(9);
|
||||||
|
|
||||||
@ -139,7 +143,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
this.takePicture(destType, encodingType);
|
this.takePicture(destType, encodingType);
|
||||||
}
|
}
|
||||||
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
|
||||||
this.getImage(srcType, destType);
|
this.getImage(srcType, destType, encodingType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e)
|
catch (IllegalArgumentException e)
|
||||||
@ -238,33 +242,93 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
* @param quality Compression quality hint (0-100: 0=low quality & high compression, 100=compress of max quality)
|
||||||
* @param srcType The album to get image from.
|
* @param srcType The album to get image from.
|
||||||
* @param returnType Set the type of image to return.
|
* @param returnType Set the type of image to return.
|
||||||
|
* @param encodingType
|
||||||
*/
|
*/
|
||||||
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
|
// TODO: Images selected from SDCARD don't display correctly, but from CAMERA ALBUM do!
|
||||||
public void getImage(int srcType, int returnType) {
|
// TODO: Images from kitkat filechooser not going into crop function
|
||||||
|
public void getImage(int srcType, int returnType, int encodingType) {
|
||||||
Intent intent = new Intent();
|
Intent intent = new Intent();
|
||||||
String title = GET_PICTURE;
|
String title = GET_PICTURE;
|
||||||
|
croppedUri = null;
|
||||||
if (this.mediaType == PICTURE) {
|
if (this.mediaType == PICTURE) {
|
||||||
intent.setType("image/*");
|
intent.setType("image/*");
|
||||||
|
if (this.allowEdit) {
|
||||||
|
intent.setAction(Intent.ACTION_PICK);
|
||||||
|
intent.putExtra("crop", "true");
|
||||||
|
if (targetWidth > 0) {
|
||||||
|
intent.putExtra("outputX", targetWidth);
|
||||||
|
}
|
||||||
|
if (targetHeight > 0) {
|
||||||
|
intent.putExtra("outputY", targetHeight);
|
||||||
|
}
|
||||||
|
if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
|
||||||
|
intent.putExtra("aspectX", 1);
|
||||||
|
intent.putExtra("aspectY", 1);
|
||||||
|
}
|
||||||
|
File photo = createCaptureFile(encodingType);
|
||||||
|
croppedUri = Uri.fromFile(photo);
|
||||||
|
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, croppedUri);
|
||||||
|
} else {
|
||||||
|
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
}
|
||||||
|
} else if (this.mediaType == VIDEO) {
|
||||||
|
intent.setType("video/*");
|
||||||
|
title = GET_VIDEO;
|
||||||
|
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
|
} else if (this.mediaType == ALLMEDIA) {
|
||||||
|
// I wanted to make the type 'image/*, video/*' but this does not work on all versions
|
||||||
|
// of android so I had to go with the wildcard search.
|
||||||
|
intent.setType("*/*");
|
||||||
|
title = GET_All;
|
||||||
|
intent.setAction(Intent.ACTION_GET_CONTENT);
|
||||||
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||||
}
|
}
|
||||||
else if (this.mediaType == VIDEO) {
|
|
||||||
intent.setType("video/*");
|
|
||||||
title = GET_VIDEO;
|
|
||||||
}
|
|
||||||
else if (this.mediaType == ALLMEDIA) {
|
|
||||||
// I wanted to make the type 'image/*, video/*' but this does not work on all versions
|
|
||||||
// of android so I had to go with the wildcard search.
|
|
||||||
intent.setType("*/*");
|
|
||||||
title = GET_All;
|
|
||||||
}
|
|
||||||
|
|
||||||
intent.setAction(Intent.ACTION_GET_CONTENT);
|
|
||||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
||||||
if (this.cordova != null) {
|
if (this.cordova != null) {
|
||||||
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
|
||||||
new String(title)), (srcType + 1) * 16 + returnType + 1);
|
new String(title)), (srcType + 1) * 16 + returnType + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Brings up the UI to perform crop on passed image URI
|
||||||
|
*
|
||||||
|
* @param picUri
|
||||||
|
*/
|
||||||
|
private void performCrop(Uri picUri) {
|
||||||
|
try {
|
||||||
|
Intent cropIntent = new Intent("com.android.camera.action.CROP");
|
||||||
|
// indicate image type and Uri
|
||||||
|
cropIntent.setDataAndType(picUri, "image/*");
|
||||||
|
// set crop properties
|
||||||
|
cropIntent.putExtra("crop", "true");
|
||||||
|
// indicate output X and Y
|
||||||
|
if (targetWidth > 0) {
|
||||||
|
cropIntent.putExtra("outputX", targetWidth);
|
||||||
|
}
|
||||||
|
if (targetHeight > 0) {
|
||||||
|
cropIntent.putExtra("outputY", targetHeight);
|
||||||
|
}
|
||||||
|
if (targetHeight > 0 && targetWidth > 0 && targetWidth == targetHeight) {
|
||||||
|
cropIntent.putExtra("aspectX", 1);
|
||||||
|
cropIntent.putExtra("aspectY", 1);
|
||||||
|
}
|
||||||
|
// retrieve data on return
|
||||||
|
cropIntent.putExtra("return-data", true);
|
||||||
|
// start the activity - we handle returning in onActivityResult
|
||||||
|
|
||||||
|
if (this.cordova != null) {
|
||||||
|
this.cordova.startActivityForResult((CordovaPlugin) this,
|
||||||
|
cropIntent, CROP_CAMERA);
|
||||||
|
}
|
||||||
|
} catch (ActivityNotFoundException anfe) {
|
||||||
|
Log.e(LOG_TAG, "Crop operation not supported on this device");
|
||||||
|
// Send Uri back to JavaScript for viewing image
|
||||||
|
this.callbackContext.success(picUri.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies all needed transformation to the image received from the camera.
|
* Applies all needed transformation to the image received from the camera.
|
||||||
*
|
*
|
||||||
@ -355,7 +419,12 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
exif.createOutFile(exifPath);
|
exif.createOutFile(exifPath);
|
||||||
exif.writeExifData();
|
exif.writeExifData();
|
||||||
}
|
}
|
||||||
|
if (this.allowEdit) {
|
||||||
|
performCrop(uri);
|
||||||
|
} else {
|
||||||
|
// Send Uri back to JavaScript for viewing image
|
||||||
|
this.callbackContext.success(uri.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Send Uri back to JavaScript for viewing image
|
// Send Uri back to JavaScript for viewing image
|
||||||
this.callbackContext.success(uri.toString());
|
this.callbackContext.success(uri.toString());
|
||||||
@ -365,7 +434,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
bitmap = null;
|
bitmap = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
|
||||||
// Create an ExifHelper to save the exif data that is lost during compression
|
// Create an ExifHelper to save the exif data that is lost during compression
|
||||||
String modifiedPath = getTempDirectoryPath() + "/modified.jpg";
|
String modifiedPath = getTempDirectoryPath() + "/modified.jpg";
|
||||||
|
|
||||||
@ -392,7 +461,7 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
return modifiedPath;
|
return modifiedPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies all needed transformation to the image received from the gallery.
|
* Applies all needed transformation to the image received from the gallery.
|
||||||
*
|
*
|
||||||
* @param destType In which form should we return the image
|
* @param destType In which form should we return the image
|
||||||
@ -400,6 +469,14 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
*/
|
*/
|
||||||
private void processResultFromGallery(int destType, Intent intent) {
|
private void processResultFromGallery(int destType, Intent intent) {
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
|
if (uri == null) {
|
||||||
|
if (croppedUri != null) {
|
||||||
|
uri = croppedUri;
|
||||||
|
} else {
|
||||||
|
this.failPicture("null data from photo library");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
int rotate = 0;
|
int rotate = 0;
|
||||||
|
|
||||||
// If you ask for video or all media type you will automatically get back a file URI
|
// If you ask for video or all media type you will automatically get back a file URI
|
||||||
@ -495,7 +572,50 @@ public class CameraLauncher extends CordovaPlugin implements MediaScannerConnect
|
|||||||
// Get src and dest types from request code
|
// Get src and dest types from request code
|
||||||
int srcType = (requestCode / 16) - 1;
|
int srcType = (requestCode / 16) - 1;
|
||||||
int destType = (requestCode % 16) - 1;
|
int destType = (requestCode % 16) - 1;
|
||||||
|
// if camera crop
|
||||||
|
if (requestCode == CROP_CAMERA) {
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
// // get the returned data
|
||||||
|
Bundle extras = intent.getExtras();
|
||||||
|
// get the cropped bitmap
|
||||||
|
Bitmap thePic = extras.getParcelable("data");
|
||||||
|
if (thePic == null) {
|
||||||
|
this.failPicture("Crop returned no data.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// now save the bitmap to a file
|
||||||
|
OutputStream fOut = null;
|
||||||
|
File temp_file = new File(getTempDirectoryPath(),
|
||||||
|
System.currentTimeMillis() + ".jpg");
|
||||||
|
try {
|
||||||
|
temp_file.createNewFile();
|
||||||
|
fOut = new FileOutputStream(temp_file);
|
||||||
|
thePic.compress(Bitmap.CompressFormat.JPEG, this.mQuality,
|
||||||
|
fOut);
|
||||||
|
fOut.flush();
|
||||||
|
fOut.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Send Uri back to JavaScript for viewing image
|
||||||
|
this.callbackContext
|
||||||
|
.success(Uri.fromFile(temp_file).toString());
|
||||||
|
|
||||||
|
}// If cancelled
|
||||||
|
else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
|
this.failPicture("Camera cancelled.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// If something else
|
||||||
|
else {
|
||||||
|
this.failPicture("Did not complete!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// If CAMERA
|
// If CAMERA
|
||||||
if (srcType == CAMERA) {
|
if (srcType == CAMERA) {
|
||||||
// If image available
|
// If image available
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#import <Cordova/NSDictionary+Extensions.h>
|
#import <Cordova/NSDictionary+Extensions.h>
|
||||||
#import <ImageIO/CGImageProperties.h>
|
#import <ImageIO/CGImageProperties.h>
|
||||||
#import <AssetsLibrary/ALAssetRepresentation.h>
|
#import <AssetsLibrary/ALAssetRepresentation.h>
|
||||||
|
#import <AssetsLibrary/AssetsLibrary.h>
|
||||||
#import <ImageIO/CGImageSource.h>
|
#import <ImageIO/CGImageSource.h>
|
||||||
#import <ImageIO/CGImageProperties.h>
|
#import <ImageIO/CGImageProperties.h>
|
||||||
#import <ImageIO/CGImageDestination.h>
|
#import <ImageIO/CGImageDestination.h>
|
||||||
@ -84,7 +85,7 @@ static NSSet* org_apache_cordova_validArrowDirections;
|
|||||||
|
|
||||||
bool hasCamera = [UIImagePickerController isSourceTypeAvailable:sourceType];
|
bool hasCamera = [UIImagePickerController isSourceTypeAvailable:sourceType];
|
||||||
if (!hasCamera) {
|
if (!hasCamera) {
|
||||||
NSLog(@"Camera.getPicture: source type %d not available.", sourceType);
|
NSLog(@"Camera.getPicture: source type %lu not available.", (unsigned long)sourceType);
|
||||||
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no camera available"];
|
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no camera available"];
|
||||||
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
|
[self.commandDelegate sendPluginResult:result callbackId:callbackId];
|
||||||
return;
|
return;
|
||||||
@ -170,10 +171,10 @@ static NSSet* org_apache_cordova_validArrowDirections;
|
|||||||
|
|
||||||
- (void)displayPopover:(NSDictionary*)options
|
- (void)displayPopover:(NSDictionary*)options
|
||||||
{
|
{
|
||||||
int x = 0;
|
NSInteger x = 0;
|
||||||
int y = 32;
|
NSInteger y = 32;
|
||||||
int width = 320;
|
NSInteger width = 320;
|
||||||
int height = 480;
|
NSInteger height = 480;
|
||||||
UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
|
UIPopoverArrowDirection arrowDirection = UIPopoverArrowDirectionAny;
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
@ -182,7 +183,7 @@ static NSSet* org_apache_cordova_validArrowDirections;
|
|||||||
width = [options integerValueForKey:@"width" defaultValue:320];
|
width = [options integerValueForKey:@"width" defaultValue:320];
|
||||||
height = [options integerValueForKey:@"height" defaultValue:480];
|
height = [options integerValueForKey:@"height" defaultValue:480];
|
||||||
arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny];
|
arrowDirection = [options integerValueForKey:@"arrowDir" defaultValue:UIPopoverArrowDirectionAny];
|
||||||
if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithInt:arrowDirection]]) {
|
if (![org_apache_cordova_validArrowDirections containsObject:[NSNumber numberWithUnsignedInteger:arrowDirection]]) {
|
||||||
arrowDirection = UIPopoverArrowDirectionAny;
|
arrowDirection = UIPopoverArrowDirectionAny;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -392,7 +393,13 @@ static NSSet* org_apache_cordova_validArrowDirections;
|
|||||||
}
|
}
|
||||||
// popoverControllerDidDismissPopover:(id)popoverController is called if popover is cancelled
|
// popoverControllerDidDismissPopover:(id)popoverController is called if popover is cancelled
|
||||||
|
|
||||||
CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"]; // error callback expects string ATM
|
CDVPluginResult* result;
|
||||||
|
if ([ALAssetsLibrary authorizationStatus] == ALAuthorizationStatusAuthorized) {
|
||||||
|
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"no image selected"]; // error callback expects string ATM
|
||||||
|
} else {
|
||||||
|
result = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:@"has no access to assets"]; // error callback expects string ATM
|
||||||
|
}
|
||||||
|
|
||||||
[self.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
|
[self.commandDelegate sendPluginResult:result callbackId:cameraPicker.callbackId];
|
||||||
|
|
||||||
self.hasPendingOperation = NO;
|
self.hasPendingOperation = NO;
|
||||||
|
@ -190,7 +190,7 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
|
|||||||
// construct the complete app1 data block
|
// construct the complete app1 data block
|
||||||
app1 = [[NSMutableString alloc] initWithFormat: @"%@%04x%@%@%@%@%@",
|
app1 = [[NSMutableString alloc] initWithFormat: @"%@%04x%@%@%@%@%@",
|
||||||
app1marker,
|
app1marker,
|
||||||
16 + ([exifIFD length]/2) + ([subExifIFD length]/2) /*16+[exifIFD length]/2*/,
|
(unsigned int)(16 + ([exifIFD length]/2) + ([subExifIFD length]/2)) /*16+[exifIFD length]/2*/,
|
||||||
exifmarker,
|
exifmarker,
|
||||||
tiffheader,
|
tiffheader,
|
||||||
ifd0offset,
|
ifd0offset,
|
||||||
@ -268,10 +268,10 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
|
|||||||
}
|
}
|
||||||
|
|
||||||
// calculate IFD0 terminal offset tags, currently ExifSubIFD
|
// calculate IFD0 terminal offset tags, currently ExifSubIFD
|
||||||
int entrycount = [ifdblock count];
|
unsigned int entrycount = (unsigned int)[ifdblock count];
|
||||||
if (ifd0flag) {
|
if (ifd0flag) {
|
||||||
// 18 accounts for 8769's width + offset to next ifd, 8 accounts for start of header
|
// 18 accounts for 8769's width + offset to next ifd, 8 accounts for start of header
|
||||||
NSNumber * offset = [NSNumber numberWithInt:[exifstr length] / 2 + [dbstr length] / 2 + 18+8];
|
NSNumber * offset = [NSNumber numberWithUnsignedInteger:[exifstr length] / 2 + [dbstr length] / 2 + 18+8];
|
||||||
|
|
||||||
[self appendExifOffsetTagTo: exifstr
|
[self appendExifOffsetTagTo: exifstr
|
||||||
withOffset : offset];
|
withOffset : offset];
|
||||||
@ -293,7 +293,7 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
|
|||||||
NSNumber * dataformat = [formtemplate objectAtIndex:1];
|
NSNumber * dataformat = [formtemplate objectAtIndex:1];
|
||||||
NSNumber * components = [formtemplate objectAtIndex:2];
|
NSNumber * components = [formtemplate objectAtIndex:2];
|
||||||
if([components intValue] == 0) {
|
if([components intValue] == 0) {
|
||||||
components = [NSNumber numberWithInt: [data length] * DataTypeToWidth[[dataformat intValue]-1]];
|
components = [NSNumber numberWithUnsignedInteger:[data length] * DataTypeToWidth[[dataformat intValue]-1]];
|
||||||
}
|
}
|
||||||
|
|
||||||
return [[NSString alloc] initWithFormat: @"%@%@%08x",
|
return [[NSString alloc] initWithFormat: @"%@%@%08x",
|
||||||
@ -344,7 +344,7 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
|
|||||||
[datastr appendString:[dataformat objectAtIndex:3]];
|
[datastr appendString:[dataformat objectAtIndex:3]];
|
||||||
}
|
}
|
||||||
if ([datastr length] < 8) {
|
if ([datastr length] < 8) {
|
||||||
NSString * format = [NSString stringWithFormat:@"%%0%dd", 8 - [datastr length]];
|
NSString * format = [NSString stringWithFormat:@"%%0%dd", (int)(8 - [datastr length])];
|
||||||
[datastr appendFormat:format,0];
|
[datastr appendFormat:format,0];
|
||||||
}
|
}
|
||||||
return datastr;
|
return datastr;
|
||||||
@ -464,7 +464,7 @@ const uint mTiffLength = 0x2a; // after byte align bits, next to bits are 0x002a
|
|||||||
-(void) expandContinuedFraction: (NSArray*) fractionlist
|
-(void) expandContinuedFraction: (NSArray*) fractionlist
|
||||||
withResultNumerator: (NSNumber**) numerator
|
withResultNumerator: (NSNumber**) numerator
|
||||||
withResultDenominator: (NSNumber**) denominator {
|
withResultDenominator: (NSNumber**) denominator {
|
||||||
int i = 0;
|
NSUInteger i = 0;
|
||||||
int den = 0;
|
int den = 0;
|
||||||
int num = 0;
|
int num = 0;
|
||||||
if ([fractionlist count] == 1) {
|
if ([fractionlist count] == 1) {
|
||||||
|
@ -351,4 +351,4 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
require("cordova/windows8/commandProxy").add("Camera",module.exports);
|
require("cordova/exec/proxy").add("Camera",module.exports);
|
||||||
|
383
src/wp/Camera.cs
383
src/wp/Camera.cs
@ -113,8 +113,6 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
[DataMember(IsRequired = false, Name = "correctOrientation")]
|
[DataMember(IsRequired = false, Name = "correctOrientation")]
|
||||||
public bool CorrectOrientation { get; set; }
|
public bool CorrectOrientation { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ignored
|
/// Ignored
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -176,16 +174,6 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to open photo library
|
|
||||||
/// </summary>
|
|
||||||
PhotoChooserTask photoChooserTask;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to open camera application
|
|
||||||
/// </summary>
|
|
||||||
CameraCaptureTask cameraTask;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Camera options
|
/// Camera options
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -198,54 +186,70 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
|
string[] args = JSON.JsonHelper.Deserialize<string[]>(options);
|
||||||
// ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
|
// ["quality", "destinationType", "sourceType", "targetWidth", "targetHeight", "encodingType",
|
||||||
// "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
|
// "mediaType", "allowEdit", "correctOrientation", "saveToPhotoAlbum" ]
|
||||||
this.cameraOptions = new CameraOptions();
|
cameraOptions = new CameraOptions();
|
||||||
this.cameraOptions.Quality = int.Parse(args[0]);
|
cameraOptions.Quality = int.Parse(args[0]);
|
||||||
this.cameraOptions.DestinationType = int.Parse(args[1]);
|
cameraOptions.DestinationType = int.Parse(args[1]);
|
||||||
this.cameraOptions.PictureSourceType = int.Parse(args[2]);
|
cameraOptions.PictureSourceType = int.Parse(args[2]);
|
||||||
this.cameraOptions.TargetWidth = int.Parse(args[3]);
|
cameraOptions.TargetWidth = int.Parse(args[3]);
|
||||||
this.cameraOptions.TargetHeight = int.Parse(args[4]);
|
cameraOptions.TargetHeight = int.Parse(args[4]);
|
||||||
this.cameraOptions.EncodingType = int.Parse(args[5]);
|
cameraOptions.EncodingType = int.Parse(args[5]);
|
||||||
this.cameraOptions.MediaType = int.Parse(args[6]);
|
cameraOptions.MediaType = int.Parse(args[6]);
|
||||||
this.cameraOptions.AllowEdit = bool.Parse(args[7]);
|
cameraOptions.AllowEdit = bool.Parse(args[7]);
|
||||||
this.cameraOptions.CorrectOrientation = bool.Parse(args[8]);
|
cameraOptions.CorrectOrientation = bool.Parse(args[8]);
|
||||||
this.cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
|
cameraOptions.SaveToPhotoAlbum = bool.Parse(args[9]);
|
||||||
|
|
||||||
//this.cameraOptions = String.IsNullOrEmpty(options) ?
|
// a very large number will force the other value to be the bound
|
||||||
// new CameraOptions() : JSON.JsonHelper.Deserialize<CameraOptions>(options);
|
if (cameraOptions.TargetWidth > -1 && cameraOptions.TargetHeight == -1)
|
||||||
|
{
|
||||||
|
cameraOptions.TargetHeight = 100000;
|
||||||
|
}
|
||||||
|
else if (cameraOptions.TargetHeight > -1 && cameraOptions.TargetWidth == -1)
|
||||||
|
{
|
||||||
|
cameraOptions.TargetWidth = 100000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
this.DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, ex.Message));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO Check if all the options are acceptable
|
if(cameraOptions.DestinationType != Camera.FILE_URI && cameraOptions.DestinationType != Camera.DATA_URL )
|
||||||
|
{
|
||||||
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ChooserBase<PhotoResult> chooserTask = null;
|
||||||
if (cameraOptions.PictureSourceType == CAMERA)
|
if (cameraOptions.PictureSourceType == CAMERA)
|
||||||
{
|
{
|
||||||
cameraTask = new CameraCaptureTask();
|
chooserTask = new CameraCaptureTask();
|
||||||
cameraTask.Completed += onCameraTaskCompleted;
|
}
|
||||||
cameraTask.Show();
|
else if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
|
||||||
|
{
|
||||||
|
chooserTask = new PhotoChooserTask();
|
||||||
|
}
|
||||||
|
// if chooserTask is still null, then PictureSourceType was invalid
|
||||||
|
if (chooserTask != null)
|
||||||
|
{
|
||||||
|
chooserTask.Completed += onTaskCompleted;
|
||||||
|
chooserTask.Show();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if ((cameraOptions.PictureSourceType == PHOTOLIBRARY) || (cameraOptions.PictureSourceType == SAVEDPHOTOALBUM))
|
Debug.WriteLine("Unrecognized PictureSourceType :: " + cameraOptions.PictureSourceType.ToString());
|
||||||
{
|
DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
|
||||||
photoChooserTask = new PhotoChooserTask();
|
|
||||||
photoChooserTask.Completed += onPickerTaskCompleted;
|
|
||||||
photoChooserTask.Show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onCameraTaskCompleted(object sender, PhotoResult e)
|
public void onTaskCompleted(object sender, PhotoResult e)
|
||||||
{
|
{
|
||||||
|
var task = sender as ChooserBase<PhotoResult>;
|
||||||
|
if (task != null)
|
||||||
|
{
|
||||||
|
task.Completed -= onTaskCompleted;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.Error != null)
|
if (e.Error != null)
|
||||||
{
|
{
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
||||||
@ -259,119 +263,69 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
{
|
{
|
||||||
string imagePathOrContent = string.Empty;
|
string imagePathOrContent = string.Empty;
|
||||||
|
|
||||||
if (cameraOptions.DestinationType == FILE_URI)
|
// Save image back to media library
|
||||||
|
// only save to photoalbum if it didn't come from there ...
|
||||||
|
if (cameraOptions.PictureSourceType == CAMERA && cameraOptions.SaveToPhotoAlbum)
|
||||||
{
|
{
|
||||||
// Save image in media library
|
MediaLibrary library = new MediaLibrary();
|
||||||
if (cameraOptions.SaveToPhotoAlbum)
|
Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
|
||||||
{
|
|
||||||
MediaLibrary library = new MediaLibrary();
|
|
||||||
Picture pict = library.SavePicture(e.OriginalFileName, e.ChosenPhoto); // to save to photo-roll ...
|
|
||||||
}
|
|
||||||
|
|
||||||
int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
|
|
||||||
int newAngle = 0;
|
|
||||||
switch (orient)
|
|
||||||
{
|
|
||||||
case ImageExifOrientation.LandscapeLeft:
|
|
||||||
newAngle = 90;
|
|
||||||
break;
|
|
||||||
case ImageExifOrientation.PortraitUpsideDown:
|
|
||||||
newAngle = 180;
|
|
||||||
break;
|
|
||||||
case ImageExifOrientation.LandscapeRight:
|
|
||||||
newAngle = 270;
|
|
||||||
break;
|
|
||||||
case ImageExifOrientation.Portrait:
|
|
||||||
default: break; // 0 default already set
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle);
|
|
||||||
|
|
||||||
// we should return stream position back after saving stream to media library
|
|
||||||
rotImageStream.Seek(0, SeekOrigin.Begin);
|
|
||||||
|
|
||||||
WriteableBitmap image = PictureDecoder.DecodeJpeg(rotImageStream);
|
|
||||||
|
|
||||||
imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (cameraOptions.DestinationType == DATA_URL)
|
|
||||||
|
int orient = ImageExifHelper.getImageOrientationFromStream(e.ChosenPhoto);
|
||||||
|
int newAngle = 0;
|
||||||
|
switch (orient)
|
||||||
{
|
{
|
||||||
imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
|
case ImageExifOrientation.LandscapeLeft:
|
||||||
|
newAngle = 90;
|
||||||
|
break;
|
||||||
|
case ImageExifOrientation.PortraitUpsideDown:
|
||||||
|
newAngle = 180;
|
||||||
|
break;
|
||||||
|
case ImageExifOrientation.LandscapeRight:
|
||||||
|
newAngle = 270;
|
||||||
|
break;
|
||||||
|
case ImageExifOrientation.Portrait:
|
||||||
|
default: break; // 0 default already set
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (newAngle != 0)
|
||||||
{
|
{
|
||||||
// TODO: shouldn't this happen before we launch the camera-picker?
|
using (Stream rotImageStream = ImageExifHelper.RotateStream(e.ChosenPhoto, newAngle))
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
|
{
|
||||||
return;
|
// we should reset stream position after saving stream to media library
|
||||||
|
rotImageStream.Seek(0, SeekOrigin.Begin);
|
||||||
|
if (cameraOptions.DestinationType == DATA_URL)
|
||||||
|
{
|
||||||
|
imagePathOrContent = GetImageContent(rotImageStream);
|
||||||
|
}
|
||||||
|
else // FILE_URL
|
||||||
|
{
|
||||||
|
imagePathOrContent = SaveImageToLocalStorage(rotImageStream, Path.GetFileName(e.OriginalFileName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else // no need to reorient
|
||||||
|
{
|
||||||
|
if (cameraOptions.DestinationType == DATA_URL)
|
||||||
|
{
|
||||||
|
imagePathOrContent = GetImageContent(e.ChosenPhoto);
|
||||||
|
}
|
||||||
|
else // FILE_URL
|
||||||
|
{
|
||||||
|
imagePathOrContent = SaveImageToLocalStorage(e.ChosenPhoto, Path.GetFileName(e.OriginalFileName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TaskResult.Cancel:
|
case TaskResult.Cancel:
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onPickerTaskCompleted(object sender, PhotoResult e)
|
|
||||||
{
|
|
||||||
if (e.Error != null)
|
|
||||||
{
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (e.TaskResult)
|
|
||||||
{
|
|
||||||
case TaskResult.OK:
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string imagePathOrContent = string.Empty;
|
|
||||||
|
|
||||||
if (cameraOptions.DestinationType == FILE_URI)
|
|
||||||
{
|
|
||||||
WriteableBitmap image = PictureDecoder.DecodeJpeg(e.ChosenPhoto);
|
|
||||||
imagePathOrContent = this.SaveImageToLocalStorage(image, Path.GetFileName(e.OriginalFileName));
|
|
||||||
}
|
|
||||||
else if (cameraOptions.DestinationType == DATA_URL)
|
|
||||||
{
|
|
||||||
imagePathOrContent = this.GetImageContent(e.ChosenPhoto);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: shouldn't this happen before we launch the camera-picker?
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Incorrect option: destinationType"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.OK, imagePathOrContent));
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Error retrieving image."));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TaskResult.Cancel:
|
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection cancelled."));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
|
DispatchCommandResult(new PluginResult(PluginResult.Status.ERROR, "Selection did not complete!"));
|
||||||
break;
|
break;
|
||||||
@ -385,23 +339,29 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
/// <returns>Base64 representation of the image</returns>
|
/// <returns>Base64 representation of the image</returns>
|
||||||
private string GetImageContent(Stream stream)
|
private string GetImageContent(Stream stream)
|
||||||
{
|
{
|
||||||
int streamLength = (int)stream.Length;
|
byte[] imageContent = null;
|
||||||
byte[] fileData = new byte[streamLength + 1];
|
|
||||||
stream.Read(fileData, 0, streamLength);
|
|
||||||
|
|
||||||
//use photo's actual width & height if user doesn't provide width & height
|
try
|
||||||
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
|
||||||
{
|
{
|
||||||
stream.Close();
|
//use photo's actual width & height if user doesn't provide width & height
|
||||||
return Convert.ToBase64String(fileData);
|
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
||||||
|
{
|
||||||
|
int streamLength = (int)stream.Length;
|
||||||
|
imageContent = new byte[streamLength + 1];
|
||||||
|
stream.Read(imageContent, 0, streamLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// resize photo
|
||||||
|
imageContent = ResizePhoto(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
// resize photo
|
stream.Dispose();
|
||||||
byte[] resizedFile = ResizePhoto(stream, fileData);
|
|
||||||
stream.Close();
|
|
||||||
return Convert.ToBase64String(resizedFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Convert.ToBase64String(imageContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -410,51 +370,87 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
/// <param name="stream">Image stream</param>
|
/// <param name="stream">Image stream</param>
|
||||||
/// <param name="fileData">File data</param>
|
/// <param name="fileData">File data</param>
|
||||||
/// <returns>resized image</returns>
|
/// <returns>resized image</returns>
|
||||||
private byte[] ResizePhoto(Stream stream, byte[] fileData)
|
private byte[] ResizePhoto(Stream stream)
|
||||||
{
|
{
|
||||||
int streamLength = (int)stream.Length;
|
//output
|
||||||
int intResult = 0;
|
|
||||||
|
|
||||||
byte[] resizedFile;
|
byte[] resizedFile;
|
||||||
|
|
||||||
stream.Read(fileData, 0, streamLength);
|
|
||||||
|
|
||||||
BitmapImage objBitmap = new BitmapImage();
|
BitmapImage objBitmap = new BitmapImage();
|
||||||
MemoryStream objBitmapStream = new MemoryStream(fileData);
|
|
||||||
MemoryStream objBitmapStreamResized = new MemoryStream();
|
|
||||||
WriteableBitmap objWB;
|
|
||||||
objBitmap.SetSource(stream);
|
objBitmap.SetSource(stream);
|
||||||
objWB = new WriteableBitmap(objBitmap);
|
objBitmap.CreateOptions = BitmapCreateOptions.None;
|
||||||
|
|
||||||
// resize the photo with user defined TargetWidth & TargetHeight
|
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
|
||||||
Extensions.SaveJpeg(objWB, objBitmapStreamResized, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
|
objBitmap.UriSource = null;
|
||||||
|
|
||||||
//Convert the resized stream to a byte array.
|
//Keep proportionally
|
||||||
streamLength = (int)objBitmapStreamResized.Length;
|
double ratio = Math.Min((double)cameraOptions.TargetWidth / objWB.PixelWidth, (double)cameraOptions.TargetHeight / objWB.PixelHeight);
|
||||||
resizedFile = new Byte[streamLength]; //-1
|
int width = Convert.ToInt32(ratio * objWB.PixelWidth);
|
||||||
objBitmapStreamResized.Position = 0;
|
int height = Convert.ToInt32(ratio * objWB.PixelHeight);
|
||||||
//for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
|
|
||||||
intResult = objBitmapStreamResized.Read(resizedFile, 0, streamLength);
|
//Hold the result stream
|
||||||
|
using (MemoryStream objBitmapStreamResized = new MemoryStream())
|
||||||
|
{
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// resize the photo with user defined TargetWidth & TargetHeight
|
||||||
|
Extensions.SaveJpeg(objWB, objBitmapStreamResized, width, height, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
//Dispose bitmaps immediately, they are memory expensive
|
||||||
|
DisposeImage(objBitmap);
|
||||||
|
DisposeImage(objWB);
|
||||||
|
GC.Collect();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Convert the resized stream to a byte array.
|
||||||
|
int streamLength = (int)objBitmapStreamResized.Length;
|
||||||
|
resizedFile = new Byte[streamLength]; //-1
|
||||||
|
objBitmapStreamResized.Position = 0;
|
||||||
|
|
||||||
|
//for some reason we have to set Position to zero, but we don't have to earlier when we get the bytes from the chosen photo...
|
||||||
|
objBitmapStreamResized.Read(resizedFile, 0, streamLength);
|
||||||
|
}
|
||||||
|
|
||||||
return resizedFile;
|
return resizedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Util: Dispose a bitmap resource
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="image">BitmapSource subclass to dispose</param>
|
||||||
|
private void DisposeImage(BitmapSource image)
|
||||||
|
{
|
||||||
|
if (image != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var ms = new MemoryStream(new byte[] { 0x0 }))
|
||||||
|
{
|
||||||
|
image.SetSource(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Saves captured image in isolated storage
|
/// Saves captured image in isolated storage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="imageFileName">image file name</param>
|
/// <param name="imageFileName">image file name</param>
|
||||||
/// <returns>Image path</returns>
|
/// <returns>Image path</returns>
|
||||||
private string SaveImageToLocalStorage(WriteableBitmap image, string imageFileName)
|
private string SaveImageToLocalStorage(Stream stream, string imageFileName)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (image == null)
|
if (stream == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException("imageBytes");
|
throw new ArgumentNullException("imageBytes");
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
|
var isoFile = IsolatedStorageFile.GetUserStoreForApplication();
|
||||||
|
|
||||||
if (!isoFile.DirectoryExists(isoFolder))
|
if (!isoFile.DirectoryExists(isoFolder))
|
||||||
@ -464,16 +460,41 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
|
|
||||||
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
|
string filePath = System.IO.Path.Combine("///" + isoFolder + "/", imageFileName);
|
||||||
|
|
||||||
using (var stream = isoFile.CreateFile(filePath))
|
using (IsolatedStorageFileStream outputStream = isoFile.CreateFile(filePath))
|
||||||
{
|
{
|
||||||
// resize image if Height and Width defined via options
|
BitmapImage objBitmap = new BitmapImage();
|
||||||
if (cameraOptions.TargetHeight > 0 && cameraOptions.TargetWidth > 0)
|
objBitmap.SetSource(stream);
|
||||||
|
objBitmap.CreateOptions = BitmapCreateOptions.None;
|
||||||
|
|
||||||
|
WriteableBitmap objWB = new WriteableBitmap(objBitmap);
|
||||||
|
objBitmap.UriSource = null;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
image.SaveJpeg(stream, cameraOptions.TargetWidth, cameraOptions.TargetHeight, 0, cameraOptions.Quality);
|
|
||||||
|
//use photo's actual width & height if user doesn't provide width & height
|
||||||
|
if (cameraOptions.TargetWidth < 0 && cameraOptions.TargetHeight < 0)
|
||||||
|
{
|
||||||
|
objWB.SaveJpeg(outputStream, objWB.PixelWidth, objWB.PixelHeight, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Resize
|
||||||
|
//Keep proportionally
|
||||||
|
double ratio = Math.Min((double)cameraOptions.TargetWidth / objWB.PixelWidth, (double)cameraOptions.TargetHeight / objWB.PixelHeight);
|
||||||
|
int width = Convert.ToInt32(ratio * objWB.PixelWidth);
|
||||||
|
int height = Convert.ToInt32(ratio * objWB.PixelHeight);
|
||||||
|
|
||||||
|
// resize the photo with user defined TargetWidth & TargetHeight
|
||||||
|
objWB.SaveJpeg(outputStream, width, height, 0, cameraOptions.Quality);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
image.SaveJpeg(stream, image.PixelWidth, image.PixelHeight, 0, cameraOptions.Quality);
|
//Dispose bitmaps immediately, they are memory expensive
|
||||||
|
DisposeImage(objBitmap);
|
||||||
|
DisposeImage(objWB);
|
||||||
|
GC.Collect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -484,6 +505,10 @@ namespace WPCordovaClassLib.Cordova.Commands
|
|||||||
//TODO: log or do something else
|
//TODO: log or do something else
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
stream.Dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user