mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-22 01:40:07 +01:00
Rewrite fallbackphoto system.
This commit is contained in:
committed by
Greyson Parrelli
parent
d698f74d0b
commit
11557e4815
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user