Rewrite fallbackphoto system.

This commit is contained in:
Alex Hart
2024-06-12 15:59:35 -03:00
committed by Greyson Parrelli
parent d698f74d0b
commit 11557e4815
42 changed files with 676 additions and 805 deletions

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.util
import org.signal.core.util.dp
import kotlin.math.ceil
import kotlin.math.floor
/**
* Describes the absolute measurements for adaptive icon bitmaps.
*
* Because adaptive icons are meant to be 108dp with a 72dp image,
* this can result in subpixel measurement on some devices. This class
* is responsible for on-the-fly calculation of metrics that'll look good
* and not cause off-by-a-pixel errors in Adaptive bitmaps.
*/
object AdaptiveBitmapMetrics {
private val ADAPTIVE_ICON_OUTER_SIZE: Float = 108f.dp
private val ADAPTIVE_ICON_INNER_SIZE: Float = 72f.dp
@get:JvmStatic
val outerWidth = ceil(ADAPTIVE_ICON_OUTER_SIZE).toInt()
@get:JvmStatic
val innerWidth = ceil(ADAPTIVE_ICON_INNER_SIZE).let { ceiling ->
if (floor(ADAPTIVE_ICON_OUTER_SIZE) < outerWidth) {
ceiling + 2
} else {
ceiling
}.toInt()
}
@get:JvmStatic
val padding = (outerWidth - innerWidth) / 2f
}

View File

