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

@@ -16,7 +16,13 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import com.google.android.material.chip.Chip;
import com.google.android.material.shape.MaterialShapeDrawable;
import com.google.android.material.shape.RelativeCornerSize;
import com.google.android.material.shape.RoundedCornerTreatment;
import com.google.android.material.shape.ShapeAppearanceModel;
import com.google.android.material.shape.Shapeable;
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable;
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
import org.thoughtcrime.securesms.recipients.Recipient;
@@ -48,10 +54,16 @@ public final class ContactChip extends Chip {
if (recipient != null) {
requestManager.clear(this);
Drawable fallbackContactPhotoDrawable = new HalfScaleDrawable(recipient.getFallbackContactPhotoDrawable(getContext(), false));
ContactPhoto contactPhoto = recipient.getContactPhoto();
FallbackAvatarDrawable fallbackContactPhotoDrawable = new FallbackAvatarDrawable(getContext(), recipient.getFallbackAvatar());
ContactPhoto contactPhoto = recipient.getContactPhoto();
if (contactPhoto == null) {
fallbackContactPhotoDrawable.setShapeAppearanceModel(
ShapeAppearanceModel.builder()
.setAllCorners(new RoundedCornerTreatment())
.setAllCornerSizes(new RelativeCornerSize(0.5f)).build()
);
setChipIcon(fallbackContactPhotoDrawable);
if (onAvatarSet != null) {
onAvatarSet.run();

View File

@@ -1,17 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
public interface FallbackContactPhoto {
Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color);
Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted);
Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted);
Drawable asCallCard(@NonNull Context context);
}

View File

@@ -1,64 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Px;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.drawable.DrawableCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.Avatars;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import java.util.Objects;
/**
* Fallback resource based contact photo with a 20dp icon
*/
public final class FallbackPhoto implements FallbackContactPhoto {
@DrawableRes private final int drawableResource;
@Px private final int foregroundInset;
public FallbackPhoto(@DrawableRes int drawableResource, @Px int foregroundInset) {
this.drawableResource = drawableResource;
this.foregroundInset = foregroundInset;
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color) {
return buildDrawable(context, color);
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context, color);
}
@Override
public Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context, color);
}
@Override
public Drawable asCallCard(@NonNull Context context) {
throw new UnsupportedOperationException();
}
private @NonNull Drawable buildDrawable(@NonNull Context context, @NonNull AvatarColor color) {
Drawable background = DrawableCompat.wrap(Objects.requireNonNull(AppCompatResources.getDrawable(context, R.drawable.circle_tintable))).mutate();
Drawable foreground = Objects.requireNonNull(AppCompatResources.getDrawable(context, drawableResource));
LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground});
DrawableCompat.setTint(background, color.colorInt());
DrawableCompat.setTint(foreground, Avatars.getForegroundColor(color).getColorInt());
drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset);
return drawable;
}
}

View File

@@ -1,63 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.drawable.DrawableCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.Avatars;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Objects;
/**
* Fallback resource based contact photo with a 20dp icon
*/
public final class FallbackPhoto20dp implements FallbackContactPhoto {
@DrawableRes private final int drawable20dp;
public FallbackPhoto20dp(@DrawableRes int drawable20dp) {
this.drawable20dp = drawable20dp;
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color) {
return buildDrawable(context, color);
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context, color);
}
@Override
public Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context, color);
}
@Override
public Drawable asCallCard(@NonNull Context context) {
throw new UnsupportedOperationException();
}
private @NonNull Drawable buildDrawable(@NonNull Context context, @NonNull AvatarColor color) {
Drawable background = DrawableCompat.wrap(Objects.requireNonNull(AppCompatResources.getDrawable(context, R.drawable.circle_tintable))).mutate();
Drawable foreground = Objects.requireNonNull(AppCompatResources.getDrawable(context, drawable20dp));
LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground});
int foregroundInset = ViewUtil.dpToPx(2);
DrawableCompat.setTint(background, color.colorInt());
DrawableCompat.setTint(foreground, Avatars.getForegroundColor(color).getColorInt());
drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset);
return drawable;
}
}

View File

