You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ma...@apache.org on 2012/06/20 17:03:38 UTC
android commit: CB-910: Camera out of memory error
Updated Branches:
refs/heads/master 8969eed50 -> a691e9f74
CB-910: Camera out of memory error
Whenever possible do not load the image into a Bitmap as it takes too much memory and blows up the Java heap.
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/commit/a691e9f7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/tree/a691e9f7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/diff/a691e9f7
Branch: refs/heads/master
Commit: a691e9f744a878d3f457d7c229a4c74889fa1cac
Parents: 8969eed
Author: macdonst <si...@gmail.com>
Authored: Tue Jun 19 10:51:19 2012 -0400
Committer: macdonst <si...@gmail.com>
Committed: Wed Jun 20 11:00:13 2012 -0400
----------------------------------------------------------------------
.../src/org/apache/cordova/CameraLauncher.java | 55 +++++++++---
framework/src/org/apache/cordova/Capture.java | 69 ++++++++++----
2 files changed, 91 insertions(+), 33 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/a691e9f7/framework/src/org/apache/cordova/CameraLauncher.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/CameraLauncher.java b/framework/src/org/apache/cordova/CameraLauncher.java
index 1afaf8d..3520247 100755
--- a/framework/src/org/apache/cordova/CameraLauncher.java
+++ b/framework/src/org/apache/cordova/CameraLauncher.java
@@ -20,6 +20,7 @@ package org.apache.cordova;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -43,6 +44,7 @@ import android.graphics.Matrix;
import android.graphics.Bitmap.CompressFormat;
import android.net.Uri;
import android.provider.MediaStore;
+import android.util.Log;
/**
* This class launches the camera view, allows the user to take a picture, closes the camera view,
@@ -277,6 +279,7 @@ public class CameraLauncher extends Plugin {
Bitmap retval = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
bitmap.recycle();
+ System.gc();
return retval;
}
@@ -310,20 +313,12 @@ public class CameraLauncher extends Plugin {
// If image available
if (resultCode == Activity.RESULT_OK) {
try {
- // Read in bitmap of captured image
- Bitmap bitmap;
- try {
- bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.cordova.getActivity().getContentResolver(), imageUri);
- } catch (FileNotFoundException e) {
- Uri uri = intent.getData();
- android.content.ContentResolver resolver = this.cordova.getActivity().getContentResolver();
- bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
- }
-
- bitmap = scaleBitmap(bitmap);
+ Bitmap bitmap = null;
// If sending base64 image back
if (destType == DATA_URL) {
+ bitmap = scaleBitmap(getBitmapFromResult(intent));
+
this.processPicture(bitmap);
checkForDuplicateImage(DATA_URL);
}
@@ -348,6 +343,27 @@ public class CameraLauncher extends Plugin {
}
}
+ // If all this is true we shouldn't compress the image.
+ if (this.targetHeight == -1 && this.targetWidth == -1 && this.mQuality == 100) {
+ FileInputStream fis = new FileInputStream(FileUtils.stripFileProtocol(imageUri.toString()));
+ OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
+ byte[] buffer = new byte[4096];
+ int len;
+ while ((len = fis.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ os.flush();
+ os.close();
+ fis.close();
+
+ checkForDuplicateImage(FILE_URI);
+
+ this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
+ return;
+ }
+
+ bitmap = scaleBitmap(getBitmapFromResult(intent));
+
// Add compressed version of captured image to returned media store Uri
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
bitmap.compress(Bitmap.CompressFormat.JPEG, this.mQuality, os);
@@ -390,7 +406,7 @@ public class CameraLauncher extends Plugin {
Uri uri = intent.getData();
android.content.ContentResolver resolver = this.cordova.getActivity().getContentResolver();
- // 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
// and there will be no attempt to resize any returned data
if (this.mediaType != PICTURE) {
this.success(new PluginResult(PluginResult.Status.OK, uri.toString()), this.callbackId);
@@ -447,7 +463,7 @@ public class CameraLauncher extends Plugin {
bitmap.recycle();
bitmap = null;
- // The resized image is cached by the app in order to get around this and not have to delete you
+ // The resized 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.success(new PluginResult(PluginResult.Status.OK, ("file://" + fileName + "?" + System.currentTimeMillis())), this.callbackId);
System.gc();
@@ -471,6 +487,19 @@ public class CameraLauncher extends Plugin {
}
}
+ private Bitmap getBitmapFromResult(Intent intent)
+ throws IOException, FileNotFoundException {
+ Bitmap bitmap = null;
+ try {
+ bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.ctx.getActivity().getContentResolver(), imageUri);
+ } catch (FileNotFoundException e) {
+ Uri uri = intent.getData();
+ android.content.ContentResolver resolver = this.ctx.getActivity().getContentResolver();
+ bitmap = android.graphics.BitmapFactory.decodeStream(resolver.openInputStream(uri));
+ }
+ return bitmap;
+ }
+
/**
* Creates a cursor that can be used to determine how many images we have.
*
http://git-wip-us.apache.org/repos/asf/incubator-cordova-android/blob/a691e9f7/framework/src/org/apache/cordova/Capture.java
----------------------------------------------------------------------
diff --git a/framework/src/org/apache/cordova/Capture.java b/framework/src/org/apache/cordova/Capture.java
index a2dfafd..cd115d4 100644
--- a/framework/src/org/apache/cordova/Capture.java
+++ b/framework/src/org/apache/cordova/Capture.java
@@ -19,6 +19,7 @@
package org.apache.cordova;
import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
@@ -32,10 +33,11 @@ import org.json.JSONObject;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Intent;
-import android.graphics.Bitmap;
+import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.media.MediaPlayer;
import android.net.Uri;
+import android.provider.MediaStore;
import android.util.Log;
public class Capture extends Plugin {
@@ -61,6 +63,7 @@ public class Capture extends Plugin {
private double duration; // optional duration parameter for video recording
private JSONArray results; // The array of results to be returned to the user
private Uri imageUri; // Uri of captured image
+ private int numPics; // Number of pictures before capture activity
//private CordovaInterface cordova;
@@ -202,6 +205,9 @@ public class Capture extends Plugin {
* Sets up an intent to capture images. Result handled by onActivityResult()
*/
private void captureImage() {
+ // Save the number of images currently on disk for later
+ this.numPics = queryImgDB().getCount();
+
Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
// Specify file so that large image is captured and returned
@@ -256,14 +262,6 @@ public class Capture extends Plugin {
// It crashes in the emulator and on my phone with a null pointer exception
// To work around it I had to grab the code from CameraLauncher.java
try {
- // Create an ExifHelper to save the exif data that is lost during compression
- ExifHelper exif = new ExifHelper();
- exif.createInFile(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
- exif.readExifData();
-
- // Read in bitmap of captured image
- Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(this.cordova.getActivity().getContentResolver(), imageUri);
-
// Create entry in media store for image
// (Don't use insertImage() because it uses default compression setting of 50 - no way to change it)
ContentValues values = new ContentValues();
@@ -281,23 +279,22 @@ public class Capture extends Plugin {
return;
}
}
-
- // Add compressed version of captured image to returned media store Uri
+ FileInputStream fis = new FileInputStream(DirectoryManager.getTempDirectoryPath(this.cordova.getActivity()) + "/Capture.jpg");
OutputStream os = this.cordova.getActivity().getContentResolver().openOutputStream(uri);
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
+ byte[] buffer = new byte[4096];
+ int len;
+ while ((len = fis.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ os.flush();
os.close();
-
- bitmap.recycle();
- bitmap = null;
- System.gc();
-
- // Restore exif data to file
- exif.createOutFile(FileUtils.getRealPathFromURI(uri, this.cordova));
- exif.writeExifData();
+ fis.close();
// Add image to results
results.put(createMediaFile(uri));
+ checkForDuplicateImage();
+
if (results.length() >= limit) {
// Send Uri back to JavaScript for viewing image
this.success(new PluginResult(PluginResult.Status.OK, results), this.callbackId);
@@ -405,4 +402,36 @@ public class Capture extends Plugin {
public void fail(JSONObject err) {
this.error(new PluginResult(PluginResult.Status.ERROR, err), this.callbackId);
}
+
+
+ /**
+ * Creates a cursor that can be used to determine how many images we have.
+ *
+ * @return a cursor
+ */
+ private Cursor queryImgDB() {
+ return this.cordova.getActivity().getContentResolver().query(
+ android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
+ new String[] { MediaStore.Images.Media._ID },
+ null,
+ null,
+ null);
+ }
+
+ /**
+ * Used to find out if we are in a situation where the Camera Intent adds to images
+ * to the content store.
+ */
+ private void checkForDuplicateImage() {
+ Cursor cursor = queryImgDB();
+ int currentNumOfImages = cursor.getCount();
+
+ // delete the duplicate file if the difference is 2
+ if ((currentNumOfImages - numPics) == 2) {
+ cursor.moveToLast();
+ int id = Integer.valueOf(cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media._ID))) - 1;
+ Uri uri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI + "/" + id);
+ this.cordova.getActivity().getContentResolver().delete(uri, null, null);
+ }
+ }
}