@@ -28,14 +28,14 @@ import com.bumptech.glide.request.transition.Transition;
import org.signal.core.util.ThreadUtil;
import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar;
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
import org.thoughtcrime.securesms.providers.AvatarProvider;
import org.thoughtcrime.securesms.recipients.Recipient;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
@@ -115,7 +115,7 @@ public final class AvatarUtil {
if (Build.VERSION.SDK_INT > 29) {
return IconCompat.createWithContentUri(AvatarProvider.getContentUri(recipient.getId()));
} else {
return IconCompat.createWithBitmap(getBitmapForNotification(context, recipient, DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE));
return IconCompat.createWithBitmap(getBitmapForNotification(context, recipient, AdaptiveBitmapMetrics.getInnerWidth()));
}
}
@@ -162,7 +162,7 @@ public final class AvatarUtil {
photo = recipient.getContactPhoto();
}
final int size = targetSize == -1 ? DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE : targetSize;
final int size = targetSize == -1 ? AdaptiveBitmapMetrics.getInnerWidth() : targetSize;
final RequestBuilder<T> request = requestBuilder.load(photo)
.error(getFallback(context, recipient, size))
.diskCacheStrategy(DiskCacheStrategy.ALL)
@@ -183,9 +183,12 @@ public final class AvatarUtil {
}
private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient, int targetSize) {
String name = Optional.of(recipient.getDisplayName(context)).orElse("");
FallbackAvatar fallbackAvatar = FallbackAvatar.forTextOrDefault(recipient.getDisplayName(context), recipient.getAvatarColor());
return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40, targetSize).asDrawable(context, recipient.getAvatarColor());
Drawable avatar = new FallbackAvatarDrawable(context, fallbackAvatar).circleCrop();
avatar.setBounds(0, 0, targetSize, targetSize);
return avatar;
}
/**
@@ -199,7 +202,7 @@ public final class AvatarUtil {
private final int size;
private AvatarTarget(int size) {
this.size = size == UNDEFINED_SIZE ? DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE : size;
this.size = size == UNDEFINED_SIZE ? AdaptiveBitmapMetrics.getInnerWidth() : size;
}
public @Nullable Bitmap await() throws InterruptedException {

View File

@@ -2,9 +2,7 @@ package org.thoughtcrime.securesms.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
@@ -18,12 +16,8 @@ import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import org.signal.libsignal.protocol.util.ByteUtil;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp;
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable;
import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails;
import org.thoughtcrime.securesms.dependencies.AppDependencies;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -162,50 +156,19 @@ public final class ConversationShortcutPhoto implements Key {
private @NonNull Bitmap getShortcutInfoBitmap(@NonNull Context context) throws ExecutionException, InterruptedException {
return DrawableUtil.wrapBitmapForShortcutInfo(AvatarUtil.loadIconBitmapSquareNoCache(context,
photo.recipient,
DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE,
DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE));
AdaptiveBitmapMetrics.getInnerWidth(),
AdaptiveBitmapMetrics.getInnerWidth()));
}
private @NonNull Bitmap getFallbackForShortcut(@NonNull Context context) {
Recipient recipient = photo.recipient;
@DrawableRes final int photoSource;
if (recipient.isSelf()) {
photoSource = R.drawable.ic_note_80;
} else if (recipient.isGroup()) {
photoSource = R.drawable.ic_group_80;
} else {
photoSource = R.drawable.ic_profile_80;
}
FallbackContactPhoto photo = recipient.isSelf() || recipient.isGroup() ? new FallbackPhoto80dp(photoSource, recipient.getAvatarColor())
: new ShortcutGeneratedContactPhoto(recipient.getDisplayName(context), photoSource, ViewUtil.dpToPx(80), recipient.getAvatarColor());
Bitmap toWrap = DrawableUtil.toBitmap(photo.asCallCard(context), ViewUtil.dpToPx(80), ViewUtil.dpToPx(80));
Bitmap wrapped = DrawableUtil.wrapBitmapForShortcutInfo(toWrap);
Bitmap toWrap = DrawableUtil.toBitmap(new FallbackAvatarDrawable(context, recipient.getFallbackAvatar()), ViewUtil.dpToPx(80), ViewUtil.dpToPx(80));
Bitmap wrapped = DrawableUtil.wrapBitmapForShortcutInfo(toWrap);
toWrap.recycle();
return wrapped;
}
}
private static final class ShortcutGeneratedContactPhoto extends GeneratedContactPhoto {
private final AvatarColor color;
public ShortcutGeneratedContactPhoto(@NonNull String name, int fallbackResId, int targetSize, @NonNull AvatarColor color) {
super(name, fallbackResId, targetSize);
this.color = color;
}
@Override
protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return new FallbackPhoto80dp(getFallbackResId(), color).asDrawable(context, AvatarColor.UNKNOWN);
}
@Override public Drawable asCallCard(@NonNull Context context) {
return new FallbackPhoto80dp(getFallbackResId(), color).asCallCard(context);
}
}
}

View File

@@ -10,10 +10,6 @@ import androidx.core.graphics.drawable.DrawableCompat;
public final class DrawableUtil {
private static final int SHORTCUT_INFO_BITMAP_SIZE = ViewUtil.dpToPx(108);
public static final int SHORTCUT_INFO_WRAPPED_SIZE = ViewUtil.dpToPx(72);
private static final int SHORTCUT_INFO_PADDING = (SHORTCUT_INFO_BITMAP_SIZE - SHORTCUT_INFO_WRAPPED_SIZE) / 2;
private DrawableUtil() {}
public static @NonNull Bitmap toBitmap(@NonNull Drawable drawable, int width, int height) {
@@ -27,11 +23,11 @@ public final class DrawableUtil {
}
public static @NonNull Bitmap wrapBitmapForShortcutInfo(@NonNull Bitmap toWrap) {
Bitmap bitmap = Bitmap.createBitmap(SHORTCUT_INFO_BITMAP_SIZE, SHORTCUT_INFO_BITMAP_SIZE, Bitmap.Config.ARGB_8888);
Bitmap scaled = Bitmap.createScaledBitmap(toWrap, SHORTCUT_INFO_WRAPPED_SIZE, SHORTCUT_INFO_WRAPPED_SIZE, true);
Bitmap bitmap = Bitmap.createBitmap(AdaptiveBitmapMetrics.getOuterWidth(), AdaptiveBitmapMetrics.getOuterWidth(), Bitmap.Config.ARGB_8888);
Bitmap scaled = Bitmap.createScaledBitmap(toWrap, AdaptiveBitmapMetrics.getInnerWidth(), AdaptiveBitmapMetrics.getInnerWidth(), true);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(scaled, SHORTCUT_INFO_PADDING, SHORTCUT_INFO_PADDING, null);
canvas.drawBitmap(scaled, AdaptiveBitmapMetrics.getPadding(), AdaptiveBitmapMetrics.getPadding(), null);
return bitmap;
}