@@ -1,71 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.graphics.drawable.DrawableCompat;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.Avatars;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.util.ViewUtil;
import java.util.Objects;
public final class FallbackPhoto80dp implements FallbackContactPhoto {
@DrawableRes private final int drawable80dp;
private final AvatarColor color;
public FallbackPhoto80dp(@DrawableRes int drawable80dp, @NonNull AvatarColor color) {
this.drawable80dp = drawable80dp;
this.color = color;
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color) {
return buildDrawable(context);
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context);
}
@Override
public Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
throw new UnsupportedOperationException();
}
@Override
public Drawable asCallCard(@NonNull Context context) {
Drawable background = new ColorDrawable(color.colorInt());
Drawable foreground = Objects.requireNonNull(AppCompatResources.getDrawable(context, drawable80dp));
LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground});
int foregroundInset = ViewUtil.dpToPx(24);
DrawableCompat.setTint(foreground, Avatars.getForegroundColor(color).getColorInt());
drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset);
return drawable;
}
private @NonNull Drawable buildDrawable(@NonNull Context context) {
Drawable background = DrawableCompat.wrap(Objects.requireNonNull(AppCompatResources.getDrawable(context, R.drawable.circle_tintable))).mutate();
Drawable foreground = Objects.requireNonNull(AppCompatResources.getDrawable(context, drawable80dp));
LayerDrawable drawable = new LayerDrawable(new Drawable[]{background, foreground});
int foregroundInset = ViewUtil.dpToPx(24);
DrawableCompat.setTint(background, color.colorInt());
DrawableCompat.setTint(foreground, Avatars.getForegroundColor(color).getColorInt());
drawable.setLayerInset(1, foregroundInset, foregroundInset, foregroundInset, foregroundInset);
return drawable;
}
}

View File

@@ -1,85 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import com.airbnb.lottie.SimpleColorFilter;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.avatar.Avatar;
import org.thoughtcrime.securesms.avatar.AvatarRenderer;
import org.thoughtcrime.securesms.avatar.Avatars;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.util.NameUtil;
import java.util.Objects;
public class GeneratedContactPhoto implements FallbackContactPhoto {
private final String name;
private final int fallbackResId;
private final int targetSize;
public GeneratedContactPhoto(@NonNull String name, @DrawableRes int fallbackResId) {
this(name, fallbackResId, -1);
}
public GeneratedContactPhoto(@NonNull String name, @DrawableRes int fallbackResId, int targetSize) {
this.name = name;
this.fallbackResId = fallbackResId;
this.targetSize = targetSize;
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color) {
return asDrawable(context, color,false);
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
int targetSize = this.targetSize > 0
? this.targetSize
: context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
String character = NameUtil.getAbbreviation(name);
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, false);
Drawable background = Objects.requireNonNull(ContextCompat.getDrawable(context, R.drawable.circle_tintable));
background.setColorFilter(new SimpleColorFilter(inverted ? foregroundColor.getColorInt() : color.colorInt()));
return new LayerDrawable(new Drawable[] { background, foreground });
}
return newFallbackDrawable(context, color, inverted);
}
@Override
public Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return asDrawable(context, color, inverted);
}
protected @DrawableRes int getFallbackResId() {
return fallbackResId;
}
protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return new ResourceContactPhoto(fallbackResId).asDrawable(context, color, inverted);
}
@Override
public Drawable asCallCard(@NonNull Context context) {
return AppCompatResources.getDrawable(context, R.drawable.ic_person_large);
}
}

View File

@@ -1,103 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.content.ContextCompat;
import com.makeramen.roundedimageview.RoundedDrawable;
import org.jetbrains.annotations.NotNull;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
import org.thoughtcrime.securesms.conversation.colors.AvatarColorPair;
import java.util.Objects;
public class ResourceContactPhoto implements FallbackContactPhoto {
private final int resourceId;
private final int smallResourceId;
private final int callCardResourceId;
private ImageView.ScaleType scaleType = ImageView.ScaleType.CENTER;
public ResourceContactPhoto(@DrawableRes int resourceId) {
this(resourceId, resourceId, resourceId);
}
public ResourceContactPhoto(@DrawableRes int resourceId, @DrawableRes int smallResourceId) {
this(resourceId, smallResourceId, resourceId);
}
public ResourceContactPhoto(@DrawableRes int resourceId, @DrawableRes int smallResourceId, @DrawableRes int callCardResourceId) {
this.resourceId = resourceId;
this.callCardResourceId = callCardResourceId;
this.smallResourceId = smallResourceId;
}
public void setScaleType(@NonNull ImageView.ScaleType scaleType) {
this.scaleType = scaleType;
}
@Override
public @NonNull Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color) {
return asDrawable(context, color, false);
}
@Override
public @NonNull Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context, resourceId, color, inverted);
}
@Override
public @NonNull Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return buildDrawable(context, smallResourceId, color, inverted);
}
private @NonNull Drawable buildDrawable(@NonNull Context context, int resourceId, @NonNull AvatarColor color, boolean inverted) {
AvatarColorPair avatarColorPair = AvatarColorPair.create(context, color);
final int backgroundColor = avatarColorPair.getBackgroundColor();
final int foregroundColor = avatarColorPair.getForegroundColor();
Drawable background = Objects.requireNonNull(ContextCompat.getDrawable(context, R.drawable.circle_tintable));
RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(AppCompatResources.getDrawable(context, resourceId));
//noinspection ConstantConditions
foreground.setScaleType(scaleType);
background.setColorFilter(inverted ? foregroundColor : backgroundColor, PorterDuff.Mode.SRC_IN);
foreground.setColorFilter(inverted ? backgroundColor : foregroundColor, PorterDuff.Mode.SRC_ATOP);
return new ExpandingLayerDrawable(new Drawable[] {background, foreground});
}
@Override
public @Nullable Drawable asCallCard(@NotNull @NonNull Context context) {
return AppCompatResources.getDrawable(context, callCardResourceId);
}
private static class ExpandingLayerDrawable extends LayerDrawable {
public ExpandingLayerDrawable(@NonNull Drawable[] layers) {
super(layers);
}
@Override
public int getIntrinsicWidth() {
return -1;
}
@Override
public int getIntrinsicHeight() {
return -1;
}
}
}

