Loading app/k9mail/src/main/java/com/fsck/k9/directshare/K9ChooserTargetService.java +2 −2 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import android.support.annotation.Nullable; import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.compose.RecipientLoader; import com.fsck.k9.activity.misc.ContactPicture; import com.fsck.k9.activity.misc.ContactPictureLoader; import com.fsck.k9.contacts.ContactPictureLoader; import com.fsck.k9.mail.Address; import com.fsck.k9.view.RecipientSelectView.Recipient; Loading @@ -39,7 +39,7 @@ public class K9ChooserTargetService extends ChooserTargetService { Context applicationContext = getApplicationContext(); recipientLoader = RecipientLoader.getMostContactedRecipientLoader(applicationContext, MAX_TARGETS); contactPictureLoader = ContactPicture.getContactPictureLoader(applicationContext); contactPictureLoader = ContactPicture.getContactPictureLoader(); } @Override Loading app/ui/src/main/java/com/fsck/k9/UiKoinModules.kt +3 −1 Original line number Diff line number Diff line package com.fsck.k9 import com.fsck.k9.activity.activityModule import com.fsck.k9.contacts.contactsModule import com.fsck.k9.fragment.fragmentModule import com.fsck.k9.ui.endtoend.endToEndUiModule import com.fsck.k9.ui.settings.settingsUiModule Loading @@ -11,5 +12,6 @@ val uiModules = listOf( uiModule, settingsUiModule, endToEndUiModule, fragmentModule fragmentModule, contactsModule ) app/ui/src/main/java/com/fsck/k9/activity/compose/RecipientAdapter.java +1 −1 Original line number Diff line number Diff line Loading @@ -153,7 +153,7 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { } public static void setContactPhotoOrPlaceholder(Context context, ImageView imageView, Recipient recipient) { ContactPicture.getContactPictureLoader(context).loadContactPicture(recipient, imageView); ContactPicture.getContactPictureLoader().loadContactPicture(recipient, imageView); } @Override Loading app/ui/src/main/java/com/fsck/k9/activity/misc/ContactPicture.java +5 −16 Original line number Diff line number Diff line package com.fsck.k9.activity.misc; import android.content.Context; import android.util.TypedValue; import com.fsck.k9.K9; import com.fsck.k9.ui.R; import com.fsck.k9.DI; import com.fsck.k9.contacts.ContactPictureLoader; public class ContactPicture { public static ContactPictureLoader getContactPictureLoader(Context context) { final int defaultBgColor; if (!K9.isColorizeMissingContactPictures()) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.contactPictureFallbackDefaultBackgroundColor, outValue, true); defaultBgColor = outValue.data; } else { defaultBgColor = 0; } public class ContactPicture { return new ContactPictureLoader(context, defaultBgColor); public static ContactPictureLoader getContactPictureLoader() { return DI.get(ContactPictureLoader.class); } } app/ui/src/main/java/com/fsck/k9/activity/misc/ContactPictureLoader.javadeleted 100644 → 0 +0 −319 Original line number Diff line number Diff line package com.fsck.k9.activity.misc; import java.io.IOException; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.net.Uri; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.text.TextUtils; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.Priority; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.resource.bitmap.BitmapEncoder; import com.bumptech.glide.load.resource.bitmap.BitmapResource; import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.load.resource.file.FileToStreamDecoder; import com.bumptech.glide.load.resource.transcode.BitmapToGlideDrawableTranscoder; import com.bumptech.glide.request.FutureTarget; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import com.fsck.k9.helper.Contacts; import com.fsck.k9.mail.Address; import com.fsck.k9.view.RecipientSelectView.Recipient; public class ContactPictureLoader { /** * Resize the pictures to the following value (device-independent pixels). */ private static final int PICTURE_SIZE = 40; /** * Pattern to extract the letter to be displayed as fallback image. */ private static final Pattern EXTRACT_LETTER_PATTERN = Pattern.compile("\\p{L}\\p{M}*"); /** * Letter to use when {@link #EXTRACT_LETTER_PATTERN} couldn't find a match. */ private static final String FALLBACK_CONTACT_LETTER = "?"; private final Context context; private Contacts mContactsHelper; private int mPictureSizeInPx; private int mDefaultBackgroundColor; /** * @see <a href="http://developer.android.com/design/style/color.html">Color palette used</a> */ private final static int CONTACT_DUMMY_COLORS_ARGB[] = { 0xff33B5E5, 0xffAA66CC, 0xff99CC00, 0xffFFBB33, 0xffFF4444, 0xff0099CC, 0xff9933CC, 0xff669900, 0xffFF8800, 0xffCC0000 }; @VisibleForTesting protected static String calcUnknownContactLetter(Address address) { String letter = null; String personal = address.getPersonal(); String str = (personal != null) ? personal : address.getAddress(); Matcher m = EXTRACT_LETTER_PATTERN.matcher(str); if (m.find()) { letter = m.group(0).toUpperCase(Locale.US); } return (TextUtils.isEmpty(letter)) ? FALLBACK_CONTACT_LETTER : letter; } /** * Constructor. * * @param context * A {@link Context} instance. * @param defaultBackgroundColor * The ARGB value to be used as background color for the fallback picture. {@code 0} to * use a dynamically calculated background color. */ public ContactPictureLoader(Context context, int defaultBackgroundColor) { this.context = context.getApplicationContext(); mContactsHelper = Contacts.getInstance(this.context); Resources resources = context.getResources(); float scale = resources.getDisplayMetrics().density; mPictureSizeInPx = (int) (PICTURE_SIZE * scale); mDefaultBackgroundColor = defaultBackgroundColor; } public void loadContactPicture(final Address address, final ImageView imageView) { Uri photoUri = mContactsHelper.getPhotoUri(address.getAddress()); loadContactPicture(photoUri, address, imageView); } public void loadContactPicture(Recipient recipient, ImageView imageView) { loadContactPicture(recipient.photoThumbnailUri, recipient.address, imageView); } private void loadFallbackPicture(Address address, ImageView imageView) { Context context = imageView.getContext(); Glide.with(context) .using(new FallbackGlideModelLoader(), FallbackGlideParams.class) .from(FallbackGlideParams.class) .as(Bitmap.class) .transcode(new BitmapToGlideDrawableTranscoder(context), GlideDrawable.class) .decoder(new FallbackGlideBitmapDecoder(context)) .encoder(new BitmapEncoder(Bitmap.CompressFormat.PNG, 0)) .cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context))) .diskCacheStrategy(DiskCacheStrategy.NONE) .load(new FallbackGlideParams(address)) // for some reason, following 2 lines fix loading issues. .dontAnimate() .override(mPictureSizeInPx, mPictureSizeInPx) .into(imageView); } private void loadContactPicture(Uri photoUri, final Address address, final ImageView imageView) { if (photoUri != null) { RequestListener<Uri, GlideDrawable> noPhotoListener = new RequestListener<Uri, GlideDrawable>() { @Override public boolean onException(Exception e, Uri model, Target<GlideDrawable> target, boolean isFirstResource) { loadFallbackPicture(address, imageView); return true; } @Override public boolean onResourceReady(GlideDrawable resource, Uri model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { return false; } }; Glide.with(imageView.getContext()) .load(photoUri) .diskCacheStrategy(DiskCacheStrategy.NONE) .listener(noPhotoListener) // for some reason, following 2 lines fix loading issues. .dontAnimate() .override(mPictureSizeInPx, mPictureSizeInPx) .into(imageView); } else { loadFallbackPicture(address, imageView); } } public Bitmap loadContactPictureIcon(Recipient recipient) { return loadContactPicture(recipient.photoThumbnailUri, recipient.address); } @WorkerThread private Bitmap loadContactPicture(Uri photoUri, Address address) { FutureTarget<Bitmap> bitmapTarget; if (photoUri != null) { bitmapTarget = Glide.with(context) .load(photoUri) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.NONE) .dontAnimate() .into(mPictureSizeInPx, mPictureSizeInPx); } else { bitmapTarget = Glide.with(context) .using(new FallbackGlideModelLoader(), FallbackGlideParams.class) .from(FallbackGlideParams.class) .as(Bitmap.class) .decoder(new FallbackGlideBitmapDecoder(context)) .encoder(new BitmapEncoder(CompressFormat.PNG, 0)) .cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context))) .diskCacheStrategy(DiskCacheStrategy.NONE) .load(new FallbackGlideParams(address)) .dontAnimate() .into(mPictureSizeInPx, mPictureSizeInPx); } return loadIgnoringErors(bitmapTarget); } private int calcUnknownContactColor(Address address) { if (mDefaultBackgroundColor != 0) { return mDefaultBackgroundColor; } int val = address.hashCode(); int colorIndex = (val & Integer.MAX_VALUE) % CONTACT_DUMMY_COLORS_ARGB.length; return CONTACT_DUMMY_COLORS_ARGB[colorIndex]; } private Bitmap drawTextAndBgColorOnBitmap(Bitmap bitmap, FallbackGlideParams params) { Canvas canvas = new Canvas(bitmap); int rgb = calcUnknownContactColor(params.address); bitmap.eraseColor(rgb); String letter = calcUnknownContactLetter(params.address); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Style.FILL); paint.setARGB(255, 255, 255, 255); paint.setTextSize(mPictureSizeInPx * 3 / 4); // just scale this down a bit 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 bitmap; } private class FallbackGlideBitmapDecoder implements ResourceDecoder<FallbackGlideParams, Bitmap> { private final Context context; FallbackGlideBitmapDecoder(Context context) { this.context = context; } @Override public Resource<Bitmap> decode(FallbackGlideParams source, int width, int height) throws IOException { BitmapPool pool = Glide.get(context).getBitmapPool(); Bitmap bitmap = pool.getDirty(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888); if (bitmap == null) { bitmap = Bitmap.createBitmap(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888); } drawTextAndBgColorOnBitmap(bitmap, source); return BitmapResource.obtain(bitmap, pool); } @Override public String getId() { return "fallback-photo"; } } private class FallbackGlideParams { final Address address; FallbackGlideParams(Address address) { this.address = address; } public String getId() { return String.format(Locale.ROOT, "%s-%s", address.getAddress(), address.getPersonal()); } } private class FallbackGlideModelLoader implements ModelLoader<FallbackGlideParams, FallbackGlideParams> { @Override public DataFetcher<FallbackGlideParams> getResourceFetcher(final FallbackGlideParams model, int width, int height) { return new DataFetcher<FallbackGlideParams>() { @Override public FallbackGlideParams loadData(Priority priority) throws Exception { return model; } @Override public void cleanup() { } @Override public String getId() { return model.getId(); } @Override public void cancel() { } }; } } @WorkerThread @Nullable private <T> T loadIgnoringErors(FutureTarget<T> target) { try { return target.get(); } catch (Exception e) { return null; } } } Loading
app/k9mail/src/main/java/com/fsck/k9/directshare/K9ChooserTargetService.java +2 −2 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ import android.support.annotation.Nullable; import com.fsck.k9.activity.MessageCompose; import com.fsck.k9.activity.compose.RecipientLoader; import com.fsck.k9.activity.misc.ContactPicture; import com.fsck.k9.activity.misc.ContactPictureLoader; import com.fsck.k9.contacts.ContactPictureLoader; import com.fsck.k9.mail.Address; import com.fsck.k9.view.RecipientSelectView.Recipient; Loading @@ -39,7 +39,7 @@ public class K9ChooserTargetService extends ChooserTargetService { Context applicationContext = getApplicationContext(); recipientLoader = RecipientLoader.getMostContactedRecipientLoader(applicationContext, MAX_TARGETS); contactPictureLoader = ContactPicture.getContactPictureLoader(applicationContext); contactPictureLoader = ContactPicture.getContactPictureLoader(); } @Override Loading
app/ui/src/main/java/com/fsck/k9/UiKoinModules.kt +3 −1 Original line number Diff line number Diff line package com.fsck.k9 import com.fsck.k9.activity.activityModule import com.fsck.k9.contacts.contactsModule import com.fsck.k9.fragment.fragmentModule import com.fsck.k9.ui.endtoend.endToEndUiModule import com.fsck.k9.ui.settings.settingsUiModule Loading @@ -11,5 +12,6 @@ val uiModules = listOf( uiModule, settingsUiModule, endToEndUiModule, fragmentModule fragmentModule, contactsModule )
app/ui/src/main/java/com/fsck/k9/activity/compose/RecipientAdapter.java +1 −1 Original line number Diff line number Diff line Loading @@ -153,7 +153,7 @@ public class RecipientAdapter extends BaseAdapter implements Filterable { } public static void setContactPhotoOrPlaceholder(Context context, ImageView imageView, Recipient recipient) { ContactPicture.getContactPictureLoader(context).loadContactPicture(recipient, imageView); ContactPicture.getContactPictureLoader().loadContactPicture(recipient, imageView); } @Override Loading
app/ui/src/main/java/com/fsck/k9/activity/misc/ContactPicture.java +5 −16 Original line number Diff line number Diff line package com.fsck.k9.activity.misc; import android.content.Context; import android.util.TypedValue; import com.fsck.k9.K9; import com.fsck.k9.ui.R; import com.fsck.k9.DI; import com.fsck.k9.contacts.ContactPictureLoader; public class ContactPicture { public static ContactPictureLoader getContactPictureLoader(Context context) { final int defaultBgColor; if (!K9.isColorizeMissingContactPictures()) { TypedValue outValue = new TypedValue(); context.getTheme().resolveAttribute(R.attr.contactPictureFallbackDefaultBackgroundColor, outValue, true); defaultBgColor = outValue.data; } else { defaultBgColor = 0; } public class ContactPicture { return new ContactPictureLoader(context, defaultBgColor); public static ContactPictureLoader getContactPictureLoader() { return DI.get(ContactPictureLoader.class); } }
app/ui/src/main/java/com/fsck/k9/activity/misc/ContactPictureLoader.javadeleted 100644 → 0 +0 −319 Original line number Diff line number Diff line package com.fsck.k9.activity.misc; import java.io.IOException; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.Style; import android.graphics.Rect; import android.net.Uri; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import android.support.annotation.WorkerThread; import android.text.TextUtils; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.bumptech.glide.Priority; import com.bumptech.glide.load.ResourceDecoder; import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.model.ModelLoader; import com.bumptech.glide.load.resource.bitmap.BitmapEncoder; import com.bumptech.glide.load.resource.bitmap.BitmapResource; import com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.load.resource.file.FileToStreamDecoder; import com.bumptech.glide.load.resource.transcode.BitmapToGlideDrawableTranscoder; import com.bumptech.glide.request.FutureTarget; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.target.Target; import com.fsck.k9.helper.Contacts; import com.fsck.k9.mail.Address; import com.fsck.k9.view.RecipientSelectView.Recipient; public class ContactPictureLoader { /** * Resize the pictures to the following value (device-independent pixels). */ private static final int PICTURE_SIZE = 40; /** * Pattern to extract the letter to be displayed as fallback image. */ private static final Pattern EXTRACT_LETTER_PATTERN = Pattern.compile("\\p{L}\\p{M}*"); /** * Letter to use when {@link #EXTRACT_LETTER_PATTERN} couldn't find a match. */ private static final String FALLBACK_CONTACT_LETTER = "?"; private final Context context; private Contacts mContactsHelper; private int mPictureSizeInPx; private int mDefaultBackgroundColor; /** * @see <a href="http://developer.android.com/design/style/color.html">Color palette used</a> */ private final static int CONTACT_DUMMY_COLORS_ARGB[] = { 0xff33B5E5, 0xffAA66CC, 0xff99CC00, 0xffFFBB33, 0xffFF4444, 0xff0099CC, 0xff9933CC, 0xff669900, 0xffFF8800, 0xffCC0000 }; @VisibleForTesting protected static String calcUnknownContactLetter(Address address) { String letter = null; String personal = address.getPersonal(); String str = (personal != null) ? personal : address.getAddress(); Matcher m = EXTRACT_LETTER_PATTERN.matcher(str); if (m.find()) { letter = m.group(0).toUpperCase(Locale.US); } return (TextUtils.isEmpty(letter)) ? FALLBACK_CONTACT_LETTER : letter; } /** * Constructor. * * @param context * A {@link Context} instance. * @param defaultBackgroundColor * The ARGB value to be used as background color for the fallback picture. {@code 0} to * use a dynamically calculated background color. */ public ContactPictureLoader(Context context, int defaultBackgroundColor) { this.context = context.getApplicationContext(); mContactsHelper = Contacts.getInstance(this.context); Resources resources = context.getResources(); float scale = resources.getDisplayMetrics().density; mPictureSizeInPx = (int) (PICTURE_SIZE * scale); mDefaultBackgroundColor = defaultBackgroundColor; } public void loadContactPicture(final Address address, final ImageView imageView) { Uri photoUri = mContactsHelper.getPhotoUri(address.getAddress()); loadContactPicture(photoUri, address, imageView); } public void loadContactPicture(Recipient recipient, ImageView imageView) { loadContactPicture(recipient.photoThumbnailUri, recipient.address, imageView); } private void loadFallbackPicture(Address address, ImageView imageView) { Context context = imageView.getContext(); Glide.with(context) .using(new FallbackGlideModelLoader(), FallbackGlideParams.class) .from(FallbackGlideParams.class) .as(Bitmap.class) .transcode(new BitmapToGlideDrawableTranscoder(context), GlideDrawable.class) .decoder(new FallbackGlideBitmapDecoder(context)) .encoder(new BitmapEncoder(Bitmap.CompressFormat.PNG, 0)) .cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context))) .diskCacheStrategy(DiskCacheStrategy.NONE) .load(new FallbackGlideParams(address)) // for some reason, following 2 lines fix loading issues. .dontAnimate() .override(mPictureSizeInPx, mPictureSizeInPx) .into(imageView); } private void loadContactPicture(Uri photoUri, final Address address, final ImageView imageView) { if (photoUri != null) { RequestListener<Uri, GlideDrawable> noPhotoListener = new RequestListener<Uri, GlideDrawable>() { @Override public boolean onException(Exception e, Uri model, Target<GlideDrawable> target, boolean isFirstResource) { loadFallbackPicture(address, imageView); return true; } @Override public boolean onResourceReady(GlideDrawable resource, Uri model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { return false; } }; Glide.with(imageView.getContext()) .load(photoUri) .diskCacheStrategy(DiskCacheStrategy.NONE) .listener(noPhotoListener) // for some reason, following 2 lines fix loading issues. .dontAnimate() .override(mPictureSizeInPx, mPictureSizeInPx) .into(imageView); } else { loadFallbackPicture(address, imageView); } } public Bitmap loadContactPictureIcon(Recipient recipient) { return loadContactPicture(recipient.photoThumbnailUri, recipient.address); } @WorkerThread private Bitmap loadContactPicture(Uri photoUri, Address address) { FutureTarget<Bitmap> bitmapTarget; if (photoUri != null) { bitmapTarget = Glide.with(context) .load(photoUri) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.NONE) .dontAnimate() .into(mPictureSizeInPx, mPictureSizeInPx); } else { bitmapTarget = Glide.with(context) .using(new FallbackGlideModelLoader(), FallbackGlideParams.class) .from(FallbackGlideParams.class) .as(Bitmap.class) .decoder(new FallbackGlideBitmapDecoder(context)) .encoder(new BitmapEncoder(CompressFormat.PNG, 0)) .cacheDecoder(new FileToStreamDecoder<>(new StreamBitmapDecoder(context))) .diskCacheStrategy(DiskCacheStrategy.NONE) .load(new FallbackGlideParams(address)) .dontAnimate() .into(mPictureSizeInPx, mPictureSizeInPx); } return loadIgnoringErors(bitmapTarget); } private int calcUnknownContactColor(Address address) { if (mDefaultBackgroundColor != 0) { return mDefaultBackgroundColor; } int val = address.hashCode(); int colorIndex = (val & Integer.MAX_VALUE) % CONTACT_DUMMY_COLORS_ARGB.length; return CONTACT_DUMMY_COLORS_ARGB[colorIndex]; } private Bitmap drawTextAndBgColorOnBitmap(Bitmap bitmap, FallbackGlideParams params) { Canvas canvas = new Canvas(bitmap); int rgb = calcUnknownContactColor(params.address); bitmap.eraseColor(rgb); String letter = calcUnknownContactLetter(params.address); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Style.FILL); paint.setARGB(255, 255, 255, 255); paint.setTextSize(mPictureSizeInPx * 3 / 4); // just scale this down a bit 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 bitmap; } private class FallbackGlideBitmapDecoder implements ResourceDecoder<FallbackGlideParams, Bitmap> { private final Context context; FallbackGlideBitmapDecoder(Context context) { this.context = context; } @Override public Resource<Bitmap> decode(FallbackGlideParams source, int width, int height) throws IOException { BitmapPool pool = Glide.get(context).getBitmapPool(); Bitmap bitmap = pool.getDirty(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888); if (bitmap == null) { bitmap = Bitmap.createBitmap(mPictureSizeInPx, mPictureSizeInPx, Bitmap.Config.ARGB_8888); } drawTextAndBgColorOnBitmap(bitmap, source); return BitmapResource.obtain(bitmap, pool); } @Override public String getId() { return "fallback-photo"; } } private class FallbackGlideParams { final Address address; FallbackGlideParams(Address address) { this.address = address; } public String getId() { return String.format(Locale.ROOT, "%s-%s", address.getAddress(), address.getPersonal()); } } private class FallbackGlideModelLoader implements ModelLoader<FallbackGlideParams, FallbackGlideParams> { @Override public DataFetcher<FallbackGlideParams> getResourceFetcher(final FallbackGlideParams model, int width, int height) { return new DataFetcher<FallbackGlideParams>() { @Override public FallbackGlideParams loadData(Priority priority) throws Exception { return model; } @Override public void cleanup() { } @Override public String getId() { return model.getId(); } @Override public void cancel() { } }; } } @WorkerThread @Nullable private <T> T loadIgnoringErors(FutureTarget<T> target) { try { return target.get(); } catch (Exception e) { return null; } } }