rounded ImageView instead of Bitmap, crop-select

// FREEBIE
This commit is contained in:
Jake McGinty 2014-12-29 17:31:41 -08:00
parent a0599c1639
commit 62816ee51a
19 changed files with 81 additions and 154 deletions

View File

@ -212,6 +212,8 @@
android:theme="@android:style/Theme.Translucent.NoTitleBar" android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/> android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
<activity android:name="com.soundcloud.android.crop.CropImageActivity" />
<service android:enabled="true" android:name=".service.ApplicationMigrationService"/> <service android:enabled="true" android:name=".service.ApplicationMigrationService"/>
<service android:enabled="true" android:name=".service.KeyCachingService"/> <service android:enabled="true" android:name=".service.KeyCachingService"/>
<service android:enabled="true" android:name=".service.RegistrationService"/> <service android:enabled="true" android:name=".service.RegistrationService"/>

View File

@ -40,6 +40,8 @@ dependencies {
compile 'org.w3c:smil:1.0.0' compile 'org.w3c:smil:1.0.0'
compile 'org.apache.httpcomponents:httpclient-android:4.3.5' compile 'org.apache.httpcomponents:httpclient-android:4.3.5'
compile 'com.github.chrisbanes.photoview:library:1.2.3' 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.android.support:appcompat-v7:20.0.0'
compile 'com.madgag.spongycastle:prov:1.51.0.0' compile 'com.madgag.spongycastle:prov:1.51.0.0'
compile 'com.google.zxing:android-integration:3.1.0' compile 'com.google.zxing:android-integration:3.1.0'
@ -71,6 +73,8 @@ dependencyVerification {
'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b', 'org.w3c:smil:085dc40f2bb249651578bfa07499fd08b16ad0886dbe2c4078586a408da62f9b',
'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1', 'org.apache.httpcomponents:httpclient-android:6f56466a9bd0d42934b90bfbfe9977a8b654c058bf44a12bdc2877c4e1f033f1',
'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc', 'com.github.chrisbanes.photoview:library:8b5344e206f125e7ba9d684008f36c4992d03853c57e5814125f88496126e3cc',
'com.makeramen:roundedimageview:7dda2e78c406760e5c356ccce59b0df46b5b171cf18abb891998594405021548',
'com.soundcloud.android:android-crop:ffd4b973cf6e97f7d64118a0dc088df50e9066fd5634fe6911dd0c0c5d346177',
'com.android.support:appcompat-v7:736f576ab0b68d27bdf18b1e7931566e6d8254b73965175313e87f8866b91547', 'com.android.support:appcompat-v7:736f576ab0b68d27bdf18b1e7931566e6d8254b73965175313e87f8866b91547',
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a', 'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4', 'com.google.zxing:android-integration:89e56aadf1164bd71e57949163c53abf90af368b51669c0d4a47a163335f95c4',

View File

@ -5,7 +5,8 @@
android:paddingRight="10dip" android:paddingRight="10dip"
android:orientation="vertical" android:orientation="vertical"
android:background="?conversation_item_background" android:background="?conversation_item_background"
xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView android:id="@+id/group_message_status" <TextView android:id="@+id/group_message_status"
android:layout_width="wrap_content" android:layout_width="wrap_content"
@ -26,11 +27,12 @@
android:layout_marginRight="0dp" android:layout_marginRight="0dp"
android:orientation="horizontal"> android:orientation="horizontal">
<org.thoughtcrime.securesms.components.ForegroundImageView <com.makeramen.RoundedImageView
android:id="@+id/contact_photo" android:id="@+id/contact_photo"
android:foreground="@drawable/contact_photo_background" android:foreground="@drawable/contact_photo_background"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
app:riv_oval="true"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:cropToPadding="true" android:cropToPadding="true"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<org.thoughtcrime.securesms.ConversationListItem <org.thoughtcrime.securesms.ConversationListItem
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -15,11 +16,12 @@
android:paddingRight="10dp" android:paddingRight="10dp"
android:visibility="visible"> android:visibility="visible">
<org.thoughtcrime.securesms.components.ForegroundImageView <com.makeramen.RoundedImageView
android:id="@+id/contact_photo_image" android:id="@+id/contact_photo_image"
android:foreground="@drawable/contact_photo_background" android:foreground="@drawable/contact_photo_background"
android:layout_width="60dp" android:layout_width="60dp"
android:layout_height="60dp" android:layout_height="60dp"
app:riv_oval="true"
android:cropToPadding="true" android:cropToPadding="true"
android:scaleType="centerCrop" android:scaleType="centerCrop"
android:visibility="gone" android:visibility="gone"

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
@ -24,6 +25,7 @@
android:layout_width="70dp" android:layout_width="70dp"
android:layout_height="70dp" android:layout_height="70dp"
position="bottom_right" position="bottom_right"
app:riv_oval="true"
android:layout_marginRight="10dp" android:layout_marginRight="10dp"
android:src="@drawable/ic_group_photo" android:src="@drawable/ic_group_photo"
android:contentDescription="@string/GroupCreateActivity_avatar_content_description" /> android:contentDescription="@string/GroupCreateActivity_avatar_content_description" />

View File

@ -1,11 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight" android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingRight="25dip"> android:paddingRight="25dip">
<ImageView android:id="@+id/contact_photo_image" <com.makeramen.RoundedImageView
android:id="@+id/contact_photo_image"
android:layout_width="@dimen/contact_selection_photo_size" android:layout_width="@dimen/contact_selection_photo_size"
android:layout_height="@dimen/contact_selection_photo_size" android:layout_height="@dimen/contact_selection_photo_size"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
@ -13,6 +15,7 @@
android:layout_marginLeft="10dp" android:layout_marginLeft="10dp"
android:cropToPadding="true" android:cropToPadding="true"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:riv_oval="true"
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" /> android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
<TextView android:id="@+id/number" <TextView android:id="@+id/number"

View File

@ -590,10 +590,6 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
} else if (isGroupConversation()) { } else if (isGroupConversation()) {
if (isPushGroupConversation()) { if (isPushGroupConversation()) {
final String groupName = recipient.getName(); final String groupName = recipient.getName();
final Bitmap avatar = recipient.getContactPhoto();
if (avatar != null) {
getSupportActionBar().setIcon(new BitmapDrawable(getResources(), BitmapUtil.getCircleCroppedBitmap(avatar)));
}
title = (!TextUtils.isEmpty(groupName)) ? groupName : getString(R.string.ConversationActivity_unnamed_group); title = (!TextUtils.isEmpty(groupName)) ? groupName : getString(R.string.ConversationActivity_unnamed_group);
subtitle = null; subtitle = null;

View File

@ -439,7 +439,7 @@ public class ConversationItem extends LinearLayout {
if ((recipient.getContactPhoto() == ContactPhotoFactory.getDefaultContactPhoto(context)) && (groupThread)) { if ((recipient.getContactPhoto() == ContactPhotoFactory.getDefaultContactPhoto(context)) && (groupThread)) {
contactPhotoBitmap = recipient.getGeneratedAvatar(context); contactPhotoBitmap = recipient.getGeneratedAvatar(context);
} else { } else {
contactPhotoBitmap = recipient.getCircleCroppedContactPhoto(); contactPhotoBitmap = recipient.getContactPhoto();
} }
contactPhoto.setImageBitmap(contactPhotoBitmap); contactPhoto.setImageBitmap(contactPhotoBitmap);

View File

@ -128,7 +128,7 @@ public class ConversationListItem extends RelativeLayout
private void setContactPhoto(final Recipient recipient) { private void setContactPhoto(final Recipient recipient) {
if (recipient == null) return; if (recipient == null) return;
contactPhotoImage.setImageBitmap(recipient.getCircleCroppedContactPhoto()); contactPhotoImage.setImageBitmap(recipient.getContactPhoto());
if (!recipient.isGroupRecipient()) { if (!recipient.isGroupRecipient()) {
contactPhotoImage.setOnClickListener(new View.OnClickListener() { contactPhotoImage.setOnClickListener(new View.OnClickListener() {

View File

@ -40,6 +40,7 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.soundcloud.android.crop.Crop;
import org.thoughtcrime.securesms.components.PushRecipientsPanel; import org.thoughtcrime.securesms.components.PushRecipientsPanel;
import org.thoughtcrime.securesms.contacts.ContactAccessor; import org.thoughtcrime.securesms.contacts.ContactAccessor;
@ -68,6 +69,7 @@ import org.thoughtcrime.securesms.database.NotInDirectoryException;
import org.whispersystems.textsecure.api.util.InvalidNumberException; import org.whispersystems.textsecure.api.util.InvalidNumberException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
@ -282,9 +284,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
avatar.setOnClickListener(new View.OnClickListener() { avatar.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT, null); Crop.pickImage(GroupCreateActivity.this);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, PICK_AVATAR);
} }
}); });
@ -373,6 +373,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
@Override @Override
public void onActivityResult(int reqCode, int resultCode, Intent data) { public void onActivityResult(int reqCode, int resultCode, Intent data) {
super.onActivityResult(reqCode, resultCode, data); super.onActivityResult(reqCode, resultCode, data);
Uri outputFile = Uri.fromFile(new File(getCacheDir(), "cropped"));
if (data == null || resultCode != Activity.RESULT_OK) if (data == null || resultCode != Activity.RESULT_OK)
return; return;
@ -398,9 +399,11 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
syncAdapterWithSelectedContacts(); syncAdapterWithSelectedContacts();
break; break;
case PICK_AVATAR: case Crop.REQUEST_PICK:
new DecodeCropAndSetAsyncTask(data.getData()).execute(); new Crop(data.getData()).output(outputFile).asSquare().start(this);
break; break;
case Crop.REQUEST_CROP:
new DecodeCropAndSetAsyncTask(Crop.getOutput(data)).execute();
} }
} }
@ -510,7 +513,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
protected Bitmap doInBackground(Void... voids) { protected Bitmap doInBackground(Void... voids) {
if (avatarUri != null) { if (avatarUri != null) {
try { try {
avatarBmp = BitmapUtil.getScaledCircleCroppedBitmap(GroupCreateActivity.this, masterSecret, avatarUri, AVATAR_SIZE); avatarBmp = BitmapUtil.createScaledBitmap(GroupCreateActivity.this, masterSecret, avatarUri, AVATAR_SIZE, AVATAR_SIZE);
} catch (IOException | BitmapDecodingException e) { } catch (IOException | BitmapDecodingException e) {
Log.w(TAG, e); Log.w(TAG, e);
return null; return null;
@ -683,8 +686,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
existingTitle = group.getTitle(); existingTitle = group.getTitle();
final byte[] existingAvatar = group.getAvatar(); final byte[] existingAvatar = group.getAvatar();
if (existingAvatar != null) { if (existingAvatar != null) {
existingAvatarBmp = BitmapUtil.getCircleCroppedBitmap( existingAvatarBmp = BitmapFactory.decodeByteArray(existingAvatar, 0, existingAvatar.length);
BitmapFactory.decodeByteArray(existingAvatar, 0, existingAvatar.length));
} }
} }
return null; return null;

View File

@ -98,7 +98,7 @@ public class ShareListItem extends RelativeLayout
private void setContactPhoto(final Recipient recipient) { private void setContactPhoto(final Recipient recipient) {
if (recipient == null) return; if (recipient == null) return;
contactPhotoImage.setImageBitmap(BitmapUtil.getCircleCroppedBitmap(recipient.getContactPhoto())); contactPhotoImage.setImageBitmap(recipient.getContactPhoto());
} }
private void setBackground() { private void setBackground() {

View File

@ -1,16 +1,17 @@
package org.thoughtcrime.securesms.components; package org.thoughtcrime.securesms.components;
import android.content.Context; import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.ImageView; import android.widget.ImageView;
import com.makeramen.RoundedImageView;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
public class ImageDivet extends ImageView { public class ImageDivet extends RoundedImageView {
private static final float CORNER_OFFSET = 12F; private static final float CORNER_OFFSET = 12F;
private static final String[] POSITIONS = new String[] {"bottom_right"}; private static final String[] POSITIONS = new String[] {"bottom_right"};

View File

@ -21,13 +21,9 @@ public class ContactPhotoFactory {
private static final Object defaultPhotoLock = new Object(); private static final Object defaultPhotoLock = new Object();
private static final Object defaultGroupPhotoLock = new Object(); private static final Object defaultGroupPhotoLock = new Object();
private static final Object defaultPhotoCroppedLock = new Object();
private static final Object defaultGroupPhotoCroppedLock = new Object();
private static Bitmap defaultContactPhoto; private static Bitmap defaultContactPhoto;
private static Bitmap defaultGroupContactPhoto; private static Bitmap defaultGroupContactPhoto;
private static Bitmap defaultContactPhotoCropped;
private static Bitmap defaultGroupContactPhotoCropped;
private static final Map<Uri,Bitmap> localUserContactPhotoCache = private static final Map<Uri,Bitmap> localUserContactPhotoCache =
Collections.synchronizedMap(new LRUCache<Uri,Bitmap>(2)); Collections.synchronizedMap(new LRUCache<Uri,Bitmap>(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) { public static Bitmap getLocalUserContactPhoto(Context context, Uri uri) {
if (uri == null) return getDefaultContactPhoto(context); if (uri == null) return getDefaultContactPhoto(context);

View File

@ -30,12 +30,10 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.FilterQueryProvider;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.BitmapUtil;
import org.thoughtcrime.securesms.util.BitmapWorkerRunnable; import org.thoughtcrime.securesms.util.BitmapWorkerRunnable;
import org.thoughtcrime.securesms.util.BitmapWorkerRunnable.AsyncDrawable; import org.thoughtcrime.securesms.util.BitmapWorkerRunnable.AsyncDrawable;
import org.thoughtcrime.securesms.util.TaggedFutureTask; import org.thoughtcrime.securesms.util.TaggedFutureTask;
@ -76,10 +74,9 @@ public class ContactSelectionListAdapter extends CursorAdapter
private final LayoutInflater li; private final LayoutInflater li;
private final TypedArray drawables; private final TypedArray drawables;
private final Bitmap defaultPhoto; private final Bitmap defaultPhoto;
private final Bitmap defaultCroppedPhoto;
private final int scaledPhotoSize; private final int scaledPhotoSize;
private final HashMap<Long, ContactAccessor.ContactData> selectedContacts = new HashMap<Long, ContactAccessor.ContactData>(); private final HashMap<Long, ContactAccessor.ContactData> selectedContacts = new HashMap<>();
public ContactSelectionListAdapter(Context context, Cursor cursor, boolean multiSelect) { public ContactSelectionListAdapter(Context context, Cursor cursor, boolean multiSelect) {
super(context, cursor, 0); super(context, cursor, 0);
@ -89,7 +86,6 @@ public class ContactSelectionListAdapter extends CursorAdapter
this.multiSelect = multiSelect; this.multiSelect = multiSelect;
this.defaultPhoto = ContactPhotoFactory.getDefaultContactPhoto(context); this.defaultPhoto = ContactPhotoFactory.getDefaultContactPhoto(context);
this.scaledPhotoSize = context.getResources().getDimensionPixelSize(R.dimen.contact_selection_photo_size); this.scaledPhotoSize = context.getResources().getDimensionPixelSize(R.dimen.contact_selection_photo_size);
this.defaultCroppedPhoto = BitmapUtil.getScaledCircleCroppedBitmap(defaultPhoto, scaledPhotoSize);
} }
public static class ViewHolder { 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); numberLabelSpan.setSpan(new ForegroundColorSpan(drawables.getColor(2, 0xff444444)), contactData.number.length(), numberWithLabel.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
holder.number.setText(numberLabelSpan); holder.number.setText(numberLabelSpan);
} }
holder.contactPhoto.setImageBitmap(defaultCroppedPhoto); holder.contactPhoto.setImageBitmap(defaultPhoto);
if (contactData.id > -1) loadBitmap(contactData.number, holder.contactPhoto); if (contactData.id > -1) loadBitmap(contactData.number, holder.contactPhoto);
} }
@ -237,7 +233,7 @@ public class ContactSelectionListAdapter extends CursorAdapter
if (cancelPotentialWork(number, imageView)) { if (cancelPotentialWork(number, imageView)) {
final BitmapWorkerRunnable runnable = new BitmapWorkerRunnable(context, imageView, defaultPhoto, number, scaledPhotoSize); final BitmapWorkerRunnable runnable = new BitmapWorkerRunnable(context, imageView, defaultPhoto, number, scaledPhotoSize);
final TaggedFutureTask<?> task = new TaggedFutureTask<Void>(runnable, null, number); final TaggedFutureTask<?> task = new TaggedFutureTask<Void>(runnable, null, number);
final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(), defaultCroppedPhoto, task); final AsyncDrawable asyncDrawable = new AsyncDrawable(defaultPhoto, task);
imageView.setImageDrawable(asyncDrawable); imageView.setImageDrawable(asyncDrawable);
if (!task.isCancelled()) photoResolver.execute(new FutureTask<Void>(task, null)); if (!task.isCancelled()) photoResolver.execute(new FutureTask<Void>(task, null));

View File

@ -24,7 +24,7 @@ public class AvatarGenerator {
public static Bitmap generateFor(Context context, Recipient recipient) { public static Bitmap generateFor(Context context, Recipient recipient) {
if ((recipient == null) || (recipient.getName() == null)) { if ((recipient == null) || (recipient.getName() == null)) {
return BitmapUtil.getCircleCroppedBitmap(ContactPhotoFactory.getDefaultContactPhoto(context)); return ContactPhotoFactory.getDefaultContactPhoto(context);
} }
final int size = ContactPhotoFactory.getDefaultContactPhoto(context).getHeight(); final int size = ContactPhotoFactory.getDefaultContactPhoto(context).getHeight();

View File

@ -41,16 +41,14 @@ public class Recipient {
private String name; private String name;
private Bitmap contactPhoto; private Bitmap contactPhoto;
private Bitmap circleCroppedContactPhoto;
private Bitmap generatedAvatar; private Bitmap generatedAvatar;
private Uri contactUri; private Uri contactUri;
Recipient(String number, Bitmap contactPhoto, Bitmap circleCroppedContactPhoto, Recipient(String number, Bitmap contactPhoto,
long recipientId, ListenableFutureTask<RecipientDetails> future) long recipientId, ListenableFutureTask<RecipientDetails> future)
{ {
this.number = number; this.number = number;
this.circleCroppedContactPhoto = circleCroppedContactPhoto;
this.contactPhoto = contactPhoto; this.contactPhoto = contactPhoto;
this.recipientId = recipientId; this.recipientId = recipientId;
this.generatedAvatar = null; this.generatedAvatar = null;
@ -66,7 +64,6 @@ public class Recipient {
Recipient.this.number = result.number; Recipient.this.number = result.number;
Recipient.this.contactUri = result.contactUri; Recipient.this.contactUri = result.contactUri;
Recipient.this.contactPhoto = result.avatar; Recipient.this.contactPhoto = result.avatar;
Recipient.this.circleCroppedContactPhoto = result.croppedAvatar;
localListeners = (HashSet<RecipientModifiedListener>) listeners.clone(); localListeners = (HashSet<RecipientModifiedListener>) listeners.clone();
listeners.clear(); listeners.clear();
@ -84,15 +81,12 @@ public class Recipient {
}); });
} }
Recipient(String name, String number, long recipientId, Uri contactUri, Bitmap contactPhoto, Recipient(String name, String number, long recipientId, Uri contactUri, Bitmap contactPhoto) {
Bitmap circleCroppedContactPhoto)
{
this.number = number; this.number = number;
this.recipientId = recipientId; this.recipientId = recipientId;
this.contactUri = contactUri; this.contactUri = contactUri;
this.name = name; this.name = name;
this.contactPhoto = contactPhoto; this.contactPhoto = contactPhoto;
this.circleCroppedContactPhoto = circleCroppedContactPhoto;
} }
public synchronized Uri getContactUri() { public synchronized Uri getContactUri() {
@ -153,10 +147,6 @@ public class Recipient {
return contactPhoto; return contactPhoto;
} }
public synchronized Bitmap getCircleCroppedContactPhoto() {
return this.circleCroppedContactPhoto;
}
public synchronized Bitmap getGeneratedAvatar(Context context) { public synchronized Bitmap getGeneratedAvatar(Context context) {
if (this.generatedAvatar == null) if (this.generatedAvatar == null)
this.generatedAvatar = AvatarGenerator.generateFor(context, this); this.generatedAvatar = AvatarGenerator.generateFor(context, this);
@ -166,8 +156,7 @@ public class Recipient {
public static Recipient getUnknownRecipient(Context context) { public static Recipient getUnknownRecipient(Context context) {
return new Recipient("Unknown", "Unknown", -1, null, return new Recipient("Unknown", "Unknown", -1, null,
ContactPhotoFactory.getDefaultContactPhoto(context), ContactPhotoFactory.getDefaultContactPhoto(context));
ContactPhotoFactory.getDefaultContactPhotoCropped(context));
} }
@Override @Override

View File

@ -73,17 +73,13 @@ public class RecipientProvider {
else details = getRecipientDetails(context, number); else details = getRecipientDetails(context, number);
if (details != null) { if (details != null) {
recipient = new Recipient(details.name, details.number, recipientId, details.contactUri, details.avatar, recipient = new Recipient(details.name, details.number, recipientId, details.contactUri, details.avatar);
details.croppedAvatar);
} else { } else {
final Bitmap defaultPhoto = isGroupRecipient final Bitmap defaultPhoto = isGroupRecipient
? ContactPhotoFactory.getDefaultGroupPhoto(context) ? ContactPhotoFactory.getDefaultGroupPhoto(context)
: ContactPhotoFactory.getDefaultContactPhoto(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); recipientCache.put(recipientId, recipient);
@ -109,17 +105,14 @@ public class RecipientProvider {
asyncRecipientResolver.submit(future); asyncRecipientResolver.submit(future);
Bitmap contactPhoto; Bitmap contactPhoto;
Bitmap contactPhotoCropped;
if (isGroupRecipient) { if (isGroupRecipient) {
contactPhoto = ContactPhotoFactory.getDefaultGroupPhoto(context); contactPhoto = ContactPhotoFactory.getDefaultGroupPhoto(context);
contactPhotoCropped = ContactPhotoFactory.getDefaultGroupPhotoCropped(context);
} else { } else {
contactPhoto = ContactPhotoFactory.getDefaultContactPhoto(context); 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); recipientCache.put(recipientId, recipient);
return recipient; return recipient;
@ -144,8 +137,7 @@ public class RecipientProvider {
Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1)); Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1));
Bitmap contactPhoto = ContactPhotoFactory.getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI, Bitmap contactPhoto = ContactPhotoFactory.getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
cursor.getLong(2)+"")); cursor.getLong(2)+""));
return new RecipientDetails(cursor.getString(0), cursor.getString(3), contactUri, contactPhoto, return new RecipientDetails(cursor.getString(0), cursor.getString(3), contactUri, contactPhoto);
BitmapUtil.getCircleCroppedBitmap(contactPhoto));
} }
} finally { } finally {
if (cursor != null) if (cursor != null)
@ -167,7 +159,7 @@ public class RecipientProvider {
if (avatarBytes == null) avatar = ContactPhotoFactory.getDefaultGroupPhoto(context); if (avatarBytes == null) avatar = ContactPhotoFactory.getDefaultGroupPhoto(context);
else avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length); 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; return null;
@ -181,14 +173,12 @@ public class RecipientProvider {
public final String name; public final String name;
public final String number; public final String number;
public final Bitmap avatar; public final Bitmap avatar;
public final Bitmap croppedAvatar;
public final Uri contactUri; 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.name = name;
this.number = number; this.number = number;
this.avatar = avatar; this.avatar = avatar;
this.croppedAvatar = croppedAvatar;
this.contactUri = contactUri; this.contactUri = contactUri;
} }
} }

View File

@ -217,41 +217,6 @@ public class BitmapUtil {
return new Pair<>(options.outWidth, options.outHeight); 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) { public static InputStream toCompressedJpeg(Bitmap bitmap) {
ByteArrayOutputStream thumbnailBytes = new ByteArrayOutputStream(); ByteArrayOutputStream thumbnailBytes = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, 85, thumbnailBytes); bitmap.compress(CompressFormat.JPEG, 85, thumbnailBytes);

View File

@ -25,6 +25,8 @@ import android.graphics.drawable.Drawable;
import android.util.Log; import android.util.Log;
import android.widget.ImageView; import android.widget.ImageView;
import com.makeramen.RoundedDrawable;
import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
@ -48,7 +50,7 @@ public class BitmapWorkerRunnable implements Runnable {
public final String number; public final String number;
public BitmapWorkerRunnable(Context context, ImageView imageView, Bitmap defaultPhoto, String number, int size) { public BitmapWorkerRunnable(Context context, ImageView imageView, Bitmap defaultPhoto, String number, int size) {
this.imageViewReference = new WeakReference<ImageView>(imageView); this.imageViewReference = new WeakReference<>(imageView);
this.context = context; this.context = context;
this.defaultPhoto = defaultPhoto; this.defaultPhoto = defaultPhoto;
this.size = size; this.size = size;
@ -57,26 +59,18 @@ public class BitmapWorkerRunnable implements Runnable {
@Override @Override
public void run() { public void run() {
final Bitmap bitmap;
try { try {
final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient(); final Recipient recipient = RecipientFactory.getRecipientsFromString(context, number, false).getPrimaryRecipient();
final Bitmap contactPhoto = recipient.getContactPhoto(); final Bitmap contactPhoto = recipient.getContactPhoto();
if (defaultPhoto == contactPhoto) { if (defaultPhoto == contactPhoto) {
return; return;
} }
if (recipient.getContactPhoto() != null) {
bitmap = BitmapUtil.getScaledCircleCroppedBitmap(contactPhoto, size);
} catch (RecipientFormattingException rfe) {
Log.w(TAG, "Couldn't get recipient from string", rfe);
return;
}
if (bitmap != null) {
final ImageView imageView = imageViewReference.get(); final ImageView imageView = imageViewReference.get();
final TaggedFutureTask<?> bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView); final TaggedFutureTask<?> bitmapWorkerTask = AsyncDrawable.getBitmapWorkerTask(imageView);
if (bitmapWorkerTask.getTag().equals(number) && imageView != null) { if (bitmapWorkerTask.getTag().equals(number) && imageView != null) {
final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap); final BitmapDrawable drawable = new BitmapDrawable(context.getResources(), recipient.getContactPhoto());
imageView.post(new Runnable() { imageView.post(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -85,14 +79,16 @@ public class BitmapWorkerRunnable implements Runnable {
}); });
} }
} }
} catch (RecipientFormattingException rfe) {
Log.w(TAG, "Couldn't get recipient from string", rfe);
}
} }
public static class AsyncDrawable extends BitmapDrawable { public static class AsyncDrawable extends RoundedDrawable {
private final WeakReference<TaggedFutureTask<?>> bitmapWorkerTaskReference; private final WeakReference<TaggedFutureTask<?>> bitmapWorkerTaskReference;
public AsyncDrawable(Resources res, Bitmap bitmap, public AsyncDrawable(Bitmap bitmap, TaggedFutureTask<?> bitmapWorkerTask) {
TaggedFutureTask<?> bitmapWorkerTask) { super(bitmap);
super(res, bitmap);
bitmapWorkerTaskReference = bitmapWorkerTaskReference =
new WeakReference<TaggedFutureTask<?>>(bitmapWorkerTask); new WeakReference<TaggedFutureTask<?>>(bitmapWorkerTask);
} }
@ -112,5 +108,4 @@ public class BitmapWorkerRunnable implements Runnable {
return null; return null;
} }
} }
} }