View File

@@ -1,38 +0,0 @@
package org.thoughtcrime.securesms.contacts.avatars;
import android.content.Context;
import android.graphics.drawable.Drawable;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.makeramen.roundedimageview.RoundedDrawable;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
public class TransparentContactPhoto implements FallbackContactPhoto {
public TransparentContactPhoto() {}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color) {
return asDrawable(context, color, false);
}
@Override
public Drawable asDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return RoundedDrawable.fromDrawable(context.getResources().getDrawable(android.R.color.transparent));
}
@Override
public Drawable asSmallDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
return asDrawable(context, color, inverted);
}
@Override
public Drawable asCallCard(@NonNull Context context) {
return ContextCompat.getDrawable(context, R.drawable.symbol_person_display_40);
}
}

View File

@@ -13,8 +13,8 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.disposables.Disposable
import org.signal.core.util.BreakIteratorCompat
import org.signal.core.util.dp
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar
import org.thoughtcrime.securesms.avatar.view.AvatarView
import org.thoughtcrime.securesms.badges.BadgeImageView
import org.thoughtcrime.securesms.components.AvatarImageView
@@ -24,8 +24,6 @@ import org.thoughtcrime.securesms.components.emoji.EmojiUtil
import org.thoughtcrime.securesms.components.menu.ActionItem
import org.thoughtcrime.securesms.components.menu.SignalContextMenu
import org.thoughtcrime.securesms.contacts.LetterHeaderDecoration
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
import org.thoughtcrime.securesms.database.model.DistributionListPrivacyMode
import org.thoughtcrime.securesms.database.model.StoryViewState
import org.thoughtcrime.securesms.keyvalue.SignalStore
@@ -243,10 +241,10 @@ open class ContactSearchAdapter(
fun bindAvatar(model: StoryModel) {
if (model.story.recipient.isMyStory) {
avatar.setFallbackPhotoProvider(MyStoryFallbackPhotoProvider(Recipient.self().getDisplayName(context), 40.dp))
avatar.setFallbackAvatarProvider(MyStoryFallbackAvatarProvider)
avatar.displayProfileAvatar(Recipient.self())
} else {
avatar.setFallbackPhotoProvider(Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER)
avatar.setFallbackAvatarProvider(null)
avatar.displayChatAvatar(getRecipient(model))
}
groupStoryIndicator.visible = showStoryRing && model.story.recipient.isGroup
@@ -308,9 +306,14 @@ open class ContactSearchAdapter(
}
}
private class MyStoryFallbackPhotoProvider(private val name: String, private val targetSize: Int) : Recipient.FallbackPhotoProvider() {
override val photoForLocalNumber: FallbackContactPhoto
get() = GeneratedContactPhoto(name, R.drawable.symbol_person_40, targetSize)
private object MyStoryFallbackAvatarProvider : AvatarImageView.FallbackAvatarProvider {
override fun getFallbackAvatar(recipient: Recipient): FallbackAvatar {
if (recipient.isSelf) {
return FallbackAvatar.Resource.Person(recipient.avatarColor)
}
return super.getFallbackAvatar(recipient)
}
}
override fun onAttachedToWindow() {