From 04887af28e380f7f4e2662f94f68893f7c2b1939 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Fri, 7 Jul 2017 14:26:21 +0200 Subject: [PATCH] Use FileProvider to prevent exception when cliking on image + fixed image preview orientation + improved bitmap memory management --- AndroidManifest.xml | 10 +++ build.gradle | 2 +- liblinphone_tester/build.gradle | 2 +- res/xml/provider_paths.xml | 4 ++ src/android/org/linphone/ChatFragment.java | 72 +++++++++++++++++++--- 5 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 res/xml/provider_paths.xml diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3b02918e6..9ee95b627 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -287,6 +287,16 @@ + + + + diff --git a/build.gradle b/build.gradle index 29272e5b8..a3f7fcee5 100644 --- a/build.gradle +++ b/build.gradle @@ -18,7 +18,7 @@ buildscript { mavenLocal() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:2.3.3' if (googleFile.exists()) { classpath 'com.google.gms:google-services:3.0.0' } diff --git a/liblinphone_tester/build.gradle b/liblinphone_tester/build.gradle index 80ee5601e..8b3ab56ac 100644 --- a/liblinphone_tester/build.gradle +++ b/liblinphone_tester/build.gradle @@ -12,7 +12,7 @@ buildscript { mavenLocal() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.0' + classpath 'com.android.tools.build:gradle:2.3.3' } } diff --git a/res/xml/provider_paths.xml b/res/xml/provider_paths.xml new file mode 100644 index 000000000..ffa74ab56 --- /dev/null +++ b/res/xml/provider_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/android/org/linphone/ChatFragment.java b/src/android/org/linphone/ChatFragment.java index 7ce7a3c3f..2dfcee169 100644 --- a/src/android/org/linphone/ChatFragment.java +++ b/src/android/org/linphone/ChatFragment.java @@ -47,6 +47,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.Parcelable; import android.provider.MediaStore; +import android.support.v4.content.FileProvider; import android.text.Editable; import android.text.Spanned; import android.text.TextWatcher; @@ -97,6 +98,8 @@ import java.util.Calendar; import java.util.List; import java.util.Locale; +import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; + public class ChatFragment extends Fragment implements OnClickListener, LinphoneChatMessage.LinphoneChatMessageListener { private static final int ADD_PHOTO = 1337; @@ -876,11 +879,18 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC @Override protected byte[] doInBackground(Bitmap... params) { Bitmap bm = params[0]; + Bitmap bm_tmp = null; if (bm.getWidth() >= bm.getHeight() && bm.getWidth() > SIZE_MAX) { - bm = Bitmap.createScaledBitmap(bm, SIZE_MAX, (SIZE_MAX * bm.getHeight()) / bm.getWidth(), false); + bm_tmp = Bitmap.createScaledBitmap(bm, SIZE_MAX, (SIZE_MAX * bm.getHeight()) / bm.getWidth(), false); + } else if (bm.getHeight() >= bm.getWidth() && bm.getHeight() > SIZE_MAX) { - bm = Bitmap.createScaledBitmap(bm, (SIZE_MAX * bm.getWidth()) / bm.getHeight(), SIZE_MAX, false); + bm_tmp = Bitmap.createScaledBitmap(bm, (SIZE_MAX * bm.getWidth()) / bm.getHeight(), SIZE_MAX, false); + } + + if (bm_tmp != null) { + bm.recycle(); + bm = bm_tmp; } // Rotate the bitmap if possible/needed, using EXIF data @@ -896,12 +906,17 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } else if (pictureOrientation == 8) { matrix.postRotate(270); } - bm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + bm_tmp = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); } } catch (Exception e) { Log.e(e); } + if (bm_tmp != null) { + bm.recycle(); + bm = bm_tmp; + } + ByteArrayOutputStream stream = new ByteArrayOutputStream(); String extension = LinphoneUtils.getExtensionFromFileName(path); if (extension != null && extension.toLowerCase(Locale.getDefault()).equals("png")) { @@ -909,6 +924,14 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } else { bm.compress(Bitmap.CompressFormat.JPEG, 100, stream); } + + if (bm_tmp != null) { + bm_tmp.recycle(); + bm_tmp = null; + } + bm.recycle(); + bm = null; + byte[] byteArray = stream.toByteArray(); return byteArray; } @@ -1494,6 +1517,7 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC protected Bitmap doInBackground(String... params) { path = params[0]; Bitmap bm = null; + Bitmap thumbnail = null; if (path.startsWith("content")) { try { @@ -1505,13 +1529,33 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC } } else { bm = BitmapFactory.decodeFile(path); - path = "file://" + path; + } + + // Rotate the bitmap if possible/needed, using EXIF data + try { + Bitmap bm_tmp; + ExifInterface exif = new ExifInterface(path); + int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); + Matrix matrix = new Matrix(); + if (pictureOrientation == 6) { + matrix.postRotate(90); + } else if (pictureOrientation == 3) { + matrix.postRotate(180); + } else if (pictureOrientation == 8) { + matrix.postRotate(270); + } + bm_tmp = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + bm.recycle(); + bm = bm_tmp; + } catch (Exception e) { + Log.e(e); } if (bm != null) { - bm = ThumbnailUtils.extractThumbnail(bm, SIZE_MAX, SIZE_MAX); + thumbnail = ThumbnailUtils.extractThumbnail(bm, SIZE_SMALL, SIZE_SMALL); + bm.recycle(); } - return bm; + return thumbnail; } // Once complete, see if ImageView is still around and set bitmap. @@ -1531,7 +1575,21 @@ public class ChatFragment extends Fragment implements OnClickListener, LinphoneC @Override public void onClick(View v) { Intent intent = new Intent(Intent.ACTION_VIEW); - intent.setDataAndType(Uri.parse((String)v.getTag()), "image/*"); + + Uri contentUri = null; + String imageUri = (String)v.getTag(); + if (imageUri.startsWith("file://")) { + imageUri = imageUri.substring("file://".length()); + File file = new File(imageUri); + contentUri = FileProvider.getUriForFile(getActivity(), "org.linphone.provider", file); + } else if (imageUri.startsWith("content://")) { + contentUri = Uri.parse(imageUri); + } else { + File file = new File(imageUri); + contentUri = FileProvider.getUriForFile(getActivity(), "org.linphone.provider", file); + } + intent.setDataAndType(contentUri, "image/*"); + intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); context.startActivity(intent); } });