From 62f33d4d160045dce6759dead6599c2f67de9146 Mon Sep 17 00:00:00 2001 From: CSullivan102 Date: Fri, 29 Aug 2014 00:11:03 -0400 Subject: [PATCH 1/4] Catch out of memory exception when forced to rotate the bitmap, if out of memory, try to load a smaller subsample --- .../src/MultiImageChooserActivity.java | 53 +++++++++++++------ 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/src/android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java index e694460..8686c9a 100644 --- a/src/android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -493,6 +493,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi while(i.hasNext()) { Entry imageInfo = i.next(); File file = new File(imageInfo.getKey()); + int rotate = imageInfo.getValue().intValue(); BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 1; options.inJustDecodeBounds = true; @@ -506,35 +507,28 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi int inSampleSize = calculateInSampleSize(options, finalWidth, finalHeight); options = new BitmapFactory.Options(); options.inSampleSize = inSampleSize; - bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options); - if (bmp == null) { - throw new IOException("The image file could not be opened."); + try { + bmp = this.tryToGetBitmap(file, options, rotate, true); + } catch (OutOfMemoryError e) { + options.inSampleSize = calculateNextSampleSize(options.inSampleSize); + bmp = this.tryToGetBitmap(file, options, rotate, false); } - scale = calculateScale(options.outWidth, options.outHeight); - bmp = this.getResizedBitmap(bmp, scale); } else { try { - bmp = BitmapFactory.decodeFile(file.getAbsolutePath()); + bmp = this.tryToGetBitmap(file, null, rotate, false); } catch(OutOfMemoryError e) { options = new BitmapFactory.Options(); options.inSampleSize = 2; try { - bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options); + bmp = this.tryToGetBitmap(file, options, rotate, false); } catch(OutOfMemoryError e2) { options = new BitmapFactory.Options(); options.inSampleSize = 4; - bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options); + bmp = this.tryToGetBitmap(file, options, rotate, false); } } } - - int rotate = imageInfo.getValue().intValue(); - if (rotate != 0) { - Matrix matrix = new Matrix(); - matrix.setRotate(rotate); - bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true); - } - + file = this.storeImage(bmp, file.getName()); al.add(Uri.fromFile(file).toString()); } @@ -573,6 +567,28 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi progress.dismiss(); finish(); } + + private Bitmap tryToGetBitmap(File file, BitmapFactory.Options options, int rotate, boolean shouldScale) throws IOException, OutOfMemoryError { + Bitmap bmp; + if (options == null) { + bmp = BitmapFactory.decodeFile(file.getAbsolutePath()); + } else { + bmp = BitmapFactory.decodeFile(file.getAbsolutePath(), options); + } + if (bmp == null) { + throw new IOException("The image file could not be opened."); + } + if (options != null && shouldScale) { + float scale = calculateScale(options.outWidth, options.outHeight); + bmp = this.getResizedBitmap(bmp, scale); + } + if (rotate != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(rotate); + bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true); + } + return bmp; + } /* * The following functions are originally from @@ -631,6 +647,11 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi return inSampleSize; } + + private int calculateNextSampleSize(int sampleSize) { + double logBaseTwo = (int)(Math.log(sampleSize) / Math.log(2)); + return (int)Math.pow(logBaseTwo + 1, 2); + } private float calculateScale(int width, int height) { float widthScale = 1.0f; From 6e3437bac21df99606f69fc78b8589b06e4ce16a Mon Sep 17 00:00:00 2001 From: CSullivan102 Date: Fri, 29 Aug 2014 10:48:23 -0400 Subject: [PATCH 2/4] If the smallest sample we want to use still can't be loaded, throw an IOException for now to avoid crashing --- .../Library/src/MultiImageChooserActivity.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java index 8686c9a..334a6ad 100644 --- a/src/android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -511,7 +511,11 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi bmp = this.tryToGetBitmap(file, options, rotate, true); } catch (OutOfMemoryError e) { options.inSampleSize = calculateNextSampleSize(options.inSampleSize); - bmp = this.tryToGetBitmap(file, options, rotate, false); + try { + bmp = this.tryToGetBitmap(file, options, rotate, false); + } catch (OutOfMemoryError e2) { + throw new IOException("Unable to load image into memory."); + } } } else { try { @@ -524,7 +528,11 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi } catch(OutOfMemoryError e2) { options = new BitmapFactory.Options(); options.inSampleSize = 4; - bmp = this.tryToGetBitmap(file, options, rotate, false); + try { + bmp = this.tryToGetBitmap(file, options, rotate, false); + } catch (OutOfMemoryError e3) { + throw new IOException("Unable to load image into memory."); + } } } } From 9ed29afefdd7f7d51fa90cfc0df0552baf53b48e Mon Sep 17 00:00:00 2001 From: CSullivan102 Date: Thu, 4 Sep 2014 09:27:22 -0400 Subject: [PATCH 3/4] Added the ability to return errors in resizeImagesTask, so that failed attempts to import will call return as errors, differently than cancelling. Also some whitespace fixing. --- .../src/MultiImageChooserActivity.java | 14 ++++- .../synconset/ImagePicker/ImagePicker.java | 57 ++++++++++--------- 2 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java index 334a6ad..5263277 100644 --- a/src/android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -483,6 +483,8 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi private class ResizeImagesTask extends AsyncTask>, Void, ArrayList> { + private Exception asyncTaskError = null; + @Override protected ArrayList doInBackground(Set>... fileSets) { Set> fileNames = fileSets[0]; @@ -543,6 +545,7 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi return al; } catch(IOException e) { try { + asyncTaskError = e; for (int i = 0; i < al.size(); i++) { URI uri = new URI(al.get(i)); File file = new File(uri); @@ -559,19 +562,24 @@ public class MultiImageChooserActivity extends Activity implements OnItemClickLi @Override protected void onPostExecute(ArrayList al) { Intent data = new Intent(); - - if (al.size() > 0) { + + if (asyncTaskError != null) { + Bundle res = new Bundle(); + res.putString("ERRORMESSAGE", asyncTaskError.getMessage()); + data.putExtras(res); + setResult(RESULT_CANCELED, data); + } else if (al.size() > 0) { Bundle res = new Bundle(); res.putStringArrayList("MULTIPLEFILENAMES", al); if (imagecursor != null) { res.putInt("TOTALFILES", imagecursor.getCount()); } - data.putExtras(res); setResult(RESULT_OK, data); } else { setResult(RESULT_CANCELED, data); } + progress.dismiss(); finish(); } diff --git a/src/android/com/synconset/ImagePicker/ImagePicker.java b/src/android/com/synconset/ImagePicker/ImagePicker.java index 4a3874f..adfc969 100644 --- a/src/android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/android/com/synconset/ImagePicker/ImagePicker.java @@ -17,53 +17,56 @@ import android.content.Intent; import android.util.Log; public class ImagePicker extends CordovaPlugin { - public static String TAG = "ImagePicker"; - - private CallbackContext callbackContext; - private JSONObject params; - + public static String TAG = "ImagePicker"; + + private CallbackContext callbackContext; + private JSONObject params; + public boolean execute(String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { - this.callbackContext = callbackContext; - this.params = args.getJSONObject(0); + this.callbackContext = callbackContext; + this.params = args.getJSONObject(0); if (action.equals("getPictures")) { Intent intent = new Intent(cordova.getActivity(), MultiImageChooserActivity.class); int max = 20; int desiredWidth = 0; - int desiredHeight = 0; - int quality = 100; + int desiredHeight = 0; + int quality = 100; if (this.params.has("maximumImagesCount")) { - max = this.params.getInt("maximumImagesCount"); + max = this.params.getInt("maximumImagesCount"); } if (this.params.has("width")) { - desiredWidth = this.params.getInt("width"); + desiredWidth = this.params.getInt("width"); } if (this.params.has("height")) { - desiredWidth = this.params.getInt("height"); + desiredWidth = this.params.getInt("height"); } if (this.params.has("quality")) { - quality = this.params.getInt("quality"); - } + quality = this.params.getInt("quality"); + } intent.putExtra("MAX_IMAGES", max); intent.putExtra("WIDTH", desiredWidth); intent.putExtra("HEIGHT", desiredHeight); intent.putExtra("QUALITY", quality); - if (this.cordova != null) { - this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); - } + if (this.cordova != null) { + this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0); + } } return true; } public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (resultCode == Activity.RESULT_OK && data != null) { - ArrayList fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES"); - JSONArray res = new JSONArray(fileNames); - this.callbackContext.success(res); - } else if (resultCode == Activity.RESULT_CANCELED) { - JSONArray res = new JSONArray(); - this.callbackContext.success(res); - } else { - this.callbackContext.error("No images selected"); - } + if (resultCode == Activity.RESULT_OK && data != null) { + ArrayList fileNames = data.getStringArrayListExtra("MULTIPLEFILENAMES"); + JSONArray res = new JSONArray(fileNames); + this.callbackContext.success(res); + } else if (resultCode == Activity.RESULT_CANCELED && data != null) { + String error = data.getStringExtra("ERRORMESSAGE"); + this.callbackContext.error("An error occurred loading one of the selected images"); + } else if (resultCode == Activity.RESULT_CANCELED) { + JSONArray res = new JSONArray(); + this.callbackContext.success(res); + } else { + this.callbackContext.error("No images selected"); + } } } \ No newline at end of file From 1f60a6dd0834044f94fe127d3bcad4a03bfd395c Mon Sep 17 00:00:00 2001 From: CSullivan102 Date: Thu, 4 Sep 2014 09:39:50 -0400 Subject: [PATCH 4/4] Pass back the actual error string when an error occurs picking images --- src/android/com/synconset/ImagePicker/ImagePicker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/android/com/synconset/ImagePicker/ImagePicker.java b/src/android/com/synconset/ImagePicker/ImagePicker.java index adfc969..2f4939a 100644 --- a/src/android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/android/com/synconset/ImagePicker/ImagePicker.java @@ -61,7 +61,7 @@ public class ImagePicker extends CordovaPlugin { this.callbackContext.success(res); } else if (resultCode == Activity.RESULT_CANCELED && data != null) { String error = data.getStringExtra("ERRORMESSAGE"); - this.callbackContext.error("An error occurred loading one of the selected images"); + this.callbackContext.error(error); } else if (resultCode == Activity.RESULT_CANCELED) { JSONArray res = new JSONArray(); this.callbackContext.success(res);