Fixed contact picture edition quality & rotation

This commit is contained in:
Sylvain Berfini 2019-12-03 15:57:54 +01:00
parent 7caff1175f
commit ac12b1a7ea
4 changed files with 107 additions and 37 deletions

View file

@ -23,6 +23,7 @@ import android.content.ContentProviderOperation;
import android.content.ContentProviderResult; import android.content.ContentProviderResult;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentUris; import android.content.ContentUris;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri; import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
@ -30,6 +31,8 @@ import android.provider.ContactsContract.CommonDataKinds;
import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data; import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.RawContacts;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import org.linphone.LinphoneContext; import org.linphone.LinphoneContext;
@ -41,10 +44,12 @@ class AndroidContact implements Serializable {
private String mAndroidRawId; private String mAndroidRawId;
private boolean isAndroidRawIdLinphone; private boolean isAndroidRawIdLinphone;
private final transient ArrayList<ContentProviderOperation> mChangesToCommit; private final transient ArrayList<ContentProviderOperation> mChangesToCommit;
private byte[] mTempPicture;
AndroidContact() { AndroidContact() {
mChangesToCommit = new ArrayList<>(); mChangesToCommit = new ArrayList<>();
isAndroidRawIdLinphone = false; isAndroidRawIdLinphone = false;
mTempPicture = null;
} }
String getAndroidId() { String getAndroidId() {
@ -78,6 +83,12 @@ class AndroidContact implements Serializable {
String rawId = String.valueOf(ContentUris.parseId(results[0].uri)); String rawId = String.valueOf(ContentUris.parseId(results[0].uri));
if (mAndroidId == null) { if (mAndroidId == null) {
Log.i("[Contact] Contact created with RAW ID " + rawId); 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 = final String[] projection =
new String[] {ContactsContract.RawContacts.CONTACT_ID}; new String[] {ContactsContract.RawContacts.CONTACT_ID};
@ -551,33 +562,44 @@ class AndroidContact implements Serializable {
return; return;
} }
if (mAndroidId == null) { if (mAndroidRawId == null) {
Log.i("[Contact] Setting picture to new contact."); Log.w("[Contact] Can't set picture for not already created contact, will do it later");
addChangesToCommit( mTempPicture = photo;
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());
} else { } else {
Log.i( Log.i(
"[Contact] Setting picture to existing contact " "[Contact] Setting picture to an already created raw contact [",
+ mAndroidId mAndroidRawId,
+ " (" "]");
+ mAndroidRawId try {
+ ")"); long rawId = Long.parseLong(mAndroidRawId);
addChangesToCommit(
ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) Uri rawContactPhotoUri =
.withValue(ContactsContract.Data.RAW_CONTACT_ID, mAndroidRawId) Uri.withAppendedPath(
.withValue( ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawId),
ContactsContract.Data.MIMETYPE, RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Photo.PHOTO, photo) if (rawContactPhotoUri != null) {
.withValue(ContactsContract.Data.IS_PRIMARY, 1) ContentResolver resolver =
.withValue(ContactsContract.Data.IS_SUPER_PRIMARY, 1) LinphoneContext.instance().getApplicationContext().getContentResolver();
.build()); 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);
}
} }
} }

View file

@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.ExifInterface;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
@ -398,15 +399,18 @@ public class ContactEditorFragment extends Fragment {
editContactPicture(null, bm); editContactPicture(null, bm);
} else if (data != null && data.getData() != null) { } else if (data != null && data.getData() != null) {
Uri selectedImageUri = data.getData(); Uri selectedImageUri = data.getData();
try { String filePath = FileUtils.getRealPathFromURI(getActivity(), selectedImageUri);
Bitmap selectedImage = if (filePath != null) {
MediaStore.Images.Media.getBitmap( editContactPicture(filePath, null);
getActivity().getContentResolver(), selectedImageUri); } else {
selectedImage = try {
Bitmap.createScaledBitmap(selectedImage, PHOTO_SIZE, PHOTO_SIZE, false); Bitmap selectedImage =
editContactPicture(null, selectedImage); MediaStore.Images.Media.getBitmap(
} catch (IOException e) { getActivity().getContentResolver(), selectedImageUri);
Log.e(e); editContactPicture(null, selectedImage);
} catch (IOException e) {
Log.e("[Contact Editor] IO error: ", e);
}
} }
} else if (mPickedPhotoForContactUri != null) { } else if (mPickedPhotoForContactUri != null) {
String filePath = mPickedPhotoForContactUri.getPath(); String filePath = mPickedPhotoForContactUri.getPath();
@ -478,23 +482,52 @@ public class ContactEditorFragment extends Fragment {
} }
private void editContactPicture(String filePath, Bitmap image) { private void editContactPicture(String filePath, Bitmap image) {
int orientation = ExifInterface.ORIENTATION_NORMAL;
if (image == null) { if (image == null) {
Log.i( Log.i(
"[Contact Editor] Bitmap is null, trying to decode image from file [", "[Contact Editor] Bitmap is null, trying to decode image from file [",
filePath, filePath,
"]"); "]");
image = BitmapFactory.decodeFile(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) { if (image == null) {
Log.e( Log.e(
"[Contact Editor] Couldn't get bitmap from either filePath [", "[Contact Editor] Couldn't get bitmap from either filePath [",
filePath, filePath,
"] nor image [", "] nor image");
image,
"]");
return; 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(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, stream); image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
mPhotoToAdd = stream.toByteArray(); mPhotoToAdd = stream.toByteArray();

View file

@ -640,6 +640,10 @@ public class LinphoneContact extends AndroidContact
public void save() { public void save() {
saveChangesCommited(); saveChangesCommited();
if (getAndroidId() != null) {
setThumbnailUri(getContactThumbnailPictureUri());
setPhotoUri(getContactPictureUri());
}
syncValuesFromAndroidContact(LinphoneContext.instance().getApplicationContext()); syncValuesFromAndroidContact(LinphoneContext.instance().getApplicationContext());
createOrUpdateFriend(); createOrUpdateFriend();
} }

View file

@ -22,6 +22,7 @@ package org.linphone.utils;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode; import android.graphics.PorterDuffXfermode;
@ -85,4 +86,14 @@ public class ImageUtils {
/ ((float) context.getResources().getDisplayMetrics().densityDpi / ((float) context.getResources().getDisplayMetrics().densityDpi
/ DisplayMetrics.DENSITY_DEFAULT); / 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;
}
} }