From ac12b1a7eafd2d9137887181a75ab9e0f2813bd6 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Tue, 3 Dec 2019 15:57:54 +0100 Subject: [PATCH] Fixed contact picture edition quality & rotation --- .../org/linphone/contacts/AndroidContact.java | 72 ++++++++++++------- .../contacts/ContactEditorFragment.java | 57 +++++++++++---- .../linphone/contacts/LinphoneContact.java | 4 ++ .../java/org/linphone/utils/ImageUtils.java | 11 +++ 4 files changed, 107 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/org/linphone/contacts/AndroidContact.java b/app/src/main/java/org/linphone/contacts/AndroidContact.java index 69239d0ce..fba283b2c 100644 --- a/app/src/main/java/org/linphone/contacts/AndroidContact.java +++ b/app/src/main/java/org/linphone/contacts/AndroidContact.java @@ -23,6 +23,7 @@ import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; import android.content.ContentUris; +import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; @@ -30,6 +31,8 @@ import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; +import java.io.IOException; +import java.io.OutputStream; import java.io.Serializable; import java.util.ArrayList; import org.linphone.LinphoneContext; @@ -41,10 +44,12 @@ class AndroidContact implements Serializable { private String mAndroidRawId; private boolean isAndroidRawIdLinphone; private final transient ArrayList mChangesToCommit; + private byte[] mTempPicture; AndroidContact() { mChangesToCommit = new ArrayList<>(); isAndroidRawIdLinphone = false; + mTempPicture = null; } String getAndroidId() { @@ -78,6 +83,12 @@ class AndroidContact implements Serializable { String rawId = String.valueOf(ContentUris.parseId(results[0].uri)); if (mAndroidId == null) { Log.i("[Contact] Contact created with RAW ID " + rawId); + mAndroidRawId = rawId; + if (mTempPicture != null) { + Log.i( + "[Contact] Contact has been created, raw is is available, time to set the photo"); + setPhoto(mTempPicture); + } final String[] projection = new String[] {ContactsContract.RawContacts.CONTACT_ID}; @@ -551,33 +562,44 @@ class AndroidContact implements Serializable { return; } - if (mAndroidId == null) { - Log.i("[Contact] Setting picture to new contact."); - addChangesToCommit( - ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) - .withValue( - ContactsContract.Data.MIMETYPE, - ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo) - .build()); + if (mAndroidRawId == null) { + Log.w("[Contact] Can't set picture for not already created contact, will do it later"); + mTempPicture = photo; } else { Log.i( - "[Contact] Setting picture to existing contact " - + mAndroidId - + " (" - + mAndroidRawId - + ")"); - addChangesToCommit( - ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) - .withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId) - .withValue( - ContactsContract.Data.MIMETYPE, - ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE) - .withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo) - .withValue(ContactsContract.Data.IS_PRIMARY, 1) - .withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) - .build()); + "[Contact] Setting picture to an already created raw contact [", + mAndroidRawId, + "]"); + try { + long rawId = Long.parseLong(mAndroidRawId); + + Uri rawContactPhotoUri = + Uri.withAppendedPath( + ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawId), + RawContacts.DisplayPhoto.CONTENT_DIRECTORY); + + if (rawContactPhotoUri != null) { + ContentResolver resolver = + LinphoneContext.instance().getApplicationContext().getContentResolver(); + AssetFileDescriptor fd = + resolver.openAssetFileDescriptor(rawContactPhotoUri, "rw"); + OutputStream os = fd.createOutputStream(); + os.write(photo); + os.close(); + fd.close(); + } else { + Log.e( + "[Contact] Failed to get raw contact photo URI for raw contact id [", + rawId, + "], aborting"); + } + } catch (NumberFormatException nfe) { + Log.e("[Contact] Couldn't parse raw id [", mAndroidId, "], aborting"); + } catch (IOException ioe) { + Log.e("[Contact] Couldn't set picture, IO error: ", ioe); + } catch (Exception e) { + Log.e("[Contact] Couldn't set picture, unknown error: ", e); + } } } diff --git a/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java b/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java index 00f2465ab..238681f51 100644 --- a/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java +++ b/app/src/main/java/org/linphone/contacts/ContactEditorFragment.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.media.ExifInterface; import android.net.Uri; import android.os.Bundle; import android.os.Parcelable; @@ -398,15 +399,18 @@ public class ContactEditorFragment extends Fragment { editContactPicture(null, bm); } else if (data != null && data.getData() != null) { Uri selectedImageUri = data.getData(); - try { - Bitmap selectedImage = - MediaStore.Images.Media.getBitmap( - getActivity().getContentResolver(), selectedImageUri); - selectedImage = - Bitmap.createScaledBitmap(selectedImage, PHOTO_SIZE, PHOTO_SIZE, false); - editContactPicture(null, selectedImage); - } catch (IOException e) { - Log.e(e); + String filePath = FileUtils.getRealPathFromURI(getActivity(), selectedImageUri); + if (filePath != null) { + editContactPicture(filePath, null); + } else { + try { + Bitmap selectedImage = + MediaStore.Images.Media.getBitmap( + getActivity().getContentResolver(), selectedImageUri); + editContactPicture(null, selectedImage); + } catch (IOException e) { + Log.e("[Contact Editor] IO error: ", e); + } } } else if (mPickedPhotoForContactUri != null) { String filePath = mPickedPhotoForContactUri.getPath(); @@ -478,23 +482,52 @@ public class ContactEditorFragment extends Fragment { } private void editContactPicture(String filePath, Bitmap image) { + int orientation = ExifInterface.ORIENTATION_NORMAL; + if (image == null) { Log.i( "[Contact Editor] Bitmap is null, trying to decode image from file [", filePath, "]"); image = BitmapFactory.decodeFile(filePath); + + try { + ExifInterface ei = new ExifInterface(filePath); + orientation = + ei.getAttributeInt( + ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); + Log.i("[Contact Editor] Exif rotation is ", orientation); + } catch (IOException e) { + Log.e("[Contact Editor] Failed to get Exif rotation, error is ", e); + } + } else { + } if (image == null) { Log.e( "[Contact Editor] Couldn't get bitmap from either filePath [", filePath, - "] nor image [", - image, - "]"); + "] nor image"); return; } + switch (orientation) { + case ExifInterface.ORIENTATION_ROTATE_90: + image = ImageUtils.rotateImage(image, 90); + break; + case ExifInterface.ORIENTATION_ROTATE_180: + image = ImageUtils.rotateImage(image, 180); + break; + case ExifInterface.ORIENTATION_ROTATE_270: + image = ImageUtils.rotateImage(image, 270); + break; + case ExifInterface.ORIENTATION_NORMAL: + // Nothing to do + break; + default: + Log.w("[Contact Editor] Unexpected orientation ", orientation); + } + ByteArrayOutputStream stream = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, stream); mPhotoToAdd = stream.toByteArray(); diff --git a/app/src/main/java/org/linphone/contacts/LinphoneContact.java b/app/src/main/java/org/linphone/contacts/LinphoneContact.java index d9dee45fe..b4641b986 100644 --- a/app/src/main/java/org/linphone/contacts/LinphoneContact.java +++ b/app/src/main/java/org/linphone/contacts/LinphoneContact.java @@ -640,6 +640,10 @@ public class LinphoneContact extends AndroidContact public void save() { saveChangesCommited(); + if (getAndroidId() != null) { + setThumbnailUri(getContactThumbnailPictureUri()); + setPhotoUri(getContactPictureUri()); + } syncValuesFromAndroidContact(LinphoneContext.instance().getApplicationContext()); createOrUpdateFriend(); } diff --git a/app/src/main/java/org/linphone/utils/ImageUtils.java b/app/src/main/java/org/linphone/utils/ImageUtils.java index 844bfd781..0053af9e1 100644 --- a/app/src/main/java/org/linphone/utils/ImageUtils.java +++ b/app/src/main/java/org/linphone/utils/ImageUtils.java @@ -22,6 +22,7 @@ package org.linphone.utils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; @@ -85,4 +86,14 @@ public class ImageUtils { / ((float) context.getResources().getDisplayMetrics().densityDpi / DisplayMetrics.DENSITY_DEFAULT); } + + public static Bitmap rotateImage(Bitmap source, float angle) { + Matrix matrix = new Matrix(); + matrix.postRotate(angle); + Bitmap rotatedBitmap = + Bitmap.createBitmap( + source, 0, 0, source.getWidth(), source.getHeight(), matrix, true); + source.recycle(); + return rotatedBitmap; + } }