1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-08-13 17:03:48 -04:00

GMail-app-style generated colorful one-letter contact pictures for pictureless contacts

This commit is contained in:
Stephan Fuhrmann 2013-07-21 16:58:38 +02:00 committed by Jesse Vincent
parent b0c9ae0d88
commit 16f6d85ea2
3 changed files with 80 additions and 23 deletions

View File

@ -5,6 +5,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.ContentResolver; import android.content.ContentResolver;
@ -12,6 +14,9 @@ import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
@ -20,6 +25,7 @@ import android.os.Build;
import android.support.v4.util.LruCache; import android.support.v4.util.LruCache;
import android.widget.QuickContactBadge; import android.widget.QuickContactBadge;
import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.Contacts;
import com.fsck.k9.mail.Address;
public class ContactPictureLoader { public class ContactPictureLoader {
/** /**
@ -41,7 +47,6 @@ public class ContactPictureLoader {
private ContentResolver mContentResolver; private ContentResolver mContentResolver;
private Resources mResources; private Resources mResources;
private Contacts mContactsHelper; private Contacts mContactsHelper;
private Bitmap mDefaultPicture;
private int mPictureSizeInPx; private int mPictureSizeInPx;
/** /**
@ -68,7 +73,6 @@ public class ContactPictureLoader {
mContentResolver = appContext.getContentResolver(); mContentResolver = appContext.getContentResolver();
mResources = appContext.getResources(); mResources = appContext.getResources();
mContactsHelper = Contacts.getInstance(appContext); mContactsHelper = Contacts.getInstance(appContext);
mDefaultPicture = BitmapFactory.decodeResource(mResources, defaultPictureResource);
float scale = mResources.getDisplayMetrics().density; float scale = mResources.getDisplayMetrics().density;
mPictureSizeInPx = (int) (PICTURE_SIZE * scale); mPictureSizeInPx = (int) (PICTURE_SIZE * scale);
@ -115,7 +119,8 @@ public class ContactPictureLoader {
* @see #mBitmapCache * @see #mBitmapCache
* @see #mUnknownContactsCache * @see #mUnknownContactsCache
*/ */
public void loadContactPicture(String email, QuickContactBadge badge) { public void loadContactPicture(Address address, QuickContactBadge badge) {
String email = address.getAddress();
Bitmap bitmap = getBitmapFromCache(email); Bitmap bitmap = getBitmapFromCache(email);
if (bitmap != null) { if (bitmap != null) {
// The picture was found in the bitmap cache // The picture was found in the bitmap cache
@ -123,22 +128,73 @@ public class ContactPictureLoader {
} else if (isEmailInUnknownContactsCache(email)) { } else if (isEmailInUnknownContactsCache(email)) {
// This email address doesn't belong to a contact we have a picture for. Use the // This email address doesn't belong to a contact we have a picture for. Use the
// default picture. // default picture.
badge.setImageBitmap(mDefaultPicture); badge.setImageBitmap(calculateFallbackBitmap(address));
} else if (cancelPotentialWork(email, badge)) { } else if (cancelPotentialWork(email, badge)) {
// Query the contacts database in a background thread and try to load the contact // Query the contacts database in a background thread and try to load the contact
// picture, if there is one. // picture, if there is one.
ContactPictureRetrievalTask task = new ContactPictureRetrievalTask(badge); ContactPictureRetrievalTask task = new ContactPictureRetrievalTask(badge);
AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, mDefaultPicture, task); AsyncDrawable asyncDrawable = new AsyncDrawable(mResources, calculateFallbackBitmap(address), task);
badge.setImageDrawable(asyncDrawable); badge.setImageDrawable(asyncDrawable);
try { try {
task.exec(email); task.exec(address.getAddress(), address.getPersonal());
} catch (RejectedExecutionException e) { } catch (RejectedExecutionException e) {
// We flooded the thread pool queue... fall back to using the default picture // We flooded the thread pool queue... fall back to using the default picture
badge.setImageBitmap(mDefaultPicture); badge.setImageBitmap(calculateFallbackBitmap(address));
} }
} }
} }
private int calcUnknownContactColor(Address address) {
int val = address.getAddress().toLowerCase().hashCode();
int rgb =
(0xff) << 24 |
(~val & 0xff) << 16 |
(val & 0xff) << 8 |
(val>>>8 & 0xff);
return rgb;
}
private char calcUnknownContactLetter(Address address) {
String letter = "";
Pattern p = Pattern.compile("[^a-zA-Z]*([a-zA-Z]).*");
String str = address.getPersonal() != null ? address.getPersonal() : address.getAddress();
Matcher m = p.matcher(str);
if (m.matches()) {
letter = m.group(1).toUpperCase();
}
return letter.length() == 0 ? 'K' : letter.charAt(0);
}
/** Calculates a bitmap with a color and a capital letter for
* contacts without picture.
* */
private Bitmap calculateFallbackBitmap(Address address) {
Bitmap result = Bitmap.createBitmap(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(result);
int rgb = calcUnknownContactColor(address);
result.eraseColor(rgb);
String letter = Character.toString(calcUnknownContactLetter(address));
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.FILL);
paint.setARGB(255, 255, 255, 255);
paint.setFakeBoldText(true);
paint.setTextSize(mPictureSizeInPx);
Rect rect = new Rect();
paint.getTextBounds(letter, 0, 1, rect);
float width = paint.measureText(letter);
canvas.drawText(letter,
mPictureSizeInPx/2f-width/2f,
mPictureSizeInPx/2f+rect.height()/2f, paint);
return result;
}
private void addBitmapToCache(String key, Bitmap bitmap) { private void addBitmapToCache(String key, Bitmap bitmap) {
if (getBitmapFromCache(key) == null) { if (getBitmapFromCache(key) == null) {
mBitmapCache.put(key, bitmap); mBitmapCache.put(key, bitmap);
@ -176,7 +232,7 @@ public class ContactPictureLoader {
final ContactPictureRetrievalTask task = getContactPictureRetrievalTask(badge); final ContactPictureRetrievalTask task = getContactPictureRetrievalTask(badge);
if (task != null && email != null) { if (task != null && email != null) {
String emailFromTask = task.getEmail(); String emailFromTask = task.getAddress().getAddress();
if (!email.equals(emailFromTask)) { if (!email.equals(emailFromTask)) {
// Cancel previous task // Cancel previous task
task.cancel(true); task.cancel(true);
@ -208,7 +264,7 @@ public class ContactPictureLoader {
*/ */
class ContactPictureRetrievalTask extends AsyncTask<String, Void, Bitmap> { class ContactPictureRetrievalTask extends AsyncTask<String, Void, Bitmap> {
private final WeakReference<QuickContactBadge> mQuickContactBadgeReference; private final WeakReference<QuickContactBadge> mQuickContactBadgeReference;
private String mEmail; private Address mAddress;
ContactPictureRetrievalTask(QuickContactBadge badge) { ContactPictureRetrievalTask(QuickContactBadge badge) {
mQuickContactBadgeReference = new WeakReference<QuickContactBadge>(badge); mQuickContactBadgeReference = new WeakReference<QuickContactBadge>(badge);
@ -222,14 +278,15 @@ public class ContactPictureLoader {
} }
} }
public String getEmail() { public Address getAddress() {
return mEmail; return mAddress;
} }
@Override @Override
protected Bitmap doInBackground(String... args) { protected Bitmap doInBackground(String... args) {
String email = args[0]; String email = args[0];
mEmail = email; String personal = args[1];
mAddress = new Address(email, personal);
final Uri x = mContactsHelper.getPhotoUri(email); final Uri x = mContactsHelper.getPhotoUri(email);
Bitmap bitmap = null; Bitmap bitmap = null;
if (x != null) { if (x != null) {
@ -256,7 +313,7 @@ public class ContactPictureLoader {
} }
if (bitmap == null) { if (bitmap == null) {
bitmap = mDefaultPicture; bitmap = calculateFallbackBitmap(mAddress);
// Remember that we don't have a contact picture for this email address // Remember that we don't have a contact picture for this email address
addEmailToUnknownContactsCache(email); addEmailToUnknownContactsCache(email);

View File

@ -1875,15 +1875,15 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
CharSequence displayName = mMessageHelper.getDisplayName(account, fromAddrs, toAddrs); CharSequence displayName = mMessageHelper.getDisplayName(account, fromAddrs, toAddrs);
CharSequence displayDate = DateUtils.getRelativeTimeSpanString(context, cursor.getLong(DATE_COLUMN)); CharSequence displayDate = DateUtils.getRelativeTimeSpanString(context, cursor.getLong(DATE_COLUMN));
String counterpartyAddress = null; Address counterpartyAddress = null;
if (fromMe) { if (fromMe) {
if (toAddrs.length > 0) { if (toAddrs.length > 0) {
counterpartyAddress = toAddrs[0].getAddress(); counterpartyAddress = toAddrs[0];
} else if (ccAddrs.length > 0) { } else if (ccAddrs.length > 0) {
counterpartyAddress = ccAddrs[0].getAddress(); counterpartyAddress = ccAddrs[0];
} }
} else if (fromAddrs.length > 0) { } else if (fromAddrs.length > 0) {
counterpartyAddress = fromAddrs[0].getAddress(); counterpartyAddress = fromAddrs[0];
} }
int threadCount = (mThreadedList) ? cursor.getInt(THREAD_COUNT_COLUMN) : 0; int threadCount = (mThreadedList) ? cursor.getInt(THREAD_COUNT_COLUMN) : 0;
@ -1923,7 +1923,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
holder.position = cursor.getPosition(); holder.position = cursor.getPosition();
if (holder.contactBadge != null) { if (holder.contactBadge != null) {
holder.contactBadge.assignContactFromEmail(counterpartyAddress, true); holder.contactBadge.assignContactFromEmail(counterpartyAddress.getAddress(), true);
if (counterpartyAddress != null) { if (counterpartyAddress != null) {
/* /*
* At least in Android 2.2 a different background + padding is used when no * At least in Android 2.2 a different background + padding is used when no

View File

@ -226,15 +226,15 @@ public class MessageHeader extends ScrollView implements OnClickListener {
Address[] ccAddrs = message.getRecipients(Message.RecipientType.CC); Address[] ccAddrs = message.getRecipients(Message.RecipientType.CC);
boolean fromMe = mMessageHelper.toMe(account, fromAddrs); boolean fromMe = mMessageHelper.toMe(account, fromAddrs);
String counterpartyAddress = null; Address counterpartyAddress = null;
if (fromMe) { if (fromMe) {
if (toAddrs.length > 0) { if (toAddrs.length > 0) {
counterpartyAddress = toAddrs[0].getAddress(); counterpartyAddress = toAddrs[0];
} else if (ccAddrs.length > 0) { } else if (ccAddrs.length > 0) {
counterpartyAddress = ccAddrs[0].getAddress(); counterpartyAddress = ccAddrs[0];
} }
} else if (fromAddrs.length > 0) { } else if (fromAddrs.length > 0) {
counterpartyAddress = fromAddrs[0].getAddress(); counterpartyAddress = fromAddrs[0];
} }
/* /*
@ -275,7 +275,7 @@ public class MessageHeader extends ScrollView implements OnClickListener {
mDateView.setText(dateTime); mDateView.setText(dateTime);
if (K9.showContactPicture()) { if (K9.showContactPicture()) {
mContactBadge.assignContactFromEmail(counterpartyAddress, true); mContactBadge.assignContactFromEmail(counterpartyAddress.getAddress(), true);
if (counterpartyAddress != null) { if (counterpartyAddress != null) {
mContactsPictureLoader.loadContactPicture(counterpartyAddress, mContactBadge); mContactsPictureLoader.loadContactPicture(counterpartyAddress, mContactBadge);
} else { } else {