From 07e23c65c72493bfc715778f520ffb0fbf8b8853 Mon Sep 17 00:00:00 2001 From: Tomasz Paczesny Date: Fri, 25 Aug 2023 15:20:04 +0200 Subject: [PATCH 1/5] Android SDK 33 support - READ_MEDIA_IMAGES permission used instead of READ_EXTERNAL_STORAGE. Requires Target SDK 33 or newer. --- plugin.xml | 1 + src/android/com/synconset/ImagePicker/ImagePicker.java | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/plugin.xml b/plugin.xml index e9d0e62c..1ee7875d 100644 --- a/plugin.xml +++ b/plugin.xml @@ -108,6 +108,7 @@ + diff --git a/src/android/com/synconset/ImagePicker/ImagePicker.java b/src/android/com/synconset/ImagePicker/ImagePicker.java index b534736a..46031bbe 100644 --- a/src/android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/android/com/synconset/ImagePicker/ImagePicker.java @@ -77,13 +77,13 @@ public boolean execute(String action, final JSONArray args, final CallbackContex // some day, when everybody uses a cordova version supporting 'hasPermission', enable this: /* if (cordova != null) { - if (cordova.hasPermission(Manifest.permission.READ_EXTERNAL_STORAGE)) { + if (cordova.hasPermission(Manifest.permission.READ_MEDIA_IMAGES)) { cordova.startActivityForResult(this, imagePickerIntent, 0); } else { cordova.requestPermission( this, PERMISSION_REQUEST_CODE, - Manifest.permission.READ_EXTERNAL_STORAGE + Manifest.permission.READ_MEDIA_IMAGES ); } } @@ -105,7 +105,7 @@ public boolean execute(String action, final JSONArray args, final CallbackContex @SuppressLint("InlinedApi") private boolean hasReadPermission() { return Build.VERSION.SDK_INT < 23 || - PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this.cordova.getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE); + PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this.cordova.getActivity(), Manifest.permission.READ_MEDIA_IMAGES); } @SuppressLint("InlinedApi") @@ -113,7 +113,7 @@ private void requestReadPermission() { if (!hasReadPermission()) { ActivityCompat.requestPermissions( this.cordova.getActivity(), - new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, + new String[] {Manifest.permission.READ_MEDIA_IMAGES}, PERMISSION_REQUEST_CODE); } // This method executes async and we seem to have no known way to receive the result From 306f8e5b4b70de0466427ce716c888db711b7ff0 Mon Sep 17 00:00:00 2001 From: Tomasz Paczesny Date: Sat, 26 Aug 2023 12:12:11 +0200 Subject: [PATCH 2/5] Change dependency to androidx --- src/android/Library/src/MultiImageChooserActivity.java | 4 ++-- src/android/com/synconset/ImagePicker/ImagePicker.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java index ba379e3b..6c9c78b5 100644 --- a/src/android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -62,8 +62,8 @@ import android.os.AsyncTask; import android.os.Bundle; import android.provider.MediaStore; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.app.ActionBar; import android.util.Base64; import android.util.SparseBooleanArray; import android.view.Display; diff --git a/src/android/com/synconset/ImagePicker/ImagePicker.java b/src/android/com/synconset/ImagePicker/ImagePicker.java index 46031bbe..709b9e46 100644 --- a/src/android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/android/com/synconset/ImagePicker/ImagePicker.java @@ -20,8 +20,8 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; +import androidx.core.app.ActivityCompat; +import androidx.core.content.ContextCompat; public class ImagePicker extends CordovaPlugin { From 910ea6d4bf0f480807cc337c1e5fa6c1bd3f8cc9 Mon Sep 17 00:00:00 2001 From: Tomasz Paczesny Date: Fri, 15 Sep 2023 11:24:33 +0200 Subject: [PATCH 3/5] Still use READ_EXTERNAL_STORAGE permission for Android API < 33 --- src/android/com/synconset/ImagePicker/ImagePicker.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/android/com/synconset/ImagePicker/ImagePicker.java b/src/android/com/synconset/ImagePicker/ImagePicker.java index 709b9e46..41d3fa6b 100644 --- a/src/android/com/synconset/ImagePicker/ImagePicker.java +++ b/src/android/com/synconset/ImagePicker/ImagePicker.java @@ -102,10 +102,14 @@ public boolean execute(String action, final JSONArray args, final CallbackContex return false; } + private String getMediaPermissionName() { + return Build.VERSION.SDK_INT >= 33 ? Manifest.permission.READ_MEDIA_IMAGES : Manifest.permission.READ_EXTERNAL_STORAGE; + } + @SuppressLint("InlinedApi") private boolean hasReadPermission() { return Build.VERSION.SDK_INT < 23 || - PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this.cordova.getActivity(), Manifest.permission.READ_MEDIA_IMAGES); + PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(this.cordova.getActivity(), this.getMediaPermissionName()); } @SuppressLint("InlinedApi") @@ -113,7 +117,7 @@ private void requestReadPermission() { if (!hasReadPermission()) { ActivityCompat.requestPermissions( this.cordova.getActivity(), - new String[] {Manifest.permission.READ_MEDIA_IMAGES}, + new String[] {this.getMediaPermissionName()}, PERMISSION_REQUEST_CODE); } // This method executes async and we seem to have no known way to receive the result From 368b9e565c77549081748b2751efb6af3ea3a0ac Mon Sep 17 00:00:00 2001 From: Tomasz Paczesny Date: Tue, 9 Jan 2024 10:19:36 +0100 Subject: [PATCH 4/5] Remove appocompat library reference - can't be used with Capacitor 5 --- plugin.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/plugin.xml b/plugin.xml index 1ee7875d..a75c1e18 100644 --- a/plugin.xml +++ b/plugin.xml @@ -111,9 +111,6 @@ - - - From 7ad2ec65af6e13176d7fe4c1aac47fb39110ae40 Mon Sep 17 00:00:00 2001 From: Tomasz Paczesny Date: Thu, 9 May 2024 19:53:52 +0200 Subject: [PATCH 5/5] Improve decoding bitmaps by working with a InputStream via ContentResolver instead of File --- LICENSE | 1 + package.json | 2 +- plugin.xml | 2 +- .../src/MultiImageChooserActivity.java | 123 ++++++++++-------- 4 files changed, 74 insertions(+), 54 deletions(-) diff --git a/LICENSE b/LICENSE index ea69cf50..891d80db 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2014 CSullivan102 +(with modification by Tomasz Paczesny 2023) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/package.json b/package.json index 9dec585a..ddf3550e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "2.3.6", + "version": "2.3.7", "name": "cordova-plugin-telerik-imagepicker", "cordova_name": "ImagePicker", "description": "This plugin allows selection of multiple images from the camera roll / gallery in a phonegap app", diff --git a/plugin.xml b/plugin.xml index a75c1e18..0f0128a8 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="2.3.7"> ImagePicker diff --git a/src/android/Library/src/MultiImageChooserActivity.java b/src/android/Library/src/MultiImageChooserActivity.java index 6c9c78b5..af36ebe0 100644 --- a/src/android/Library/src/MultiImageChooserActivity.java +++ b/src/android/Library/src/MultiImageChooserActivity.java @@ -3,25 +3,25 @@ * * All rights reserved. * - * Redistribution and use in source and binary forms, with or without + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * - * Redistributions of source code must retain the above copyright notice, + * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDIN G NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDIN G NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE * * Code modified by Andrew Stephan for Sync OnSet @@ -30,6 +30,7 @@ package com.synconset; +import java.io.InputStream; import java.net.URI; import java.io.ByteArrayOutputStream; import java.io.File; @@ -218,9 +219,9 @@ public void onClick(DialogInterface dialog, int which) { ImageView imageView = (ImageView) view; if (android.os.Build.VERSION.SDK_INT >= 16) { - imageView.setImageAlpha(128); + imageView.setImageAlpha(128); } else { - imageView.setAlpha(128); + imageView.setAlpha(128); } view.setBackgroundColor(selectedColor); @@ -424,12 +425,12 @@ public boolean isChecked(int position) { /********************* - * Nested Classes - ********************/ + * Nested Classes + ********************/ private class SquareImageView extends ImageView { public SquareImageView(Context context) { - super(context); - } + super(context); + } @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -481,18 +482,18 @@ public View getView(int position, View convertView, ViewGroup parent) { if (isChecked(position)) { if (android.os.Build.VERSION.SDK_INT >= 16) { - imageView.setImageAlpha(128); + imageView.setImageAlpha(128); } else { - imageView.setAlpha(128); + imageView.setAlpha(128); } imageView.setBackgroundColor(selectedColor); } else { if (android.os.Build.VERSION.SDK_INT >= 16) { - imageView.setImageAlpha(255); + imageView.setImageAlpha(255); } else { - imageView.setAlpha(255); + imageView.setAlpha(255); } imageView.setBackgroundColor(Color.TRANSPARENT); } @@ -522,7 +523,17 @@ protected ArrayList doInBackground(Set>... fileSe BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 1; options.inJustDecodeBounds = true; - BitmapFactory.decodeFile(file.getAbsolutePath(), options); + + InputStream is = null; + try { + is = getContentResolver().openInputStream(Uri.fromFile(file)); + BitmapFactory.decodeStream(is, null, options); + } finally { + if (is != null) { + is.close(); + } + } + int width = options.outWidth; int height = options.outHeight; float scale = calculateScale(width, height); @@ -624,40 +635,48 @@ 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); - } + InputStream is = null; + try { + is = getContentResolver().openInputStream(Uri.fromFile(file)); + Bitmap bmp; + if (options == null) { + bmp = BitmapFactory.decodeStream(is); + } else { + bmp = BitmapFactory.decodeStream(is, null, options); + } - if (bmp == null) { - throw new IOException("The image file could not be opened."); - } + 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 (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); - } + if (rotate != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(rotate); + bmp = Bitmap.createBitmap(bmp, 0, 0, bmp.getWidth(), bmp.getHeight(), matrix, true); + } - return bmp; + return bmp; + } finally { + if (is != null) { + is.close(); + } + } } /* - * The following functions are originally from - * https://github.com/raananw/PhoneGap-Image-Resizer - * - * They have been modified by Andrew Stephan for Sync OnSet - * - * The software is open source, MIT Licensed. - * Copyright (C) 2012, webXells GmbH All Rights Reserved. - */ + * The following functions are originally from + * https://github.com/raananw/PhoneGap-Image-Resizer + * + * They have been modified by Andrew Stephan for Sync OnSet + * + * The software is open source, MIT Licensed. + * Copyright (C) 2012, webXells GmbH All Rights Reserved. + */ private File storeImage(Bitmap bmp, String fileName) throws IOException { int index = fileName.lastIndexOf('.'); String name = fileName.substring(0, index); @@ -687,7 +706,7 @@ private Bitmap getResizedBitmap(Bitmap bm, float factor) { return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false); } - private String getBase64OfImage(Bitmap bm) { + private String getBase64OfImage(Bitmap bm) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.JPEG, quality, byteArrayOutputStream); byte[] byteArray = byteArrayOutputStream.toByteArray();