diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d126558911..88b62fd085 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -212,6 +212,8 @@ android:theme="@android:style/Theme.Translucent.NoTitleBar" android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> + + diff --git a/build.gradle b/build.gradle index 258a313d6f..a5916cc8d6 100644 --- a/build.gradle +++ b/build.gradle @@ -40,6 +40,8 @@ dependencies { compile 'org.w3c:smil:1.0.0' compile 'org.apache.httpcomponents:httpclient-android:4.3.5' compile 'com.github.chrisbanes.photoview:library:1.2.3' + compile 'com.makeramen:roundedimageview:1.5.0' + compile 'com.soundcloud.android:android-crop:0.9.10@aar' compile 'com.android.support:appcompat-v7:20.0.0' compile 'com.madgag.spongycastle:prov:1.51.0.0' compile 'com.google.zxing:android-integration:3.1.0' @@ -71,6 +73,8 @@ dependencyVerification { 'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b', 'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1', 'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc', + 'com.makeramen:roundedimageview:7dda2e78c406760e5c356ccce59b0df46b5b171cf18abb891998594405021548', + 'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177', 'com.android.support:appcompat-v7:736f576ab0b68d27bdf18b1e7931566e6d8254b73965175313e87f8866b91547', 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a', 'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4', diff --git a/res/layout/conversation_item_received.xml b/res/layout/conversation_item_received.xml index d919678a8c..cb292f3703 100644 --- a/res/layout/conversation_item_received.xml +++ b/res/layout/conversation_item_received.xml @@ -1,11 +1,12 @@ + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:paddingRight="10dip" + android:orientation="vertical" + android:background="?conversation_item_background" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto"> - @@ -15,11 +16,12 @@ android:paddingRight="10dp" android:visibility="visible"> - + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> diff --git a/res/layout/push_contact_selection_list_item.xml b/res/layout/push_contact_selection_list_item.xml index 2e3d081bf4..7460dc7dde 100644 --- a/res/layout/push_contact_selection_list_item.xml +++ b/res/layout/push_contact_selection_list_item.xml @@ -1,11 +1,13 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="?android:attr/listPreferredItemHeight" + android:paddingRight="25dip"> - localUserContactPhotoCache = Collections.synchronizedMap(new LRUCache(2)); @@ -56,24 +52,6 @@ public class ContactPhotoFactory { } } - public static Bitmap getDefaultContactPhotoCropped(Context context) { - synchronized (defaultPhotoCroppedLock) { - if (defaultContactPhotoCropped == null) - defaultContactPhotoCropped = BitmapUtil.getCircleCroppedBitmap(getDefaultContactPhoto(context)); - - return defaultContactPhotoCropped; - } - } - - public static Bitmap getDefaultGroupPhotoCropped(Context context) { - synchronized (defaultGroupPhotoCroppedLock) { - if (defaultGroupContactPhotoCropped == null) - defaultGroupContactPhotoCropped = BitmapUtil.getCircleCroppedBitmap(getDefaultGroupPhoto(context)); - - return defaultGroupContactPhotoCropped; - } - } - public static Bitmap getLocalUserContactPhoto(Context context, Uri uri) { if (uri == null) return getDefaultContactPhoto(context); diff --git a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java index 5a8039aedd..95e1d7f9e9 100644 --- a/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java +++ b/src/org/thoughtcrime/securesms/contacts/ContactSelectionListAdapter.java @@ -30,12 +30,10 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; -import android.widget.FilterQueryProvider; import android.widget.ImageView; import android.widget.TextView; import org.thoughtcrime.securesms.R; -import org.thoughtcrime.securesms.util.BitmapUtil; import org.thoughtcrime.securesms.util.BitmapWorkerRunnable; import org.thoughtcrime.securesms.util.BitmapWorkerRunnable.AsyncDrawable; import org.thoughtcrime.securesms.util.TaggedFutureTask; @@ -76,10 +74,9 @@ public class ContactSelectionListAdapter extends CursorAdapter private final LayoutInflater li; private final TypedArray drawables; private final Bitmap defaultPhoto; - private final Bitmap defaultCroppedPhoto; private final int scaledPhotoSize; - private final HashMap selectedContacts = new HashMap(); + private final HashMap selectedContacts = new HashMap<>(); public ContactSelectionListAdapter(Context context, Cursor cursor, boolean multiSelect) { super(context, cursor, 0); @@ -89,7 +86,6 @@ public class ContactSelectionListAdapter extends CursorAdapter this.multiSelect = multiSelect; this.defaultPhoto = ContactPhotoFactory.getDefaultContactPhoto(context); this.scaledPhotoSize = context.getResources().getDimensionPixelSize(R.dimen.contact_selection_photo_size); - this.defaultCroppedPhoto = BitmapUtil.getScaledCircleCroppedBitmap(defaultPhoto, scaledPhotoSize); } public static class ViewHolder { @@ -184,7 +180,7 @@ public class ContactSelectionListAdapter extends CursorAdapter numberLabelSpan.setSpan(new ForegroundColorSpan(drawables.getColor(2, 0xff444444)), contactData.number.length(), numberWithLabel.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); holder.number.setText(numberLabelSpan); } - holder.contactPhoto.setImageBitmap(defaultCroppedPhoto); + holder.contactPhoto.setImageBitmap(defaultPhoto); if (contactData.id > -1) loadBitmap(contactData.number, holder.contactPhoto); } @@ -237,7 +233,7 @@ public class ContactSelectionListAdapter extends CursorAdapter if (cancelPotentialWork(number, imageView)) { final BitmapWorkerRunnable runnable = new BitmapWorkerRunnable(context, imageView, defaultPhoto, number, scaledPhotoSize); final TaggedFutureTask task = new TaggedFutureTask(runnable, null, number); - final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(), defaultCroppedPhoto, task); + final AsyncDrawable asyncDrawable = new AsyncDrawable(defaultPhoto, task); imageView.setImageDrawable(asyncDrawable); if (!task.isCancelled()) photoResolver.execute(new FutureTask(task, null)); diff --git a/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java b/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java index 188514435c..142f238613 100644 --- a/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java +++ b/src/org/thoughtcrime/securesms/recipients/AvatarGenerator.java @@ -24,7 +24,7 @@ public class AvatarGenerator { public static Bitmap generateFor(Context context, Recipient recipient) { if ((recipient == null) || (recipient.getName() == null)) { - return BitmapUtil.getCircleCroppedBitmap(ContactPhotoFactory.getDefaultContactPhoto(context)); + return ContactPhotoFactory.getDefaultContactPhoto(context); } final int size = ContactPhotoFactory.getDefaultContactPhoto(context).getHeight(); diff --git a/src/org/thoughtcrime/securesms/recipients/Recipient.java b/src/org/thoughtcrime/securesms/recipients/Recipient.java index c317807975..cde6e9c262 100644 --- a/src/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/src/org/thoughtcrime/securesms/recipients/Recipient.java @@ -41,16 +41,14 @@ public class Recipient { private String name; private Bitmap contactPhoto; - private Bitmap circleCroppedContactPhoto; private Bitmap generatedAvatar; private Uri contactUri; - Recipient(String number, Bitmap contactPhoto, Bitmap circleCroppedContactPhoto, + Recipient(String number, Bitmap contactPhoto, long recipientId, ListenableFutureTask future) { this.number = number; - this.circleCroppedContactPhoto = circleCroppedContactPhoto; this.contactPhoto = contactPhoto; this.recipientId = recipientId; this.generatedAvatar = null; @@ -66,8 +64,7 @@ public class Recipient { Recipient.this.number = result.number; Recipient.this.contactUri = result.contactUri; Recipient.this.contactPhoto = result.avatar; - Recipient.this.circleCroppedContactPhoto = result.croppedAvatar; - + localListeners = (HashSet) listeners.clone(); listeners.clear(); } @@ -84,15 +81,12 @@ public class Recipient { }); } - Recipient(String name, String number, long recipientId, Uri contactUri, Bitmap contactPhoto, - Bitmap circleCroppedContactPhoto) - { + Recipient(String name, String number, long recipientId, Uri contactUri, Bitmap contactPhoto) { this.number = number; this.recipientId = recipientId; this.contactUri = contactUri; this.name = name; this.contactPhoto = contactPhoto; - this.circleCroppedContactPhoto = circleCroppedContactPhoto; } public synchronized Uri getContactUri() { @@ -153,10 +147,6 @@ public class Recipient { return contactPhoto; } - public synchronized Bitmap getCircleCroppedContactPhoto() { - return this.circleCroppedContactPhoto; - } - public synchronized Bitmap getGeneratedAvatar(Context context) { if (this.generatedAvatar == null) this.generatedAvatar = AvatarGenerator.generateFor(context, this); @@ -166,8 +156,7 @@ public class Recipient { public static Recipient getUnknownRecipient(Context context) { return new Recipient("Unknown", "Unknown", -1, null, - ContactPhotoFactory.getDefaultContactPhoto(context), - ContactPhotoFactory.getDefaultContactPhotoCropped(context)); + ContactPhotoFactory.getDefaultContactPhoto(context)); } @Override diff --git a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java index 6cf15b0f29..a0a941221e 100644 --- a/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java +++ b/src/org/thoughtcrime/securesms/recipients/RecipientProvider.java @@ -73,17 +73,13 @@ public class RecipientProvider { else details = getRecipientDetails(context, number); if (details != null) { - recipient = new Recipient(details.name, details.number, recipientId, details.contactUri, details.avatar, - details.croppedAvatar); + recipient = new Recipient(details.name, details.number, recipientId, details.contactUri, details.avatar); } else { final Bitmap defaultPhoto = isGroupRecipient ? ContactPhotoFactory.getDefaultGroupPhoto(context) : ContactPhotoFactory.getDefaultContactPhoto(context); - final Bitmap defaultCroppedPhoto = isGroupRecipient - ? ContactPhotoFactory.getDefaultGroupPhotoCropped(context) - : ContactPhotoFactory.getDefaultContactPhotoCropped(context); - recipient = new Recipient(null, number, recipientId, null, defaultPhoto, defaultCroppedPhoto); + recipient = new Recipient(null, number, recipientId, null, defaultPhoto); } recipientCache.put(recipientId, recipient); @@ -109,17 +105,14 @@ public class RecipientProvider { asyncRecipientResolver.submit(future); Bitmap contactPhoto; - Bitmap contactPhotoCropped; if (isGroupRecipient) { contactPhoto = ContactPhotoFactory.getDefaultGroupPhoto(context); - contactPhotoCropped = ContactPhotoFactory.getDefaultGroupPhotoCropped(context); } else { contactPhoto = ContactPhotoFactory.getDefaultContactPhoto(context); - contactPhotoCropped = ContactPhotoFactory.getDefaultContactPhotoCropped(context); } - Recipient recipient = new Recipient(number, contactPhoto, contactPhotoCropped, recipientId, future); + Recipient recipient = new Recipient(number, contactPhoto, recipientId, future); recipientCache.put(recipientId, recipient); return recipient; @@ -144,8 +137,7 @@ public class RecipientProvider { Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1)); Bitmap contactPhoto = ContactPhotoFactory.getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI, cursor.getLong(2)+"")); - return new RecipientDetails(cursor.getString(0), cursor.getString(3), contactUri, contactPhoto, - BitmapUtil.getCircleCroppedBitmap(contactPhoto)); + return new RecipientDetails(cursor.getString(0), cursor.getString(3), contactUri, contactPhoto); } } finally { if (cursor != null) @@ -167,7 +159,7 @@ public class RecipientProvider { if (avatarBytes == null) avatar = ContactPhotoFactory.getDefaultGroupPhoto(context); else avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length); - return new RecipientDetails(record.getTitle(), groupId, null, avatar, BitmapUtil.getCircleCroppedBitmap(avatar)); + return new RecipientDetails(record.getTitle(), groupId, null, avatar); } return null; @@ -181,14 +173,12 @@ public class RecipientProvider { public final String name; public final String number; public final Bitmap avatar; - public final Bitmap croppedAvatar; public final Uri contactUri; - public RecipientDetails(String name, String number, Uri contactUri, Bitmap avatar, Bitmap croppedAvatar) { + public RecipientDetails(String name, String number, Uri contactUri, Bitmap avatar) { this.name = name; this.number = number; this.avatar = avatar; - this.croppedAvatar = croppedAvatar; this.contactUri = contactUri; } } diff --git a/src/org/thoughtcrime/securesms/util/BitmapUtil.java b/src/org/thoughtcrime/securesms/util/BitmapUtil.java index 6fd86507e5..4590dd43de 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapUtil.java +++ b/src/org/thoughtcrime/securesms/util/BitmapUtil.java @@ -217,41 +217,6 @@ public class BitmapUtil { return new Pair<>(options.outWidth, options.outHeight); } - public static Bitmap getCircleCroppedBitmap(Bitmap bitmap) { - if (bitmap == null) return null; - final int srcSize = Math.min(bitmap.getWidth(), bitmap.getHeight()); - return getScaledCircleCroppedBitmap(bitmap, srcSize); - } - - public static Bitmap getScaledCircleCroppedBitmap(Context context, MasterSecret masterSecret, Uri uri, int destSize) - throws IOException, BitmapDecodingException - { - Bitmap bitmap = createScaledBitmap(context, masterSecret, uri, destSize, destSize); - return getScaledCircleCroppedBitmap(bitmap, destSize); - } - - public static Bitmap getScaledCircleCroppedBitmap(Bitmap bitmap, int destSize) { - if (bitmap == null) return null; - Bitmap output = Bitmap.createBitmap(destSize, destSize, Bitmap.Config.ARGB_8888); - Canvas canvas = new Canvas(output); - - final int srcSize = Math.min(bitmap.getWidth(), bitmap.getHeight()); - final int srcX = (bitmap.getWidth() - srcSize) / 2; - final int srcY = (bitmap.getHeight() - srcSize) / 2; - final Rect srcRect = new Rect(srcX, srcY, srcX + srcSize, srcY + srcSize); - final Rect destRect = new Rect(0, 0, destSize, destSize); - final int color = 0xff424242; - final Paint paint = new Paint(); - - paint.setAntiAlias(true); - canvas.drawARGB(0, 0, 0, 0); - paint.setColor(color); - canvas.drawCircle(destSize / 2, destSize / 2, destSize / 2, paint); - paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); - canvas.drawBitmap(bitmap, srcRect, destRect, paint); - return output; - } - public static InputStream toCompressedJpeg(Bitmap bitmap) { ByteArrayOutputStream thumbnailBytes = new ByteArrayOutputStream(); bitmap.compress(CompressFormat.JPEG, 85, thumbnailBytes); diff --git a/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java b/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java index 90676b0f4d..c5bf9a121a 100644 --- a/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java +++ b/src/org/thoughtcrime/securesms/util/BitmapWorkerRunnable.java @@ -25,6 +25,8 @@ import android.graphics.drawable.Drawable; import android.util.Log; import android.widget.ImageView; +import com.makeramen.RoundedDrawable; + import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientFactory; @@ -48,7 +50,7 @@ public class BitmapWorkerRunnable implements Runnable { public final String number; public BitmapWorkerRunnable(Context context, ImageView imageView, Bitmap defaultPhoto, String number, int size) { - this.imageViewReference = new WeakReference(imageView); + this.imageViewReference = new WeakReference<>(imageView); this.context = context; this.defaultPhoto = defaultPhoto; this.size = size; @@ -57,42 +59,36 @@ public class BitmapWorkerRunnable implements Runnable { @Override public void run() { - final Bitmap bitmap; try { final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient(); final Bitmap contactPhoto = recipient.getContactPhoto(); if (defaultPhoto == contactPhoto) { return; } + if (recipient.getContactPhoto() != null) { + final ImageView imageView = imageViewReference.get(); + final TaggedFutureTask bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView); - bitmap = BitmapUtil.getScaledCircleCroppedBitmap(contactPhoto, size); + if (bitmapWorkerTask.getTag().equals(number) && imageView != null) { + final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), recipient.getContactPhoto()); + imageView.post(new Runnable() { + @Override + public void run() { + imageView.setImageDrawable(drawable); + } + }); + } + } } catch (RecipientFormattingException rfe) { Log.w(TAG, "Couldn't get recipient from string", rfe); - return; - } - - if (bitmap != null) { - final ImageView imageView = imageViewReference.get(); - final TaggedFutureTask bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView); - - if (bitmapWorkerTask.getTag().equals(number) && imageView != null) { - final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap); - imageView.post(new Runnable() { - @Override - public void run() { - imageView.setImageDrawable(drawable); - } - }); - } } } - public static class AsyncDrawable extends BitmapDrawable { + public static class AsyncDrawable extends RoundedDrawable { private final WeakReference> bitmapWorkerTaskReference; - public AsyncDrawable(Resources res, Bitmap bitmap, - TaggedFutureTask bitmapWorkerTask) { - super(res, bitmap); + public AsyncDrawable(Bitmap bitmap, TaggedFutureTask bitmapWorkerTask) { + super(bitmap); bitmapWorkerTaskReference = new WeakReference>(bitmapWorkerTask); } @@ -112,5 +108,4 @@ public class BitmapWorkerRunnable implements Runnable { return null; } } - }