From 2b6a4260124e06211bfc9a6f61e21a48d313ff1c Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 26 Jan 2019 09:58:37 +0000 Subject: [PATCH] Improved avatar/identicon caching --- .../eu/faircode/email/AdapterMessage.java | 79 +++------ .../java/eu/faircode/email/ContactInfo.java | 167 +++++++++--------- .../eu/faircode/email/FragmentOptions.java | 2 + .../eu/faircode/email/ServiceSynchronize.java | 15 +- 4 files changed, 118 insertions(+), 145 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/AdapterMessage.java b/app/src/main/java/eu/faircode/email/AdapterMessage.java index c467c1cbb0..5d5621853c 100644 --- a/app/src/main/java/eu/faircode/email/AdapterMessage.java +++ b/app/src/main/java/eu/faircode/email/AdapterMessage.java @@ -32,7 +32,6 @@ import android.database.Cursor; import android.graphics.Color; import android.graphics.Rect; import android.graphics.Typeface; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; @@ -75,7 +74,6 @@ import org.xml.sax.XMLReader; import java.io.File; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.text.Collator; import java.text.DateFormat; import java.text.SimpleDateFormat; @@ -130,18 +128,15 @@ public class AdapterMessage extends RecyclerView.Adapter selectionTracker = null; @@ -484,82 +479,58 @@ public class AdapterMessage extends RecyclerView.Adapter() { @Override protected void onPreExecute(Bundle args) { ivAvatar.setTag(message.id); - ivAvatar.setVisibility(View.INVISIBLE); tvFrom.setTag(message.id); - Address[] addresses = (Address[]) args.getSerializable("addresses"); - ContactInfo info = ContactInfo.get(context, addresses, true); - if (info != null && info.hasDisplayName()) - setFrom(info, addresses); - else - tvFrom.setText(MessageHelper.formatAddresses(addresses, !compact, false)); + ivAvatar.setVisibility(avatars ? View.INVISIBLE : View.GONE); + tvFrom.setText(MessageHelper.formatAddresses(addresses, !compact, false)); } @Override protected ContactInfo onExecute(Context context, Bundle args) { Address[] addresses = (Address[]) args.getSerializable("addresses"); - - ContactInfo info = ContactInfo.get(context, addresses, false); - - if ((info == null || !info.hasPhoto()) && - identicons && addresses != null && addresses.length > 0) { - Drawable ident = new BitmapDrawable( - context.getResources(), - Identicon.generate(addresses[0].toString(), - dp24, 5, "light".equals(theme))); - info = new ContactInfo(ident, (info == null ? null : info.getDisplayName())); - } - - return info; + return ContactInfo.get(context, addresses, false); } @Override protected void onExecuted(Bundle args, ContactInfo info) { - long id = args.getLong("id"); + Long id = args.getLong("id"); - if ((long) ivAvatar.getTag() == id) { - if (info == null || !info.hasPhoto()) - ivAvatar.setImageResource(R.drawable.baseline_person_24); + if (id.equals(ivAvatar.getTag())) { + if (info.hasPhoto()) + ivAvatar.setImageBitmap(info.getPhotoBitmap()); else - ivAvatar.setImageDrawable(info.getPhotoDrawable()); - ivAvatar.setVisibility(View.VISIBLE); + ivAvatar.setImageResource(R.drawable.baseline_person_24); + ivAvatar.setVisibility(avatars ? View.VISIBLE : View.GONE); } - if ((long) tvFrom.getTag() == id) { - if (info != null && info.hasDisplayName()) { - Address[] addresses = (Address[]) args.getSerializable("addresses"); - setFrom(info, addresses); - } - } + if (id.equals(tvFrom.getTag())) + tvFrom.setText(info.getDisplayName(compact)); } @Override protected void onException(Bundle args, Throwable ex) { Helper.unexpectedError(context, owner, ex); } - - - private void setFrom(ContactInfo info, Address[] addresses) { - try { - ((InternetAddress) addresses[0]).setPersonal(info.getDisplayName()); - tvFrom.setText(MessageHelper.formatAddresses(addresses, !compact, false)); - } catch (UnsupportedEncodingException ex) { - Log.w(ex); - } - } }.execute(context, owner, aargs, "message:avatar"); } else { - ivAvatar.setVisibility(View.GONE); - tvFrom.setText(MessageHelper.formatAddresses(outgoing ? message.to : message.from, !compact, false)); + if (info.hasPhoto()) + ivAvatar.setImageBitmap(info.getPhotoBitmap()); + else + ivAvatar.setImageResource(R.drawable.baseline_person_24); + ivAvatar.setVisibility(avatars ? View.VISIBLE : View.GONE); + tvFrom.setText(info.getDisplayName(compact)); } vwColor.setBackgroundColor(message.accountColor == null ? Color.TRANSPARENT : message.accountColor); @@ -2174,19 +2145,17 @@ public class AdapterMessage extends RecyclerView.Adapter"; } boolean hasLookupUri() { return (lookupUri != null); } + Uri getLookupUri() { + return lookupUri; + } + private boolean isExpired() { return (new Date().getTime() - time > CACHE_DURATION); } - static ContactInfo get(Context context, Address[] addresses, boolean cached) { + static void clearCache() { + synchronized (emailContactInfo) { + emailContactInfo.clear(); + } + } + + static ContactInfo get(Context context, Address[] addresses, boolean cacheOnly) { if (addresses == null || addresses.length == 0) - return null; - - if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) - != PackageManager.PERMISSION_GRANTED) - return null; - - String email = ((InternetAddress) addresses[0]).getAddress(); + return new ContactInfo(); + InternetAddress address = (InternetAddress) addresses[0]; + String email = address.getAddress(); synchronized (emailContactInfo) { ContactInfo info = emailContactInfo.get(email); if (info != null && !info.isExpired()) return info; } - if (cached) + + if (cacheOnly) return null; - try { - Cursor cursor = null; + ContactInfo info = new ContactInfo(); + info.email = email; + + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CONTACTS) + == PackageManager.PERMISSION_GRANTED) try { - ContentResolver resolver = context.getContentResolver(); - cursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, - new String[]{ - ContactsContract.CommonDataKinds.Photo.CONTACT_ID, - ContactsContract.Contacts.LOOKUP_KEY, - ContactsContract.Contacts.DISPLAY_NAME - }, - ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?", - new String[]{ - email - }, null); + Cursor cursor = null; + try { + ContentResolver resolver = context.getContentResolver(); + cursor = resolver.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, + new String[]{ + ContactsContract.CommonDataKinds.Photo.CONTACT_ID, + ContactsContract.Contacts.LOOKUP_KEY, + ContactsContract.Contacts.DISPLAY_NAME + }, + ContactsContract.CommonDataKinds.Email.ADDRESS + " = ?", + new String[]{ + email + }, null); - if (cursor != null && cursor.moveToNext()) { - int colContactId = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID); - int colLookupKey = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); - int colDisplayName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); + if (cursor != null && cursor.moveToNext()) { + int colContactId = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.CONTACT_ID); + int colLookupKey = cursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY); + int colDisplayName = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME); - long contactId = cursor.getLong(colContactId); - String lookupKey = cursor.getString(colLookupKey); - Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); + long contactId = cursor.getLong(colContactId); + String lookupKey = cursor.getString(colLookupKey); + Uri lookupUri = ContactsContract.Contacts.getLookupUri(contactId, lookupKey); - ContactInfo info = new ContactInfo(); - info.is = ContactsContract.Contacts.openContactPhotoInputStream(resolver, lookupUri); - info.displayName = cursor.getString(colDisplayName); - info.lookupUri = lookupUri; - info.time = new Date().getTime(); + boolean avatars = prefs.getBoolean("avatars", true); + if (avatars) { + InputStream is = ContactsContract.Contacts.openContactPhotoInputStream(resolver, lookupUri); + info.bitmap = BitmapFactory.decodeStream(is); + } - synchronized (emailContactInfo) { - emailContactInfo.put(email, info); + info.displayName = cursor.getString(colDisplayName); + info.lookupUri = lookupUri; } - - return info; + } finally { + if (cursor != null) + cursor.close(); } - } finally { - if (cursor != null) - cursor.close(); + } catch (Throwable ex) { + Log.e(ex); + } + + if (info.bitmap == null) { + boolean identicons = prefs.getBoolean("identicons", false); + if (identicons) { + String theme = prefs.getString("theme", "light"); + int dp = Helper.dp2pixels(context, 48); + info.bitmap = Identicon.generate(email, dp, 5, "light".equals(theme)); } - } catch (Throwable ex) { - Log.e(ex); } - return null; + if (info.displayName == null) + info.displayName = address.getPersonal(); + + synchronized (emailContactInfo) { + emailContactInfo.put(email, info); + } + + info.time = new Date().getTime(); + return info; } } diff --git a/app/src/main/java/eu/faircode/email/FragmentOptions.java b/app/src/main/java/eu/faircode/email/FragmentOptions.java index fdd47bdf9f..2d32c4daa4 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptions.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptions.java @@ -240,6 +240,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { prefs.edit().putBoolean("avatars", checked).apply(); + ContactInfo.clearCache(); } }); @@ -247,6 +248,7 @@ public class FragmentOptions extends FragmentBase implements SharedPreferences.O @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { prefs.edit().putBoolean("identicons", checked).apply(); + ContactInfo.clearCache(); } }); diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index 46820c2548..b98ef110d7 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -488,14 +488,9 @@ public class ServiceSynchronize extends LifecycleService { // Get contact info Map messageContact = new HashMap<>(); - for (TupleMessageEx message : messages) { - ContactInfo info = ContactInfo.get(this, message.from, true); - if (info == null) - info = ContactInfo.get(this, message.from, false); - if (info == null) - info = new ContactInfo(MessageHelper.formatAddressesShort(message.from)); - messageContact.put(message, info); - } + for (TupleMessageEx message : messages) + messageContact.put(message, + ContactInfo.get(this, message.from, false)); // Build pending intent Intent view = new Intent(this, ActivityView.class); @@ -574,7 +569,7 @@ public class ServiceSynchronize extends LifecycleService { DateFormat df = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT); StringBuilder sb = new StringBuilder(); for (EntityMessage message : messages) { - sb.append("").append(messageContact.get(message).getDisplayName()).append(""); + sb.append("").append(messageContact.get(message).getDisplayName(true)).append(""); if (!TextUtils.isEmpty(message.subject)) sb.append(": ").append(message.subject); sb.append(" ").append(df.format(message.received)); @@ -647,7 +642,7 @@ public class ServiceSynchronize extends LifecycleService { mbuilder .addExtras(args) .setSmallIcon(R.drawable.baseline_email_white_24) - .setContentTitle(info.getDisplayName()) + .setContentTitle(info.getDisplayName(true)) .setSubText(message.accountName + " ยท " + folderName) .setContentIntent(piContent) .setWhen(message.received)