You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by st...@apache.org on 2016/08/29 22:05:39 UTC
[4/5] cordova-plugin-camera git commit: Merge remote-tracking branch
'refs/remotes/apache/master'
Merge remote-tracking branch 'refs/remotes/apache/master'
Rebase from Master
Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/commit/0ed64068
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/tree/0ed64068
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/diff/0ed64068
Branch: refs/heads/master
Commit: 0ed6406864ff5a166fbc7fe226193e40c8528eec
Parents: e3a431c fa58e83
Author: swbradshaw <sw...@gmail.com>
Authored: Fri Aug 26 20:30:35 2016 -0400
Committer: swbradshaw <sw...@gmail.com>
Committed: Fri Aug 26 20:30:35 2016 -0400
----------------------------------------------------------------------
.github/PULL_REQUEST_TEMPLATE.md | 23 +
README.md | 46 +-
appium-tests/android/android.spec.js | 504 ++++++++---
appium-tests/helpers/cameraHelper.js | 202 ++++-
appium-tests/ios/ios.spec.js | 241 ++++-
jsdoc2md/TEMPLATE.md | 20 +-
plugin.xml | 5 +
src/android/CameraLauncher.java | 50 +-
src/android/CameraLauncherMaster.java | 1307 ++++++++++++++++++++++++++++
src/browser/CameraProxy.js | 21 +-
src/ios/CDVCamera.m | 16 +-
src/windows/CameraProxy.js | 10 +-
tests/tests.js | 12 +-
www/Camera.js | 8 +-
www/CameraConstants.js | 17 +-
www/ios/CameraPopoverHandle.js | 10 +-
16 files changed, 2243 insertions(+), 249 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/blob/0ed64068/src/android/CameraLauncher.java
----------------------------------------------------------------------
diff --cc src/android/CameraLauncher.java
index 60b07fe,50003a4..69cb5db
--- a/src/android/CameraLauncher.java
+++ b/src/android/CameraLauncher.java
@@@ -56,12 -55,11 +56,11 @@@ import android.net.Uri
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
+import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.util.Base64;
- import android.util.Log;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PermissionInfo;
/**
* This class launches the camera view, allows the user to take a picture, closes the camera view,
@@@ -571,28 -574,47 +570,40 @@@ public class CameraLauncher extends Cor
bitmap = null;
}
-private String getPicutresPath()
-{
- String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
- String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
- File storageDir = Environment.getExternalStoragePublicDirectory(
- Environment.DIRECTORY_PICTURES);
- String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
- return galleryPath;
-}
-
-private void refreshGallery(Uri contentUri)
-{
- Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
- mediaScanIntent.setData(contentUri);
- this.cordova.getActivity().sendBroadcast(mediaScanIntent);
-}
+ private String getPicturesPath()
+ {
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+ String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
+ File storageDir = Environment.getExternalStoragePublicDirectory(
+ Environment.DIRECTORY_PICTURES);
+ String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
+ return galleryPath;
+ }
+
+ private void refreshGallery(Uri contentUri)
+ {
+ Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
+ mediaScanIntent.setData(contentUri);
+ this.cordova.getActivity().sendBroadcast(mediaScanIntent);
+ }
+ /**
+ * Converts output image format int value to string value of mime type.
+ * @param outputFormat int Output format of camera API.
+ * Must be value of either JPEG or PNG constant
+ * @return String String value of mime type or empty string if mime type is not supported
+ */
+ private String getMimetypeForFormat(int outputFormat) {
+ if (outputFormat == PNG) return "image/png";
+ if (outputFormat == JPEG) return "image/jpeg";
+ return "";
+ }
+
- private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
- // Some content: URIs do not map to file paths (e.g. picasa).
- String realPath = FileHelper.getRealPath(uri, this.cordova);
- // Get filename from uri
- String fileName = realPath != null ?
- realPath.substring(realPath.lastIndexOf('/') + 1, realPath.lastIndexOf(".") + 1) :
- "modified.";
-
- // Append filename extension based on output encoding type
- fileName += (this.encodingType == JPEG ? "jpg" : "png");
+ private String outputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
+ String fileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
String modifiedPath = getTempDirectoryPath() + "/" + fileName;
OutputStream os = new FileOutputStream(modifiedPath);
@@@ -683,9 -726,11 +697,11 @@@
else if (destType == FILE_URI || destType == NATIVE_URI) {
// Did we modify the image?
if ( (this.targetHeight > 0 && this.targetWidth > 0) ||
- (this.correctOrientation && this.orientationCorrected) ) {
+ (this.correctOrientation && this.orientationCorrected) ||
+ !mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
+ {
try {
- String modifiedPath = this.ouputModifiedBitmap(bitmap, uri);
+ String modifiedPath = this.outputModifiedBitmap(bitmap, uri);
// The modified image is cached by the app in order to get around this and not have to delete you
// application cache I'm adding the current system time to the end of the file url.
this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis());
http://git-wip-us.apache.org/repos/asf/cordova-plugin-camera/blob/0ed64068/src/android/CameraLauncherMaster.java
----------------------------------------------------------------------
diff --cc src/android/CameraLauncherMaster.java
index 0000000,0000000..506d6d4
new file mode 100644
--- /dev/null
+++ b/src/android/CameraLauncherMaster.java
@@@ -1,0 -1,0 +1,1307 @@@
++/*
++ Licensed to the Apache Software Foundation (ASF) under one
++ or more contributor license agreements. See the NOTICE file
++ distributed with this work for additional information
++ regarding copyright ownership. The ASF licenses this file
++ to you under the Apache License, Version 2.0 (the
++ "License"); you may not use this file except in compliance
++ with the License. You may obtain a copy of the License at
++
++ http://www.apache.org/licenses/LICENSE-2.0
++
++ Unless required by applicable law or agreed to in writing,
++ software distributed under the License is distributed on an
++ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
++ KIND, either express or implied. See the License for the
++ specific language governing permissions and limitations
++ under the License.
++*/
++package org.apache.cordova.camera;
++
++import java.io.ByteArrayOutputStream;
++import java.io.File;
++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;
++import java.util.Date;
++
++import org.apache.cordova.CallbackContext;
++import org.apache.cordova.CordovaPlugin;
++import org.apache.cordova.CordovaResourceApi;
++import org.apache.cordova.LOG;
++import org.apache.cordova.PermissionHelper;
++import org.apache.cordova.PluginResult;
++import org.json.JSONArray;
++import org.json.JSONException;
++
++import android.Manifest;
++import android.annotation.TargetApi;
++import android.app.Activity;
++import android.content.ActivityNotFoundException;
++import android.content.ContentValues;
++import android.content.Context;
++import android.content.Intent;
++import android.database.Cursor;
++import android.graphics.Bitmap;
++import android.graphics.Bitmap.CompressFormat;
++import android.graphics.BitmapFactory;
++import android.graphics.Matrix;
++import android.media.ExifInterface;
++import android.media.MediaScannerConnection;
++import android.media.MediaScannerConnection.MediaScannerConnectionClient;
++import android.net.Uri;
++import android.os.Build;
++import android.os.Bundle;
++import android.os.Environment;
++import android.provider.DocumentsContract;
++import android.provider.MediaStore;
++import android.util.Base64;
++import android.content.pm.PackageManager;
++import android.content.pm.PackageManager.NameNotFoundException;
++import android.content.pm.PermissionInfo;
++
++/**
++ * This class launches the camera view, allows the user to take a picture, closes the camera view,
++ * and returns the captured image. When the camera view is closed, the screen displayed before
++ * the camera view was shown is redisplayed.
++ */
++public class CameraLauncher extends CordovaPlugin implements MediaScannerConnectionClient {
++
++ 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 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 CAMERA = 1; // Take picture from camera
++ private static final int SAVEDPHOTOALBUM = 2; // Choose image from picture library (same as PHOTOLIBRARY for Android)
++
++ private static final int PICTURE = 0; // allow selection of still pictures only. DEFAULT. Will return format specified via DestinationType
++ private static final int VIDEO = 1; // allow selection of video only, ONLY RETURNS URL
++ private static final int ALLMEDIA = 2; // allow selection from all media types
++
++ private static final int JPEG = 0; // Take a picture of type JPEG
++ private static final int PNG = 1; // Take a picture of type PNG
++ private static final String GET_PICTURE = "Get Picture";
++ private static final String GET_VIDEO = "Get Video";
++ private static final String GET_All = "Get All";
++
++ public static final int PERMISSION_DENIED_ERROR = 20;
++ public static final int TAKE_PIC_SEC = 0;
++ public static final int SAVE_TO_ALBUM_SEC = 1;
++
++ private static final String LOG_TAG = "CameraLauncher";
++
++ //Where did this come from?
++ 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 targetWidth; // desired width of the image
++ private int targetHeight; // desired height of the image
++ private Uri imageUri; // Uri of captured image
++ private int encodingType; // Type of encoding to use
++ private int mediaType; // What type of media to retrieve
++ private int destType; // Source type (needs to be saved for the permission handling)
++ private int srcType; // Destination type (needs to be saved for permission handling)
++ 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 orientationCorrected; // Has the picture's orientation been corrected
++ private boolean allowEdit; // Should we allow the user to crop the image.
++
++ protected final static String[] permissions = { Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE };
++
++ public CallbackContext callbackContext;
++ private int numPics;
++
++ 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 croppedUri;
++ private ExifHelper exifData; // Exif data from source
++
++
++ /**
++ * Executes the request and returns PluginResult.
++ *
++ * @param action The action to execute.
++ * @param args JSONArry of arguments for the plugin.
++ * @param callbackContext The callback id used when calling back into JavaScript.
++ * @return A PluginResult object with a status and message.
++ */
++ public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
++ this.callbackContext = callbackContext;
++
++ if (action.equals("takePicture")) {
++ this.srcType = CAMERA;
++ this.destType = FILE_URI;
++ this.saveToPhotoAlbum = false;
++ this.targetHeight = 0;
++ this.targetWidth = 0;
++ this.encodingType = JPEG;
++ this.mediaType = PICTURE;
++ this.mQuality = 50;
++
++ //Take the values from the arguments if they're not already defined (this is tricky)
++ this.destType = args.getInt(1);
++ this.srcType = args.getInt(2);
++ this.mQuality = args.getInt(0);
++ this.targetWidth = args.getInt(3);
++ this.targetHeight = args.getInt(4);
++ this.encodingType = args.getInt(5);
++ this.mediaType = args.getInt(6);
++ this.allowEdit = args.getBoolean(7);
++ this.correctOrientation = args.getBoolean(8);
++ this.saveToPhotoAlbum = args.getBoolean(9);
++
++ // If the user specifies a 0 or smaller width/height
++ // make it -1 so later comparisons succeed
++ if (this.targetWidth < 1) {
++ this.targetWidth = -1;
++ }
++ if (this.targetHeight < 1) {
++ this.targetHeight = -1;
++ }
++
++ // We don't return full-quality PNG files. The camera outputs a JPEG
++ // so requesting it as a PNG provides no actual benefit
++ if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
++ !this.correctOrientation && this.encodingType == PNG && this.srcType == CAMERA) {
++ this.encodingType = JPEG;
++ }
++
++ try {
++ if (this.srcType == CAMERA) {
++ this.callTakePicture(destType, encodingType);
++ }
++ else if ((this.srcType == PHOTOLIBRARY) || (this.srcType == SAVEDPHOTOALBUM)) {
++ // FIXME: Stop always requesting the permission
++ if(!PermissionHelper.hasPermission(this, permissions[0])) {
++ PermissionHelper.requestPermission(this, SAVE_TO_ALBUM_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
++ } else {
++ this.getImage(this.srcType, destType, encodingType);
++ }
++ }
++ }
++ catch (IllegalArgumentException e)
++ {
++ callbackContext.error("Illegal Argument Exception");
++ PluginResult r = new PluginResult(PluginResult.Status.ERROR);
++ callbackContext.sendPluginResult(r);
++ return true;
++ }
++
++ PluginResult r = new PluginResult(PluginResult.Status.NO_RESULT);
++ r.setKeepCallback(true);
++ callbackContext.sendPluginResult(r);
++
++ return true;
++ }
++ return false;
++ }
++
++ //--------------------------------------------------------------------------
++ // LOCAL METHODS
++ //--------------------------------------------------------------------------
++
++ private String getTempDirectoryPath() {
++ File cache = null;
++
++ // SD Card Mounted
++ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
++ cache = cordova.getActivity().getExternalCacheDir();
++ }
++ // Use internal storage
++ else {
++ cache = cordova.getActivity().getCacheDir();
++ }
++
++ // Create the cache directory if it doesn't exist
++ cache.mkdirs();
++ return cache.getAbsolutePath();
++ }
++
++ /**
++ * Take a picture with the camera.
++ * When an image is captured or the camera view is cancelled, the result is returned
++ * in CordovaActivity.onActivityResult, which forwards the result to this.onActivityResult.
++ *
++ * The image can either be returned as a base64 string or a URI that points to the file.
++ * To display base64 string in an img tag, set the source to:
++ * img.src="data:image/jpeg;base64,"+result;
++ * or to display URI in an img tag
++ * img.src=result;
++ *
++ * @param returnType Set the type of image to return.
++ * @param encodingType JPEG or PNG
++ */
++ public void callTakePicture(int returnType, int encodingType) {
++ boolean saveAlbumPermission = PermissionHelper.hasPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
++ boolean takePicturePermission = PermissionHelper.hasPermission(this, Manifest.permission.CAMERA);
++
++ // CB-10120: The CAMERA permission does not need to be requested unless it is declared
++ // in AndroidManifest.xml. This plugin does not declare it, but others may and so we must
++ // check the package info to determine if the permission is present.
++
++ if (!takePicturePermission) {
++ takePicturePermission = true;
++ try {
++ PackageManager packageManager = this.cordova.getActivity().getPackageManager();
++ String[] permissionsInPackage = packageManager.getPackageInfo(this.cordova.getActivity().getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions;
++ if (permissionsInPackage != null) {
++ for (String permission : permissionsInPackage) {
++ if (permission.equals(Manifest.permission.CAMERA)) {
++ takePicturePermission = false;
++ break;
++ }
++ }
++ }
++ } catch (NameNotFoundException e) {
++ // We are requesting the info for our package, so this should
++ // never be caught
++ }
++ }
++
++ if (takePicturePermission && saveAlbumPermission) {
++ takePicture(returnType, encodingType);
++ } else if (saveAlbumPermission && !takePicturePermission) {
++ PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.CAMERA);
++ } else if (!saveAlbumPermission && takePicturePermission) {
++ PermissionHelper.requestPermission(this, TAKE_PIC_SEC, Manifest.permission.READ_EXTERNAL_STORAGE);
++ } else {
++ PermissionHelper.requestPermissions(this, TAKE_PIC_SEC, permissions);
++ }
++ }
++
++ public void takePicture(int returnType, int encodingType)
++ {
++ // Save the number of images currently on disk for later
++ this.numPics = queryImgDB(whichContentStore()).getCount();
++
++ // Let's use the intent and see what happens
++ Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
++
++ // Specify file so that large image is captured and returned
++ File photo = createCaptureFile(encodingType);
++ intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, Uri.fromFile(photo));
++ this.imageUri = Uri.fromFile(photo);
++
++ if (this.cordova != null) {
++ // Let's check to make sure the camera is actually installed. (Legacy Nexus 7 code)
++ PackageManager mPm = this.cordova.getActivity().getPackageManager();
++ if(intent.resolveActivity(mPm) != null)
++ {
++
++ this.cordova.startActivityForResult((CordovaPlugin) this, intent, (CAMERA + 1) * 16 + returnType + 1);
++ }
++ else
++ {
++ LOG.d(LOG_TAG, "Error: You don't have a default camera. Your device may not be CTS complaint.");
++ }
++ }
++// else
++// LOG.d(LOG_TAG, "ERROR: You must use the CordovaInterface for this to work correctly. Please implement it in your activity");
++ }
++
++ /**
++ * Create a file in the applications temporary directory based upon the supplied encoding.
++ *
++ * @param encodingType of the image to be taken
++ * @return a File object pointing to the temporary picture
++ */
++ private File createCaptureFile(int encodingType) {
++ return createCaptureFile(encodingType, "");
++ }
++
++ /**
++ * Create a file in the applications temporary directory based upon the supplied encoding.
++ *
++ * @param encodingType of the image to be taken
++ * @param fileName or resultant File object.
++ * @return a File object pointing to the temporary picture
++ */
++ private File createCaptureFile(int encodingType, String fileName) {
++ if (fileName.isEmpty()) {
++ fileName = ".Pic";
++ }
++
++ if (encodingType == JPEG) {
++ fileName = fileName + ".jpg";
++ } else if (encodingType == PNG) {
++ fileName = fileName + ".png";
++ } else {
++ throw new IllegalArgumentException("Invalid Encoding Type: " + encodingType);
++ }
++
++ return new File(getTempDirectoryPath(), fileName);
++ }
++
++
++
++ /**
++ * Get image from photo library.
++ *
++ * @param srcType The album to get image from.
++ * @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 from kitkat filechooser not going into crop function
++ public void getImage(int srcType, int returnType, int encodingType) {
++ Intent intent = new Intent();
++ String title = GET_PICTURE;
++ croppedUri = null;
++ if (this.mediaType == PICTURE) {
++ 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(JPEG);
++ 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);
++ }
++ if (this.cordova != null) {
++ this.cordova.startActivityForResult((CordovaPlugin) this, Intent.createChooser(intent,
++ 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, int destType, Intent cameraIntent) {
++ 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);
++ }
++ // create new file handle to get full resolution crop
++ croppedUri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
++ cropIntent.putExtra("output", croppedUri);
++
++ // start the activity - we handle returning in onActivityResult
++
++ if (this.cordova != null) {
++ this.cordova.startActivityForResult((CordovaPlugin) this,
++ cropIntent, CROP_CAMERA + destType);
++ }
++ } catch (ActivityNotFoundException anfe) {
++ LOG.e(LOG_TAG, "Crop operation not supported on this device");
++ try {
++ processResultFromCamera(destType, cameraIntent);
++ }
++ catch (IOException e)
++ {
++ e.printStackTrace();
++ LOG.e(LOG_TAG, "Unable to write to file");
++ }
++ }
++ }
++
++ /**
++ * Applies all needed transformation to the image received from the camera.
++ *
++ * @param destType In which form should we return the image
++ * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
++ */
++ private void processResultFromCamera(int destType, Intent intent) throws IOException {
++ int rotate = 0;
++
++ // Create an ExifHelper to save the exif data that is lost during compression
++ ExifHelper exif = new ExifHelper();
++ String sourcePath = (this.allowEdit && this.croppedUri != null) ?
++ FileHelper.stripFileProtocol(this.croppedUri.toString()) :
++ FileHelper.stripFileProtocol(this.imageUri.toString());
++
++ if (this.encodingType == JPEG) {
++ try {
++ //We don't support PNG, so let's not pretend we do
++ exif.createInFile(sourcePath);
++ exif.readExifData();
++ rotate = exif.getOrientation();
++
++ } catch (IOException e) {
++ e.printStackTrace();
++ }
++ }
++
++ Bitmap bitmap = null;
++ Uri galleryUri = null;
++
++ // CB-5479 When this option is given the unchanged image should be saved
++ // in the gallery and the modified image is saved in the temporary
++ // directory
++ if (this.saveToPhotoAlbum) {
++ galleryUri = Uri.fromFile(new File(getPicturesPath()));
++
++ if(this.allowEdit && this.croppedUri != null) {
++ writeUncompressedImage(this.croppedUri, galleryUri);
++ } else {
++ writeUncompressedImage(this.imageUri, galleryUri);
++ }
++
++ refreshGallery(galleryUri);
++ }
++
++ // If sending base64 image back
++ if (destType == DATA_URL) {
++ bitmap = getScaledAndRotatedBitmap(sourcePath);
++
++ if (bitmap == null) {
++ // Try to get the bitmap from intent.
++ bitmap = (Bitmap)intent.getExtras().get("data");
++ }
++
++ // Double-check the bitmap.
++ if (bitmap == null) {
++ LOG.d(LOG_TAG, "I either have a null image path or bitmap");
++ this.failPicture("Unable to create bitmap!");
++ return;
++ }
++
++
++ this.processPicture(bitmap, this.encodingType);
++
++ if (!this.saveToPhotoAlbum) {
++ checkForDuplicateImage(DATA_URL);
++ }
++ }
++
++ // If sending filename back
++ else if (destType == FILE_URI || destType == NATIVE_URI) {
++ // If all this is true we shouldn't compress the image.
++ if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100 &&
++ !this.correctOrientation) {
++
++ // If we saved the uncompressed photo to the album, we can just
++ // return the URI we already created
++ if (this.saveToPhotoAlbum) {
++ this.callbackContext.success(galleryUri.toString());
++ } else {
++ Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
++
++ if(this.allowEdit && this.croppedUri != null) {
++ writeUncompressedImage(this.croppedUri, uri);
++ } else {
++ writeUncompressedImage(this.imageUri, uri);
++ }
++
++ this.callbackContext.success(uri.toString());
++ }
++ } else {
++ Uri uri = Uri.fromFile(createCaptureFile(this.encodingType, System.currentTimeMillis() + ""));
++ bitmap = getScaledAndRotatedBitmap(sourcePath);
++
++ // Double-check the bitmap.
++ if (bitmap == null) {
++ LOG.d(LOG_TAG, "I either have a null image path or bitmap");
++ this.failPicture("Unable to create bitmap!");
++ return;
++ }
++
++
++ // Add compressed version of captured image to returned media store Uri
++ OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
++ CompressFormat compressFormat = encodingType == JPEG ?
++ CompressFormat.JPEG :
++ CompressFormat.PNG;
++
++ bitmap.compress(compressFormat, this.mQuality, os);
++ os.close();
++
++ // Restore exif data to file
++ if (this.encodingType == JPEG) {
++ String exifPath;
++ exifPath = uri.getPath();
++ exif.createOutFile(exifPath);
++ exif.writeExifData();
++ }
++
++ // Send Uri back to JavaScript for viewing image
++ this.callbackContext.success(uri.toString());
++
++ }
++ } else {
++ throw new IllegalStateException();
++ }
++
++ this.cleanup(FILE_URI, this.imageUri, galleryUri, bitmap);
++ bitmap = null;
++ }
++
++private String getPicutresPath()
++{
++ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
++ String imageFileName = "IMG_" + timeStamp + (this.encodingType == JPEG ? ".jpg" : ".png");
++ File storageDir = Environment.getExternalStoragePublicDirectory(
++ Environment.DIRECTORY_PICTURES);
++ String galleryPath = storageDir.getAbsolutePath() + "/" + imageFileName;
++ return galleryPath;
++}
++
++private void refreshGallery(Uri contentUri)
++{
++ Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
++ mediaScanIntent.setData(contentUri);
++ this.cordova.getActivity().sendBroadcast(mediaScanIntent);
++}
++
++ /**
++ * Converts output image format int value to string value of mime type.
++ * @param outputFormat int Output format of camera API.
++ * Must be value of either JPEG or PNG constant
++ * @return String String value of mime type or empty string if mime type is not supported
++ */
++ private String getMimetypeForFormat(int outputFormat) {
++ if (outputFormat == PNG) return "image/png";
++ if (outputFormat == JPEG) return "image/jpeg";
++ return "";
++ }
++
++ private String ouputModifiedBitmap(Bitmap bitmap, Uri uri) throws IOException {
++ // Some content: URIs do not map to file paths (e.g. picasa).
++ String realPath = FileHelper.getRealPath(uri, this.cordova);
++
++ // Get filename from uri
++ String fileName = realPath != null ?
++ realPath.substring(realPath.lastIndexOf('/') + 1, realPath.lastIndexOf(".") + 1) :
++ "modified.";
++
++ // Append filename extension based on output encoding type
++ fileName += (this.encodingType == JPEG ? "jpg" : "png");
++
++ String modifiedPath = getTempDirectoryPath() + "/" + fileName;
++
++ OutputStream os = new FileOutputStream(modifiedPath);
++ CompressFormat compressFormat = this.encodingType == JPEG ?
++ CompressFormat.JPEG :
++ CompressFormat.PNG;
++
++ bitmap.compress(compressFormat, this.mQuality, os);
++ os.close();
++
++ if (realPath != null && this.encodingType == JPEG) {
++ // Create an ExifHelper to save the exif data that is lost during compression
++ ExifHelper exif = new ExifHelper();
++
++ try {
++ exif.createInFile(realPath);
++ exif.readExifData();
++ if (this.correctOrientation && this.orientationCorrected) {
++ exif.resetOrientation();
++ }
++ exif.createOutFile(modifiedPath);
++ exif.writeExifData();
++ } catch (IOException e) {
++ e.printStackTrace();
++ }
++ }
++ return modifiedPath;
++ }
++
++
++
++/**
++ * Applies all needed transformation to the image received from the gallery.
++ *
++ * @param destType In which form should we return the image
++ * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
++ */
++ private void processResultFromGallery(int destType, Intent intent) {
++ Uri uri = intent.getData();
++ if (uri == null) {
++ if (croppedUri != null) {
++ uri = croppedUri;
++ } else {
++ this.failPicture("null data from photo library");
++ return;
++ }
++ }
++ int rotate = 0;
++
++ String fileLocation = FileHelper.getRealPath(uri, this.cordova);
++ LOG.d(LOG_TAG, "File locaton is: " + fileLocation);
++
++ // If you ask for video or all media type you will automatically get back a file URI
++ // and there will be no attempt to resize any returned data
++ if (this.mediaType != PICTURE) {
++ this.callbackContext.success(fileLocation);
++ }
++ else {
++ String uriString = uri.toString();
++ // Get the path to the image. Makes loading so much easier.
++ String mimeType = FileHelper.getMimeType(uriString, this.cordova);
++
++ // This is a special case to just return the path as no scaling,
++ // rotating, nor compressing needs to be done
++ if (this.targetHeight == -1 && this.targetWidth == -1 &&
++ (destType == FILE_URI || destType == NATIVE_URI) && !this.correctOrientation &&
++ mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
++ {
++ this.callbackContext.success(uriString);
++ } else {
++ // If we don't have a valid image so quit.
++ if (!("image/jpeg".equalsIgnoreCase(mimeType) || "image/png".equalsIgnoreCase(mimeType))) {
++ LOG.d(LOG_TAG, "I either have a null image path or bitmap");
++ this.failPicture("Unable to retrieve path to picture!");
++ return;
++ }
++ Bitmap bitmap = null;
++ try {
++ bitmap = getScaledBitmap(uriString);
++ } catch (IOException e) {
++ e.printStackTrace();
++ }
++ if (bitmap == null) {
++ LOG.d(LOG_TAG, "I either have a null image path or bitmap");
++ this.failPicture("Unable to create bitmap!");
++ return;
++ }
++
++ if (this.correctOrientation) {
++ rotate = getImageOrientation(uri);
++ if (rotate != 0) {
++ Matrix matrix = new Matrix();
++ matrix.setRotate(rotate);
++ try {
++ bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
++ this.orientationCorrected = true;
++ } catch (OutOfMemoryError oom) {
++ this.orientationCorrected = false;
++ }
++ }
++ }
++
++ // If sending base64 image back
++ if (destType == DATA_URL) {
++ this.processPicture(bitmap, this.encodingType);
++ }
++
++ // If sending filename back
++ else if (destType == FILE_URI || destType == NATIVE_URI) {
++ // Did we modify the image?
++ if ( (this.targetHeight > 0 && this.targetWidth > 0) ||
++ (this.correctOrientation && this.orientationCorrected) ||
++ !mimeType.equalsIgnoreCase(getMimetypeForFormat(encodingType)))
++ {
++ try {
++ String modifiedPath = this.ouputModifiedBitmap(bitmap, uri);
++ // The modified image is cached by the app in order to get around this and not have to delete you
++ // application cache I'm adding the current system time to the end of the file url.
++ this.callbackContext.success("file://" + modifiedPath + "?" + System.currentTimeMillis());
++
++ } catch (Exception e) {
++ e.printStackTrace();
++ this.failPicture("Error retrieving image.");
++ }
++ }
++ else {
++ this.callbackContext.success(fileLocation);
++ }
++ }
++ if (bitmap != null) {
++ bitmap.recycle();
++ bitmap = null;
++ }
++ System.gc();
++ }
++ }
++ }
++
++ /**
++ * Called when the camera view exits.
++ *
++ * @param requestCode The request code originally supplied to startActivityForResult(),
++ * allowing you to identify who this result came from.
++ * @param resultCode The integer result code returned by the child activity through its setResult().
++ * @param intent An Intent, which can return result data to the caller (various data can be attached to Intent "extras").
++ */
++ public void onActivityResult(int requestCode, int resultCode, Intent intent) {
++
++ // Get src and dest types from request code for a Camera Activity
++ int srcType = (requestCode / 16) - 1;
++ int destType = (requestCode % 16) - 1;
++
++ // If Camera Crop
++ if (requestCode >= CROP_CAMERA) {
++ if (resultCode == Activity.RESULT_OK) {
++
++ // Because of the inability to pass through multiple intents, this hack will allow us
++ // to pass arcane codes back.
++ destType = requestCode - CROP_CAMERA;
++ try {
++ processResultFromCamera(destType, intent);
++ } catch (IOException e) {
++ e.printStackTrace();
++ LOG.e(LOG_TAG, "Unable to write to file");
++ }
++
++ }// If cancelled
++ else if (resultCode == Activity.RESULT_CANCELED) {
++ this.failPicture("Camera cancelled.");
++ }
++
++ // If something else
++ else {
++ this.failPicture("Did not complete!");
++ }
++ }
++ // If CAMERA
++ else if (srcType == CAMERA) {
++ // If image available
++ if (resultCode == Activity.RESULT_OK) {
++ try {
++ if(this.allowEdit)
++ {
++ Uri tmpFile = Uri.fromFile(createCaptureFile(this.encodingType));
++ performCrop(tmpFile, destType, intent);
++ }
++ else {
++ this.processResultFromCamera(destType, intent);
++ }
++ } catch (IOException e) {
++ e.printStackTrace();
++ this.failPicture("Error capturing image.");
++ }
++ }
++
++ // If cancelled
++ else if (resultCode == Activity.RESULT_CANCELED) {
++ this.failPicture("Camera cancelled.");
++ }
++
++ // If something else
++ else {
++ this.failPicture("Did not complete!");
++ }
++ }
++ // If retrieving photo from library
++ else if ((srcType == PHOTOLIBRARY) || (srcType == SAVEDPHOTOALBUM)) {
++ if (resultCode == Activity.RESULT_OK && intent != null) {
++ final Intent i = intent;
++ final int finalDestType = destType;
++ cordova.getThreadPool().execute(new Runnable() {
++ public void run() {
++ processResultFromGallery(finalDestType, i);
++ }
++ });
++ }
++ else if (resultCode == Activity.RESULT_CANCELED) {
++ this.failPicture("Selection cancelled.");
++ }
++ else {
++ this.failPicture("Selection did not complete!");
++ }
++ }
++ }
++
++ private int getImageOrientation(Uri uri) {
++ int rotate = 0;
++ String[] cols = { MediaStore.Images.Media.ORIENTATION };
++ try {
++ Cursor cursor = cordova.getActivity().getContentResolver().query(uri,
++ cols, null, null, null);
++ if (cursor != null) {
++ cursor.moveToPosition(0);
++ rotate = cursor.getInt(0);
++ cursor.close();
++ }
++ } catch (Exception e) {
++ // You can get an IllegalArgumentException if ContentProvider doesn't support querying for orientation.
++ }
++ return rotate;
++ }
++
++ /**
++ * Figure out if the bitmap should be rotated. For instance if the picture was taken in
++ * portrait mode
++ *
++ * @param rotate
++ * @param bitmap
++ * @return rotated bitmap
++ */
++ private Bitmap getRotatedBitmap(int rotate, Bitmap bitmap, ExifHelper exif) {
++ Matrix matrix = new Matrix();
++ if (rotate == 180) {
++ matrix.setRotate(rotate);
++ } else {
++ matrix.setRotate(rotate, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
++ }
++
++ try
++ {
++ bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
++ exif.resetOrientation();
++ }
++ catch (OutOfMemoryError oom)
++ {
++ // You can run out of memory if the image is very large:
++ // http://simonmacdonald.blogspot.ca/2012/07/change-to-camera-code-in-phonegap-190.html
++ // If this happens, simply do not rotate the image and return it unmodified.
++ // If you do not catch the OutOfMemoryError, the Android app crashes.
++ }
++
++ return bitmap;
++ }
++
++ /**
++ * In the special case where the default width, height and quality are unchanged
++ * we just write the file out to disk saving the expensive Bitmap.compress function.
++ *
++ * @param uri
++ * @throws FileNotFoundException
++ * @throws IOException
++ */
++ private void writeUncompressedImage(Uri src, Uri dest) throws FileNotFoundException,
++ IOException {
++ FileInputStream fis = null;
++ OutputStream os = null;
++ try {
++ fis = new FileInputStream(FileHelper.stripFileProtocol(src.toString()));
++ os = this.cordova.getActivity().getContentResolver().openOutputStream(dest);
++ 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.");
++ }
++ }
++ }
++ }
++
++ /**
++ * Create entry in media store for image
++ *
++ * @return uri
++ */
++ private Uri getUriFromMediaStore() {
++ ContentValues values = new ContentValues();
++ values.put(android.provider.MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
++ Uri uri;
++ try {
++ uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
++ } catch (RuntimeException e) {
++ LOG.d(LOG_TAG, "Can't write to external media storage.");
++ try {
++ uri = this.cordova.getActivity().getContentResolver().insert(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI, values);
++ } catch (RuntimeException ex) {
++ LOG.d(LOG_TAG, "Can't write to internal media storage.");
++ return null;
++ }
++ }
++ return uri;
++ }
++
++ /**
++ * Return a scaled bitmap based on the target width and height
++ *
++ * @param imagePath
++ * @return
++ * @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) {
++ 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;
++ 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)
++ {
++ return null;
++ }
++
++ // determine the correct aspect ratio
++ int[] widthHeight = calculateAspectRatio(options.outWidth, options.outHeight);
++
++ // 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 = 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;
++ }
++
++ return Bitmap.createScaledBitmap(unscaledBitmap, widthHeight[0], widthHeight[1], true);
++ }
++
++ /**
++ * Maintain the aspect ratio so the resulting image does not look smooshed
++ *
++ * @param origWidth
++ * @param origHeight
++ * @return
++ */
++ public int[] calculateAspectRatio(int origWidth, int origHeight) {
++ int newWidth = this.targetWidth;
++ int newHeight = this.targetHeight;
++
++ // If no new width or height were specified return the original bitmap
++ if (newWidth <= 0 && newHeight <= 0) {
++ newWidth = origWidth;
++ newHeight = origHeight;
++ }
++ // Only the width was specified
++ else if (newWidth > 0 && newHeight <= 0) {
++ newHeight = (newWidth * origHeight) / origWidth;
++ }
++ // only the height was specified
++ else if (newWidth <= 0 && newHeight > 0) {
++ newWidth = (newHeight * origWidth) / origHeight;
++ }
++ // If the user specified both a positive width and height
++ // (potentially different aspect ratio) then the width or height is
++ // scaled so that the image fits while maintaining aspect ratio.
++ // Alternatively, the specified width and height could have been
++ // kept and Bitmap.SCALE_TO_FIT specified when scaling, but this
++ // would result in whitespace in the new image.
++ else {
++ double newRatio = newWidth / (double) newHeight;
++ double origRatio = origWidth / (double) origHeight;
++
++ if (origRatio > newRatio) {
++ newHeight = (newWidth * origHeight) / origWidth;
++ } else if (origRatio < newRatio) {
++ newWidth = (newHeight * origWidth) / origHeight;
++ }
++ }
++
++ int[] retval = new int[2];
++ retval[0] = newWidth;
++ retval[1] = newHeight;
++ return retval;
++ }
++
++ /**
++ * Figure out what ratio we can load our image into memory at while still being bigger than
++ * our desired width and height
++ *
++ * @param srcWidth
++ * @param srcHeight
++ * @param dstWidth
++ * @param dstHeight
++ * @return
++ */
++ public static int calculateSampleSize(int srcWidth, int srcHeight, int dstWidth, int dstHeight) {
++ final float srcAspect = (float)srcWidth / (float)srcHeight;
++ final float dstAspect = (float)dstWidth / (float)dstHeight;
++
++ if (srcAspect > dstAspect) {
++ return srcWidth / dstWidth;
++ } else {
++ return srcHeight / dstHeight;
++ }
++ }
++
++ /**
++ * Creates a cursor that can be used to determine how many images we have.
++ *
++ * @return a cursor
++ */
++ private Cursor queryImgDB(Uri contentStore) {
++ return this.cordova.getActivity().getContentResolver().query(
++ contentStore,
++ new String[] { MediaStore.Images.Media._ID },
++ null,
++ null,
++ null);
++ }
++
++ /**
++ * Cleans up after picture taking. Checking for duplicates and that kind of stuff.
++ * @param newImage
++ */
++ private void cleanup(int imageType, Uri oldImage, Uri newImage, Bitmap bitmap) {
++ if (bitmap != null) {
++ bitmap.recycle();
++ }
++
++ // Clean up initial camera-written image file.
++ (new File(FileHelper.stripFileProtocol(oldImage.toString()))).delete();
++
++ checkForDuplicateImage(imageType);
++ // Scan for the gallery to update pic refs in gallery
++ if (this.saveToPhotoAlbum && newImage != null) {
++ this.scanForGallery(newImage);
++ }
++
++ System.gc();
++ }
++
++ /**
++ * Used to find out if we are in a situation where the Camera Intent adds to images
++ * to the content store. If we are using a FILE_URI and the number of images in the DB
++ * increases by 2 we have a duplicate, when using a DATA_URL the number is 1.
++ *
++ * @param type FILE_URI or DATA_URL
++ */
++ private void checkForDuplicateImage(int type) {
++ int diff = 1;
++ Uri contentStore = whichContentStore();
++ Cursor cursor = queryImgDB(contentStore);
++ int currentNumOfImages = cursor.getCount();
++
++ if (type == FILE_URI && this.saveToPhotoAlbum) {
++ diff = 2;
++ }
++
++ // delete the duplicate file if the difference is 2 for file URI or 1 for Data URL
++ if ((currentNumOfImages - numPics) == diff) {
++ cursor.moveToLast();
++ int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID)));
++ if (diff == 2) {
++ id--;
++ }
++ Uri uri = Uri.parse(contentStore + "/" + id);
++ this.cordova.getActivity().getContentResolver().delete(uri, null, null);
++ cursor.close();
++ }
++ }
++
++ /**
++ * Determine if we are storing the images in internal or external storage
++ * @return Uri
++ */
++ private Uri whichContentStore() {
++ if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
++ return android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
++ } else {
++ return android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI;
++ }
++ }
++
++ /**
++ * Compress bitmap using jpeg, convert to Base64 encoded string, and return to JavaScript.
++ *
++ * @param bitmap
++ */
++ public void processPicture(Bitmap bitmap, int encodingType) {
++ ByteArrayOutputStream jpeg_data = new ByteArrayOutputStream();
++ CompressFormat compressFormat = encodingType == JPEG ?
++ CompressFormat.JPEG :
++ CompressFormat.PNG;
++
++ try {
++ if (bitmap.compress(compressFormat, mQuality, jpeg_data)) {
++ byte[] code = jpeg_data.toByteArray();
++ byte[] output = Base64.encode(code, Base64.NO_WRAP);
++ String js_out = new String(output);
++ this.callbackContext.success(js_out);
++ js_out = null;
++ output = null;
++ code = null;
++ }
++ } catch (Exception e) {
++ this.failPicture("Error compressing image.");
++ }
++ jpeg_data = null;
++ }
++
++ /**
++ * Send error message to JavaScript.
++ *
++ * @param err
++ */
++ public void failPicture(String err) {
++ this.callbackContext.error(err);
++ }
++
++ private void scanForGallery(Uri newImage) {
++ this.scanMe = newImage;
++ if(this.conn != null) {
++ this.conn.disconnect();
++ }
++ this.conn = new MediaScannerConnection(this.cordova.getActivity().getApplicationContext(), this);
++ conn.connect();
++ }
++
++ public void onMediaScannerConnected() {
++ try{
++ this.conn.scanFile(this.scanMe.toString(), "image/*");
++ } catch (java.lang.IllegalStateException e){
++ LOG.e(LOG_TAG, "Can't scan file in MediaScanner after taking picture");
++ }
++
++ }
++
++ public void onScanCompleted(String path, Uri uri) {
++ this.conn.disconnect();
++ }
++
++
++ public void onRequestPermissionResult(int requestCode, String[] permissions,
++ int[] grantResults) throws JSONException
++ {
++ for(int r:grantResults)
++ {
++ if(r == PackageManager.PERMISSION_DENIED)
++ {
++ this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
++ return;
++ }
++ }
++ switch(requestCode)
++ {
++ case TAKE_PIC_SEC:
++ takePicture(this.destType, this.encodingType);
++ break;
++ case SAVE_TO_ALBUM_SEC:
++ this.getImage(this.srcType, this.destType, this.encodingType);
++ break;
++ }
++ }
++
++ /**
++ * Taking or choosing a picture launches another Activity, so we need to implement the
++ * save/restore APIs to handle the case where the CordovaActivity is killed by the OS
++ * before we get the launched Activity's result.
++ */
++ public Bundle onSaveInstanceState() {
++ Bundle state = new Bundle();
++ state.putInt("destType", this.destType);
++ state.putInt("srcType", this.srcType);
++ state.putInt("mQuality", this.mQuality);
++ state.putInt("targetWidth", this.targetWidth);
++ state.putInt("targetHeight", this.targetHeight);
++ state.putInt("encodingType", this.encodingType);
++ state.putInt("mediaType", this.mediaType);
++ state.putInt("numPics", this.numPics);
++ state.putBoolean("allowEdit", this.allowEdit);
++ state.putBoolean("correctOrientation", this.correctOrientation);
++ state.putBoolean("saveToPhotoAlbum", this.saveToPhotoAlbum);
++
++ if(this.croppedUri != null) {
++ state.putString("croppedUri", this.croppedUri.toString());
++ }
++
++ if(this.imageUri != null) {
++ state.putString("imageUri", this.imageUri.toString());
++ }
++
++ return state;
++ }
++
++ public void onRestoreStateForActivityResult(Bundle state, CallbackContext callbackContext) {
++ this.destType = state.getInt("destType");
++ this.srcType = state.getInt("srcType");
++ this.mQuality = state.getInt("mQuality");
++ this.targetWidth = state.getInt("targetWidth");
++ this.targetHeight = state.getInt("targetHeight");
++ this.encodingType = state.getInt("encodingType");
++ this.mediaType = state.getInt("mediaType");
++ this.numPics = state.getInt("numPics");
++ this.allowEdit = state.getBoolean("allowEdit");
++ this.correctOrientation = state.getBoolean("correctOrientation");
++ this.saveToPhotoAlbum = state.getBoolean("saveToPhotoAlbum");
++
++ if(state.containsKey("croppedUri")) {
++ this.croppedUri = Uri.parse(state.getString("croppedUri"));
++ }
++
++ if(state.containsKey("imageUri")) {
++ this.imageUri = Uri.parse(state.getString("imageUri"));
++ }
++
++ this.callbackContext = callbackContext;
++ }
++}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org