From 18ba5fa291d6b1713ea6de68e20b34a4786f5cda Mon Sep 17 00:00:00 2001 From: Lucio Maciel Date: Thu, 16 Sep 2021 13:40:51 -0300 Subject: [PATCH] Fix emoji avatar missing after edit. --- .../securesms/avatar/AvatarRenderer.kt | 5 +- .../securesms/avatar/TextAvatarDrawable.kt | 79 +++++++++++-------- .../securesms/components/AvatarImageView.java | 5 +- .../components/emoji/EmojiProvider.java | 70 +++++++++++++++- .../securesms/components/emoji/EmojiSpan.java | 11 +++ .../components/emoji/SimpleEmojiTextView.kt | 1 + .../avatars/GeneratedContactPhoto.java | 4 +- .../ConversationListFragment.java | 2 +- .../profiles/spoofing/ReviewBannerView.java | 2 +- .../securesms/recipients/Recipient.java | 27 ++++--- .../securesms/util/AvatarUtil.java | 30 ++++--- .../thoughtcrime/securesms/util/ViewUtil.java | 4 + .../res/layout/conversation_list_fragment.xml | 4 +- .../layout/conversation_list_item_view.xml | 4 +- app/src/main/res/values/dimens.xml | 2 + 15 files changed, 181 insertions(+), 69 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt index a3c4062330..1b7d6623b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/AvatarRenderer.kt @@ -48,8 +48,9 @@ object AvatarRenderer { avatar: Avatar.Text, inverted: Boolean = false, size: Int = DIMENSIONS, + synchronous: Boolean = false ): Drawable { - return TextAvatarDrawable(context, avatar, inverted, size) + return TextAvatarDrawable(context, avatar, inverted, size, synchronous) } private fun renderVector(context: Context, avatar: Avatar.Vector, onAvatarRendered: (Media) -> Unit, onRenderFailed: (Throwable?) -> Unit) { @@ -66,7 +67,7 @@ object AvatarRenderer { private fun renderText(context: Context, avatar: Avatar.Text, onAvatarRendered: (Media) -> Unit, onRenderFailed: (Throwable?) -> Unit) { renderInBackground(context, onAvatarRendered, onRenderFailed) { canvas -> - val textDrawable = createTextDrawable(context, avatar) + val textDrawable = createTextDrawable(context, avatar, synchronous = true) canvas.drawColor(avatar.color.backgroundColor) textDrawable.draw(canvas) diff --git a/app/src/main/java/org/thoughtcrime/securesms/avatar/TextAvatarDrawable.kt b/app/src/main/java/org/thoughtcrime/securesms/avatar/TextAvatarDrawable.kt index d41e010b56..5e4d97856b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/avatar/TextAvatarDrawable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/avatar/TextAvatarDrawable.kt @@ -3,52 +3,59 @@ package org.thoughtcrime.securesms.avatar import android.content.Context import android.graphics.Canvas import android.graphics.ColorFilter +import android.graphics.Paint import android.graphics.PixelFormat import android.graphics.drawable.Drawable -import android.util.TypedValue -import android.view.Gravity -import android.widget.FrameLayout -import androidx.core.view.updateLayoutParams -import org.thoughtcrime.securesms.components.emoji.EmojiTextView +import android.text.Layout +import android.text.SpannableString +import android.text.StaticLayout +import android.text.TextPaint +import androidx.core.graphics.withTranslation +import org.thoughtcrime.securesms.components.emoji.EmojiProvider -/** - * Uses EmojiTextView to properly render a Text Avatar with emoji in it. - */ class TextAvatarDrawable( - context: Context, - avatar: Avatar.Text, + private val context: Context, + private val avatar: Avatar.Text, inverted: Boolean = false, private val size: Int = AvatarRenderer.DIMENSIONS, + private val synchronous: Boolean = false ) : Drawable() { - private val layout: FrameLayout = FrameLayout(context) - private val textView: EmojiTextView = EmojiTextView(context) - + private val textPaint = TextPaint(Paint.ANTI_ALIAS_FLAG) init { - textView.typeface = AvatarRenderer.getTypeface(context) - textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, Avatars.getTextSizeForLength(context, avatar.text, size * 0.8f, size * 0.45f)) - textView.text = avatar.text - textView.gravity = Gravity.CENTER - textView.setTextColor(if (inverted) avatar.color.backgroundColor else avatar.color.foregroundColor) - textView.setForceCustomEmoji(true) + textPaint.typeface = AvatarRenderer.getTypeface(context) + textPaint.color = if (inverted) avatar.color.backgroundColor else avatar.color.foregroundColor + textPaint.density = context.resources.displayMetrics.density - layout.addView(textView) - - textView.updateLayoutParams { - width = size - height = size - } - - layout.measure(size, size) - layout.layout(0, 0, size, size) + setBounds(0, 0, size, size) } - override fun getIntrinsicHeight(): Int = size - - override fun getIntrinsicWidth(): Int = size - override fun draw(canvas: Canvas) { - layout.draw(canvas) + val textSize = Avatars.getTextSizeForLength(context, avatar.text, size * 0.8f, size * 0.45f) + val width = bounds.width() + val candidates = EmojiProvider.getCandidates(avatar.text) + var hasEmoji = false + + textPaint.textSize = textSize + + val newText = if (candidates == null || candidates.size() == 0) { + SpannableString(avatar.text) + } else { + EmojiProvider.emojify(context, candidates, avatar.text, textPaint, synchronous) + } + + if (newText == null) return + + val layout = StaticLayout(SpannableString(newText), textPaint, width, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true) + layout.draw(canvas, getStartX(layout), ((bounds.height() / 2) - ((layout.height / 2))).toFloat()) + } + + private fun getStartX(layout: StaticLayout): Float { + val direction = layout.getParagraphDirection(0) + val lineWidth = layout.getLineWidth(0) + val width = bounds.width() + val xPos = (width - lineWidth) / 2 + return if (direction == Layout.DIR_LEFT_TO_RIGHT) xPos else -xPos } override fun setAlpha(alpha: Int) = Unit @@ -56,4 +63,10 @@ class TextAvatarDrawable( override fun setColorFilter(colorFilter: ColorFilter?) = Unit override fun getOpacity(): Int = PixelFormat.OPAQUE + + private fun Layout.draw(canvas: Canvas, x: Float, y: Float) { + canvas.withTranslation(x, y) { + draw(canvas) + } + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java index 9ab63d0a43..bad96639c8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/AvatarImageView.java @@ -44,6 +44,7 @@ import org.thoughtcrime.securesms.util.AvatarUtil; import org.thoughtcrime.securesms.util.BlurTransformation; import org.thoughtcrime.securesms.util.ThemeUtil; import org.thoughtcrime.securesms.util.Util; +import org.thoughtcrime.securesms.util.ViewUtil; import java.util.ArrayList; import java.util.List; @@ -207,8 +208,8 @@ public final class AvatarImageView extends AppCompatImageView { this.chatColors = chatColors; recipientContactPhoto = photo; - Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL ? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider) - : photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider); + Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL ? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider, ViewUtil.getWidth(this)) + : photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, fallbackPhotoProvider, ViewUtil.getWidth(this)); if (fixedSizeTarget != null) { requestManager.clear(fixedSizeTarget); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java index be44e9dd12..ca682b543e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiProvider.java @@ -26,13 +26,15 @@ import org.thoughtcrime.securesms.util.FutureTaskListener; import org.thoughtcrime.securesms.util.ListenableFutureTask; import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; -class EmojiProvider { +public class EmojiProvider { private static final String TAG = Log.tag(EmojiProvider.class); private static final Paint PAINT = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG); - static @Nullable EmojiParser.CandidateList getCandidates(@Nullable CharSequence text) { + public static @Nullable EmojiParser.CandidateList getCandidates(@Nullable CharSequence text) { if (text == null) return null; return new EmojiParser(EmojiSource.getLatest().getEmojiTree()).findCandidates(text); } @@ -64,6 +66,32 @@ class EmojiProvider { return builder; } + public static @Nullable Spannable emojify(@NonNull Context context, + @Nullable EmojiParser.CandidateList matches, + @Nullable CharSequence text, + @NonNull Paint paint, + boolean synchronous) + { + if (matches == null || text == null) return null; + SpannableStringBuilder builder = new SpannableStringBuilder(text); + + for (EmojiParser.Candidate candidate : matches) { + Drawable drawable; + if (synchronous) { + drawable = getEmojiDrawableSync(context, candidate.getDrawInfo()); + } else { + drawable = getEmojiDrawable(context, candidate.getDrawInfo(), null); + } + + if (drawable != null) { + builder.setSpan(new EmojiSpan(context, drawable, paint), candidate.getStartIndex(), candidate.getEndIndex(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + } + + return builder; + } + static @Nullable Drawable getEmojiDrawable(@NonNull Context context, @Nullable CharSequence emoji) { EmojiDrawInfo drawInfo = EmojiSource.getLatest().getEmojiTree().getEmoji(emoji, 0, emoji.length()); return getEmojiDrawable(context, drawInfo, null); @@ -113,6 +141,43 @@ class EmojiProvider { return drawable; } + /** + * Gets an EmojiDrawable from the Page Cache synchronously + * + * @param context Context object used in reading and writing from disk + * @param drawInfo Information about the emoji being displayed + */ + private static @Nullable Drawable getEmojiDrawableSync(@NonNull Context context, @Nullable EmojiDrawInfo drawInfo) { + ThreadUtil.assertNotMainThread(); + if (drawInfo == null) { + return null; + } + + final int lowMemoryDecodeScale = DeviceProperties.isLowMemoryDevice(context) ? 2 : 1; + final EmojiSource source = EmojiSource.getLatest(); + final EmojiDrawable drawable = new EmojiDrawable(source, drawInfo, lowMemoryDecodeScale); + + EmojiPageCache.LoadResult loadResult = EmojiPageCache.INSTANCE.load(context, drawInfo.getPage(), lowMemoryDecodeScale); + Bitmap bitmap = null; + + if (loadResult instanceof EmojiPageCache.LoadResult.Immediate) { + Log.d(TAG, "Cached emoji page: " + drawInfo.getPage().getUri().toString()); + bitmap = ((EmojiPageCache.LoadResult.Immediate) loadResult).getBitmap(); + } else if (loadResult instanceof EmojiPageCache.LoadResult.Async) { + Log.d(TAG, "Loading emoji page: " + drawInfo.getPage().getUri().toString()); + try { + bitmap = ((EmojiPageCache.LoadResult.Async) loadResult).getTask().get(2, TimeUnit.SECONDS); + } catch (InterruptedException | ExecutionException | TimeoutException exception) { + Log.d(TAG, "Failed to load emoji bitmap resource", exception); + } + } else { + throw new IllegalStateException("Unexpected subclass " + loadResult.getClass()); + } + + drawable.setBitmap(bitmap); + return drawable; + } + static final class EmojiDrawable extends Drawable { private final float intrinsicWidth; private final float intrinsicHeight; @@ -160,7 +225,6 @@ class EmojiProvider { } public void setBitmap(Bitmap bitmap) { - ThreadUtil.assertMainThread(); if (bmp == null || !bmp.sameAs(bitmap)) { bmp = bitmap; invalidateSelf(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java index a0e34e476b..1be73d30d2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/EmojiSpan.java @@ -1,5 +1,6 @@ package org.thoughtcrime.securesms.components.emoji; +import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; @@ -25,6 +26,15 @@ public class EmojiSpan extends AnimatingImageSpan { getDrawable().setBounds(0, 0, size, size); } + public EmojiSpan(@NonNull Context context, @NonNull Drawable drawable, @NonNull Paint paint) { + super(drawable, null); + fontMetrics = paint.getFontMetricsInt(); + size = fontMetrics != null ? Math.abs(fontMetrics.descent) + Math.abs(fontMetrics.ascent) + : context.getResources().getDimensionPixelSize(R.dimen.conversation_item_body_text_size); + + getDrawable().setBounds(0, 0, size, size); + } + @Override public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, FontMetricsInt fm) { if (fm != null && this.fontMetrics != null) { @@ -48,6 +58,7 @@ public class EmojiSpan extends AnimatingImageSpan { int height = bottom - top; int centeringMargin = (height - size) / 2; int adjustedMargin = (int) (centeringMargin * SHIFT_FACTOR); + int adjustedBottom = bottom - adjustedMargin; super.draw(canvas, text, start, end, x, top, y, bottom - adjustedMargin, paint); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SimpleEmojiTextView.kt b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SimpleEmojiTextView.kt index e8813f1a7a..09593770eb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SimpleEmojiTextView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/emoji/SimpleEmojiTextView.kt @@ -33,6 +33,7 @@ open class SimpleEmojiTextView @JvmOverloads constructor( } else { EmojiProvider.emojify(newCandidates, newContent, this) } + bufferType = BufferType.SPANNABLE super.setText(newText, type) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java index 6331c5af2a..4dfb241c4b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java +++ b/app/src/main/java/org/thoughtcrime/securesms/contacts/avatars/GeneratedContactPhoto.java @@ -45,7 +45,7 @@ public class GeneratedContactPhoto implements FallbackContactPhoto { @Override public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) { - int targetSize = this.targetSize != -1 + int targetSize = this.targetSize > 0 ? this.targetSize : context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size); @@ -54,7 +54,7 @@ public class GeneratedContactPhoto implements FallbackContactPhoto { if (!TextUtils.isEmpty(character)) { Avatars.ForegroundColor foregroundColor = Avatars.getForegroundColor(color); Avatar.Text avatar = new Avatar.Text(character, new Avatars.ColorPair(color, foregroundColor), Avatar.DatabaseId.DoNotPersist.INSTANCE); - Drawable foreground = AvatarRenderer.createTextDrawable(context, avatar, inverted, targetSize); + Drawable foreground = AvatarRenderer.createTextDrawable(context, avatar, inverted, targetSize, false); Drawable background = Objects.requireNonNull(ContextCompat.getDrawable(context, R.drawable.circle_tintable)); background.setColorFilter(new SimpleColorFilter(inverted ? foregroundColor.getColorInt() : color.colorInt())); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 7c2d06829b..21dbe93946 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -490,7 +490,7 @@ public class ConversationListFragment extends MainFragment implements ActionMode private void initializeProfileIcon(@NonNull Recipient recipient) { ImageView icon = requireView().findViewById(R.id.toolbar_icon); - AvatarUtil.loadIconIntoImageView(recipient, icon); + AvatarUtil.loadIconIntoImageView(recipient, icon, getResources().getDimensionPixelSize(R.dimen.toolbar_avatar_size)); icon.setOnClickListener(v -> getNavigator().goToAppSettings()); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java index 7d346bcbc3..f1a4c423b9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/profiles/spoofing/ReviewBannerView.java @@ -106,7 +106,7 @@ public class ReviewBannerView extends LinearLayout { @NonNull @Override - public FallbackContactPhoto getPhotoForRecipientWithName(String name) { + public FallbackContactPhoto getPhotoForRecipientWithName(String name, int targetSize) { return new FixedSizeGeneratedContactPhoto(name, R.drawable.ic_profile_outline_20); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 741498dd5a..cb356cbf74 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -40,6 +40,7 @@ import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.phonenumbers.NumberUtil; import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter; import org.thoughtcrime.securesms.profiles.ProfileName; +import org.thoughtcrime.securesms.util.AvatarUtil; import org.thoughtcrime.securesms.util.FeatureFlags; import org.thoughtcrime.securesms.util.StringUtil; import org.thoughtcrime.securesms.util.Util; @@ -791,19 +792,23 @@ public class Recipient { } public @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted) { - return getFallbackContactPhotoDrawable(context, inverted, DEFAULT_FALLBACK_PHOTO_PROVIDER); + return getFallbackContactPhotoDrawable(context, inverted, DEFAULT_FALLBACK_PHOTO_PROVIDER, AvatarUtil.UNDEFINED_SIZE); } public @NonNull Drawable getSmallFallbackContactPhotoDrawable(Context context, boolean inverted) { return getSmallFallbackContactPhotoDrawable(context, inverted, DEFAULT_FALLBACK_PHOTO_PROVIDER); } - public @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider) { - return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asDrawable(context, avatarColor, inverted); + public @NonNull Drawable getFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider, int targetSize) { + return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER), targetSize).asDrawable(context, avatarColor, inverted); } public @NonNull Drawable getSmallFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider) { - return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER)).asSmallDrawable(context, avatarColor, inverted); + return getSmallFallbackContactPhotoDrawable(context, inverted, fallbackPhotoProvider, AvatarUtil.UNDEFINED_SIZE); + } + + public @NonNull Drawable getSmallFallbackContactPhotoDrawable(Context context, boolean inverted, @Nullable FallbackPhotoProvider fallbackPhotoProvider, int targetSize) { + return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER), targetSize).asSmallDrawable(context, avatarColor, inverted); } public @NonNull FallbackContactPhoto getFallbackContactPhoto() { @@ -811,13 +816,17 @@ public class Recipient { } public @NonNull FallbackContactPhoto getFallbackContactPhoto(@NonNull FallbackPhotoProvider fallbackPhotoProvider) { + return getFallbackContactPhoto(fallbackPhotoProvider, AvatarUtil.UNDEFINED_SIZE); + } + + public @NonNull FallbackContactPhoto getFallbackContactPhoto(@NonNull FallbackPhotoProvider fallbackPhotoProvider, int targetSize) { if (isSelf) return fallbackPhotoProvider.getPhotoForLocalNumber(); else if (isResolving()) return fallbackPhotoProvider.getPhotoForResolvingRecipient(); else if (isGroupInternal()) return fallbackPhotoProvider.getPhotoForGroup(); else if (isGroup()) return fallbackPhotoProvider.getPhotoForGroup(); - else if (!TextUtils.isEmpty(groupName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(groupName); - else if (!TextUtils.isEmpty(systemContactName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(systemContactName); - else if (!signalProfileName.isEmpty()) return fallbackPhotoProvider.getPhotoForRecipientWithName(signalProfileName.toString()); + else if (!TextUtils.isEmpty(groupName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(groupName, targetSize); + else if (!TextUtils.isEmpty(systemContactName)) return fallbackPhotoProvider.getPhotoForRecipientWithName(systemContactName, targetSize); + else if (!signalProfileName.isEmpty()) return fallbackPhotoProvider.getPhotoForRecipientWithName(signalProfileName.toString(), targetSize); else return fallbackPhotoProvider.getPhotoForRecipientWithoutName(); } @@ -1224,8 +1233,8 @@ public class Recipient { return new ResourceContactPhoto(R.drawable.ic_group_outline_34, R.drawable.ic_group_outline_20, R.drawable.ic_group_outline_48); } - public @NonNull FallbackContactPhoto getPhotoForRecipientWithName(String name) { - return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40); + public @NonNull FallbackContactPhoto getPhotoForRecipientWithName(String name, int targetSize) { + return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40, targetSize); } public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java index ee96b3c727..6331ae600a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/AvatarUtil.java @@ -30,6 +30,8 @@ import java.util.concurrent.ExecutionException; public final class AvatarUtil { + public static final int UNDEFINED_SIZE = -1; + private AvatarUtil() { } @@ -71,9 +73,13 @@ public final class AvatarUtil { } public static void loadIconIntoImageView(@NonNull Recipient recipient, @NonNull ImageView target) { + loadIconIntoImageView(recipient, target, -1); + } + + public static void loadIconIntoImageView(@NonNull Recipient recipient, @NonNull ImageView target, int requestedSize) { Context context = target.getContext(); - requestCircle(GlideApp.with(context).asDrawable(), context, recipient).into(target); + requestCircle(GlideApp.with(context).asDrawable(), context, recipient, requestedSize).into(target); } public static Bitmap loadIconBitmapSquareNoCache(@NonNull Context context, @@ -92,7 +98,7 @@ public final class AvatarUtil { @WorkerThread public static IconCompat getIconForNotification(@NonNull Context context, @NonNull Recipient recipient) { try { - return IconCompat.createWithBitmap(requestCircle(GlideApp.with(context).asBitmap(), context, recipient).submit().get()); + return IconCompat.createWithBitmap(requestCircle(GlideApp.with(context).asBitmap(), context, recipient, UNDEFINED_SIZE).submit().get()); } catch (ExecutionException | InterruptedException e) { return null; } @@ -114,25 +120,25 @@ public final class AvatarUtil { @WorkerThread public static Bitmap getBitmapForNotification(@NonNull Context context, @NonNull Recipient recipient) { try { - return requestCircle(GlideApp.with(context).asBitmap(), context, recipient).submit().get(); + return requestCircle(GlideApp.with(context).asBitmap(), context, recipient, UNDEFINED_SIZE).submit().get(); } catch (ExecutionException | InterruptedException e) { return null; } } - private static GlideRequest requestCircle(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient) { - return request(glideRequest, context, recipient).circleCrop(); + private static GlideRequest requestCircle(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient, int targetSize) { + return request(glideRequest, context, recipient, targetSize).circleCrop(); } private static GlideRequest requestSquare(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient) { - return request(glideRequest, context, recipient).centerCrop(); + return request(glideRequest, context, recipient, UNDEFINED_SIZE).centerCrop(); } - private static GlideRequest request(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient) { - return request(glideRequest, context, recipient, true); + private static GlideRequest request(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient, int targetSize) { + return request(glideRequest, context, recipient, true, targetSize); } - private static GlideRequest request(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient, boolean loadSelf) { + private static GlideRequest request(@NonNull GlideRequest glideRequest, @NonNull Context context, @NonNull Recipient recipient, boolean loadSelf, int targetSize) { final ContactPhoto photo; if (Recipient.self().equals(recipient) && loadSelf) { photo = new ProfileContactPhoto(recipient, recipient.getProfileAvatar()); @@ -141,7 +147,7 @@ public final class AvatarUtil { } final GlideRequest request = glideRequest.load(photo) - .error(getFallback(context, recipient)) + .error(getFallback(context, recipient, targetSize)) .diskCacheStrategy(DiskCacheStrategy.ALL); if (recipient.shouldBlurAvatar()) { @@ -151,9 +157,9 @@ public final class AvatarUtil { } } - private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient) { + private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient, int targetSize) { String name = Optional.fromNullable(recipient.getDisplayName(context)).or(""); - return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40).asDrawable(context, recipient.getAvatarColor()); + return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40, targetSize).asDrawable(context, recipient.getAvatarColor()); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java index 4d1e113197..3dbea7f3d2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ViewUtil.java @@ -280,6 +280,10 @@ public final class ViewUtil { view.requestLayout(); } + public static int getWidth(@NonNull View view) { + return view.getLayoutParams().width; + } + public static void setPaddingTop(@NonNull View view, int padding) { view.setPadding(view.getPaddingLeft(), padding, view.getPaddingRight(), view.getPaddingBottom()); } diff --git a/app/src/main/res/layout/conversation_list_fragment.xml b/app/src/main/res/layout/conversation_list_fragment.xml index fd56992ae7..dda78ebe66 100644 --- a/app/src/main/res/layout/conversation_list_fragment.xml +++ b/app/src/main/res/layout/conversation_list_fragment.xml @@ -35,8 +35,8 @@ 100dp + 28dp 26dp + 48dp 16dp