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
@@ -28,11 +28,11 @@ import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar;
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.FullscreenHelper;
|
||||
@@ -91,16 +91,18 @@ public final class AvatarPreviewActivity extends PassphraseRequiredActivity {
|
||||
Recipient.live(recipientId).observe(this, recipient -> {
|
||||
ContactPhoto contactPhoto = recipient.isSelf() ? new ProfileContactPhoto(recipient)
|
||||
: recipient.getContactPhoto();
|
||||
FallbackContactPhoto fallbackPhoto = recipient.isSelf() ? new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_person_large)
|
||||
: recipient.getFallbackContactPhoto();
|
||||
FallbackAvatar fallbackAvatar = recipient.isSelf() ? new FallbackAvatar.Resource.Person(recipient.getAvatarColor())
|
||||
: recipient.getFallbackAvatar();
|
||||
|
||||
Drawable fallbackDrawable = new FallbackAvatarDrawable(context, fallbackAvatar);
|
||||
|
||||
Resources resources = this.getResources();
|
||||
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(contactPhoto)
|
||||
.fallback(fallbackPhoto.asCallCard(this))
|
||||
.error(fallbackPhoto.asCallCard(this))
|
||||
.fallback(fallbackDrawable)
|
||||
.error(fallbackDrawable)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.addListener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.avatar.fallback
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.Px
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.util.DimensionUnit
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.util.NameUtil
|
||||
|
||||
/**
|
||||
* Specifies what kind of avatar should be generated for a given recipient.
|
||||
*/
|
||||
sealed interface FallbackAvatar {
|
||||
|
||||
val color: AvatarColor
|
||||
|
||||
/**
|
||||
* Transparent avatar
|
||||
*/
|
||||
data object Transparent : FallbackAvatar {
|
||||
override val color: AvatarColor = AvatarColor.UNKNOWN
|
||||
}
|
||||
|
||||
/**
|
||||
* Generated avatars utilize the initials of the given recipient
|
||||
*/
|
||||
data class Text(val content: String, override val color: AvatarColor) : FallbackAvatar {
|
||||
init {
|
||||
check(content.isNotEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback avatars that are backed by resources.
|
||||
*/
|
||||
sealed interface Resource : FallbackAvatar {
|
||||
|
||||
@DrawableRes
|
||||
fun getIconBySize(size: Size): Int
|
||||
|
||||
/**
|
||||
* Local user
|
||||
*/
|
||||
data class Local(override val color: AvatarColor) : Resource {
|
||||
override fun getIconBySize(size: Size): Int {
|
||||
return when (size) {
|
||||
Size.SMALL -> R.drawable.symbol_note_compact_16
|
||||
Size.MEDIUM -> R.drawable.symbol_note_24
|
||||
Size.LARGE -> R.drawable.symbol_note_display_bold_40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Individual user without a display name.
|
||||
*/
|
||||
data class Person(override val color: AvatarColor) : Resource {
|
||||
override fun getIconBySize(size: Size): Int {
|
||||
return when (size) {
|
||||
Size.SMALL -> R.drawable.symbol_person_compact_16
|
||||
Size.MEDIUM -> R.drawable.symbol_person_24
|
||||
Size.LARGE -> R.drawable.symbol_person_display_bold_40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A group
|
||||
*/
|
||||
data class Group(override val color: AvatarColor) : Resource {
|
||||
override fun getIconBySize(size: Size): Int {
|
||||
return when (size) {
|
||||
Size.SMALL -> R.drawable.symbol_group_compact_16
|
||||
Size.MEDIUM -> R.drawable.symbol_group_24
|
||||
Size.LARGE -> R.drawable.symbol_group_display_bold_40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Story distribution lists
|
||||
*/
|
||||
data class DistributionList(override val color: AvatarColor) : Resource {
|
||||
override fun getIconBySize(size: Size): Int {
|
||||
return when (size) {
|
||||
Size.SMALL -> R.drawable.symbol_stories_compact_16
|
||||
Size.MEDIUM -> R.drawable.symbol_stories_24
|
||||
Size.LARGE -> R.drawable.symbol_stories_display_bold_40
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call Links
|
||||
*/
|
||||
data class CallLink(override val color: AvatarColor) : Resource {
|
||||
override fun getIconBySize(size: Size): Int {
|
||||
return when (size) {
|
||||
Size.SMALL -> R.drawable.symbol_video_compact_16
|
||||
Size.MEDIUM -> R.drawable.symbol_video_24
|
||||
Size.LARGE -> R.drawable.symbol_video_display_bold_40
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class Size {
|
||||
/**
|
||||
* Smaller than 32dp
|
||||
*/
|
||||
SMALL,
|
||||
|
||||
/**
|
||||
* 32dp and larger
|
||||
*/
|
||||
MEDIUM,
|
||||
|
||||
/**
|
||||
* 80dp and larger
|
||||
*/
|
||||
LARGE
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ICON_TO_BACKGROUND_SCALE = 0.625
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun forTextOrDefault(text: String, avatarColor: AvatarColor, default: FallbackAvatar = Resource.Person(avatarColor)): FallbackAvatar {
|
||||
val abbreviation = NameUtil.getAbbreviation(text)
|
||||
return if (abbreviation != null) {
|
||||
Text(abbreviation, avatarColor)
|
||||
} else {
|
||||
default
|
||||
}
|
||||
}
|
||||
|
||||
fun getSizeByPx(@Px px: Int): Size {
|
||||
return getSizeByDp(DimensionUnit.PIXELS.toDp(px.toFloat()).dp)
|
||||
}
|
||||
|
||||
fun getSizeByDp(dp: Dp): Size {
|
||||
val rawDp = dp.value
|
||||
return when {
|
||||
rawDp >= 80.0 -> Size.LARGE
|
||||
rawDp < 32.0 -> Size.SMALL
|
||||
else -> Size.MEDIUM
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.avatar.fallback
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.airbnb.lottie.SimpleColorFilter
|
||||
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 org.thoughtcrime.securesms.avatar.Avatar
|
||||
import org.thoughtcrime.securesms.avatar.Avatars
|
||||
import org.thoughtcrime.securesms.avatar.TextAvatarDrawable
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColorPair
|
||||
|
||||
class FallbackAvatarDrawable(
|
||||
private val context: Context,
|
||||
private val fallbackAvatar: FallbackAvatar
|
||||
) : MaterialShapeDrawable() {
|
||||
|
||||
private val avatarColorPair: AvatarColorPair = AvatarColorPair.create(context, fallbackAvatar.color)
|
||||
private var avatarSize: FallbackAvatar.Size = FallbackAvatar.Size.SMALL
|
||||
private var icon: Drawable? = null
|
||||
|
||||
init {
|
||||
fillColor = ColorStateList.valueOf(avatarColorPair.backgroundColor)
|
||||
}
|
||||
|
||||
fun circleCrop(): FallbackAvatarDrawable {
|
||||
shapeAppearanceModel = ShapeAppearanceModel.builder()
|
||||
.setAllCorners(RoundedCornerTreatment())
|
||||
.setAllCornerSizes(RelativeCornerSize(0.5f))
|
||||
.build()
|
||||
return this
|
||||
}
|
||||
|
||||
override fun onBoundsChange(bounds: Rect) {
|
||||
super.onBoundsChange(bounds)
|
||||
|
||||
avatarSize = FallbackAvatar.getSizeByPx(bounds.width())
|
||||
icon = when (fallbackAvatar) {
|
||||
is FallbackAvatar.Resource -> {
|
||||
val resourceIcon = ContextCompat.getDrawable(context, fallbackAvatar.getIconBySize(avatarSize))!!
|
||||
|
||||
val iconBounds = Rect(bounds)
|
||||
iconBounds.inset(
|
||||
((bounds.width() - (bounds.width() * FallbackAvatar.ICON_TO_BACKGROUND_SCALE)) / 2f).toInt(),
|
||||
((bounds.height() - (bounds.height() * FallbackAvatar.ICON_TO_BACKGROUND_SCALE)) / 2f).toInt()
|
||||
)
|
||||
|
||||
resourceIcon.bounds = iconBounds
|
||||
resourceIcon
|
||||
}
|
||||
|
||||
is FallbackAvatar.Text -> TextAvatarDrawable(
|
||||
context = context,
|
||||
avatar = Avatar.Text(
|
||||
fallbackAvatar.content,
|
||||
Avatars.ColorPair(avatarColorPair.backgroundColor, avatarColorPair.foregroundColor, ""),
|
||||
Avatar.DatabaseId.DoNotPersist
|
||||
),
|
||||
size = bounds.width()
|
||||
)
|
||||
|
||||
FallbackAvatar.Transparent -> null
|
||||
}
|
||||
|
||||
icon?.alpha = alpha
|
||||
icon?.colorFilter = SimpleColorFilter(avatarColorPair.foregroundColor)
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
if (icon == null) return
|
||||
|
||||
super.draw(canvas)
|
||||
icon?.draw(canvas)
|
||||
}
|
||||
|
||||
override fun setAlpha(alpha: Int) {
|
||||
super.setAlpha(alpha)
|
||||
icon?.alpha = alpha
|
||||
invalidateSelf()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.avatar.fallback
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.TextUnitType
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.signal.core.ui.Previews
|
||||
import org.signal.core.ui.SignalPreview
|
||||
import org.signal.core.util.DimensionUnit
|
||||
import org.thoughtcrime.securesms.avatar.AvatarRenderer
|
||||
import org.thoughtcrime.securesms.avatar.Avatars
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColorPair
|
||||
|
||||
@Composable
|
||||
fun FallbackAvatarImage(
|
||||
fallbackAvatar: FallbackAvatar,
|
||||
modifier: Modifier = Modifier,
|
||||
shape: Shape = CircleShape
|
||||
) {
|
||||
if (fallbackAvatar is FallbackAvatar.Transparent) {
|
||||
Box(modifier = modifier)
|
||||
return
|
||||
}
|
||||
|
||||
val context = LocalContext.current
|
||||
val colorPair = remember(fallbackAvatar) {
|
||||
AvatarColorPair.create(context, fallbackAvatar.color)
|
||||
}
|
||||
|
||||
BoxWithConstraints(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = modifier
|
||||
.background(Color(colorPair.backgroundColor), shape)
|
||||
) {
|
||||
when (fallbackAvatar) {
|
||||
is FallbackAvatar.Resource -> {
|
||||
val size = remember(maxWidth) {
|
||||
FallbackAvatar.getSizeByDp(maxWidth)
|
||||
}
|
||||
|
||||
val padding = remember(maxWidth) {
|
||||
((maxWidth.value - (maxWidth.value * FallbackAvatar.ICON_TO_BACKGROUND_SCALE)) / 2).dp
|
||||
}
|
||||
|
||||
Icon(
|
||||
painter = painterResource(fallbackAvatar.getIconBySize(size)),
|
||||
contentDescription = null,
|
||||
tint = Color(colorPair.foregroundColor),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(padding)
|
||||
)
|
||||
}
|
||||
|
||||
is FallbackAvatar.Text -> {
|
||||
val size = DimensionUnit.DP.toPixels(maxWidth.value) * 0.8f
|
||||
val textSize = DimensionUnit.PIXELS.toDp(Avatars.getTextSizeForLength(context, fallbackAvatar.content, size, size))
|
||||
|
||||
// TODO [alex] -- Handle emoji
|
||||
|
||||
Text(
|
||||
text = fallbackAvatar.content,
|
||||
color = Color(colorPair.foregroundColor),
|
||||
fontSize = TextUnit(textSize, TextUnitType.Sp),
|
||||
fontFamily = FontFamily(AvatarRenderer.getTypeface(context))
|
||||
)
|
||||
}
|
||||
FallbackAvatar.Transparent -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SignalPreview
|
||||
@Composable
|
||||
fun FallbackAvatarImagePreview() {
|
||||
Previews.Preview {
|
||||
Column {
|
||||
Text(text = "Compose - Large")
|
||||
FallbackAvatarImage(
|
||||
fallbackAvatar = FallbackAvatar.Text("AE", AvatarColor.A100),
|
||||
modifier = Modifier.size(160.dp)
|
||||
)
|
||||
Text(text = "Compose - Medium")
|
||||
FallbackAvatarImage(
|
||||
fallbackAvatar = FallbackAvatar.Text("AE", AvatarColor.A100),
|
||||
modifier = Modifier.size(64.dp)
|
||||
)
|
||||
Text(text = "Compose - Small")
|
||||
FallbackAvatarImage(
|
||||
fallbackAvatar = FallbackAvatar.Text("AE", AvatarColor.A100),
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,8 +87,8 @@ class AvatarView @JvmOverloads constructor(
|
||||
avatar.setRecipient(recipient)
|
||||
}
|
||||
|
||||
fun setFallbackPhotoProvider(fallbackPhotoProvider: Recipient.FallbackPhotoProvider) {
|
||||
avatar.setFallbackPhotoProvider(fallbackPhotoProvider)
|
||||
fun setFallbackAvatarProvider(fallbackAvatarProvider: AvatarImageView.FallbackAvatarProvider?) {
|
||||
avatar.setFallbackAvatarProvider(fallbackAvatarProvider)
|
||||
}
|
||||
|
||||
fun disableQuickContact() {
|
||||
|
||||
@@ -12,7 +12,6 @@ import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Px;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
@@ -29,14 +28,18 @@ import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.SimpleTarget;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.bumptech.glide.request.transition.Transition;
|
||||
import com.google.android.material.imageview.ShapeableImageView;
|
||||
import com.google.android.material.shape.RelativeCornerSize;
|
||||
import com.google.android.material.shape.RoundedCornerTreatment;
|
||||
import com.google.android.material.shape.ShapeAppearanceModel;
|
||||
|
||||
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.components.settings.conversation.ConversationSettingsActivity;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies;
|
||||
@@ -46,13 +49,12 @@ import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheet
|
||||
import org.thoughtcrime.securesms.util.AvatarUtil;
|
||||
import org.thoughtcrime.securesms.util.BlurTransformation;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class AvatarImageView extends AppCompatImageView {
|
||||
public final class AvatarImageView extends ShapeableImageView {
|
||||
|
||||
private static final int SIZE_LARGE = 1;
|
||||
private static final int SIZE_SMALL = 2;
|
||||
@@ -65,14 +67,14 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
private int size;
|
||||
private boolean inverted;
|
||||
private OnClickListener listener;
|
||||
private Recipient.FallbackPhotoProvider fallbackPhotoProvider;
|
||||
private boolean blurred;
|
||||
private ChatColors chatColors;
|
||||
private FixedSizeTarget fixedSizeTarget;
|
||||
|
||||
private @Nullable RecipientContactPhoto recipientContactPhoto;
|
||||
private @NonNull Drawable unknownRecipientDrawable;
|
||||
private @Nullable AvatarColor fallbackPhotoColor;
|
||||
private @Nullable RecipientContactPhoto recipientContactPhoto;
|
||||
private @NonNull Drawable unknownRecipientDrawable;
|
||||
private @NonNull FallbackAvatarProvider fallbackAvatarProvider = new DefaultFallbackAvatarProvider();
|
||||
|
||||
|
||||
public AvatarImageView(Context context) {
|
||||
super(context);
|
||||
@@ -94,9 +96,11 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20).asDrawable(context, AvatarColor.UNKNOWN, inverted);
|
||||
unknownRecipientDrawable = new FallbackAvatarDrawable(context, new FallbackAvatar.Resource.Person(AvatarColor.UNKNOWN));
|
||||
blurred = false;
|
||||
chatColors = null;
|
||||
|
||||
setShapeAppearanceModel(ShapeAppearanceModel.builder().setAllCorners(new RoundedCornerTreatment()).setAllCornerSizes(new RelativeCornerSize(0.5f)).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -110,12 +114,8 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
super.setOnClickListener(listener);
|
||||
}
|
||||
|
||||
public void setFallbackPhotoProvider(Recipient.FallbackPhotoProvider fallbackPhotoProvider) {
|
||||
this.fallbackPhotoProvider = fallbackPhotoProvider;
|
||||
}
|
||||
|
||||
public void setFallbackPhotoColor(@Nullable AvatarColor fallbackPhotoColor) {
|
||||
this.fallbackPhotoColor = fallbackPhotoColor;
|
||||
public void setFallbackAvatarProvider(@Nullable FallbackAvatarProvider fallbackAvatarProvider) {
|
||||
this.fallbackAvatarProvider = fallbackAvatarProvider != null ? fallbackAvatarProvider : new DefaultFallbackAvatarProvider();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -184,18 +184,21 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
this.chatColors = chatColors;
|
||||
recipientContactPhoto = photo;
|
||||
|
||||
Recipient.FallbackPhotoProvider activeFallbackPhotoProvider = this.fallbackPhotoProvider;
|
||||
FallbackAvatarProvider activeFallbackPhotoProvider = this.fallbackAvatarProvider;
|
||||
if (recipient.isSelf() && avatarOptions.useSelfProfileAvatar) {
|
||||
activeFallbackPhotoProvider = new Recipient.FallbackPhotoProvider() {
|
||||
activeFallbackPhotoProvider = new FallbackAvatarProvider() {
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
|
||||
return super.getPhotoForRecipientWithName(recipient.getDisplayName(getContext()), ViewUtil.getWidth(AvatarImageView.this));
|
||||
public @NonNull FallbackAvatar getFallbackAvatar(@NonNull Recipient recipient) {
|
||||
if (recipient.isSelf()) {
|
||||
return new FallbackAvatar.Resource.Person(recipient.getAvatarColor());
|
||||
}
|
||||
|
||||
return FallbackAvatarProvider.super.getFallbackAvatar(recipient);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Drawable fallbackContactPhotoDrawable = size == SIZE_SMALL ? photo.recipient.getSmallFallbackContactPhotoDrawable(getContext(), inverted, activeFallbackPhotoProvider, ViewUtil.getWidth(this))
|
||||
: photo.recipient.getFallbackContactPhotoDrawable(getContext(), inverted, activeFallbackPhotoProvider, ViewUtil.getWidth(this));
|
||||
Drawable fallback = new FallbackAvatarDrawable(getContext(), activeFallbackPhotoProvider.getFallbackAvatar(recipient));
|
||||
|
||||
if (fixedSizeTarget != null) {
|
||||
requestManager.clear(fixedSizeTarget);
|
||||
@@ -212,8 +215,8 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
|
||||
RequestBuilder<Drawable> request = requestManager.load(photo.contactPhoto)
|
||||
.dontAnimate()
|
||||
.fallback(fallbackContactPhotoDrawable)
|
||||
.error(fallbackContactPhotoDrawable)
|
||||
.fallback(fallback)
|
||||
.error(fallback)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.transform(new MultiTransformation<>(transforms))
|
||||
@@ -227,7 +230,7 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
}
|
||||
|
||||
} else {
|
||||
setImageDrawable(fallbackContactPhotoDrawable);
|
||||
setImageDrawable(fallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,12 +238,7 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
} else {
|
||||
recipientContactPhoto = null;
|
||||
requestManager.clear(this);
|
||||
if (fallbackPhotoProvider != null) {
|
||||
setImageDrawable(fallbackPhotoProvider.getPhotoForRecipientWithoutName()
|
||||
.asDrawable(getContext(), Util.firstNonNull(fallbackPhotoColor, AvatarColor.UNKNOWN), inverted));
|
||||
} else {
|
||||
setImageDrawable(unknownRecipientDrawable);
|
||||
}
|
||||
setImageDrawable(unknownRecipientDrawable);
|
||||
|
||||
disableQuickContact();
|
||||
}
|
||||
@@ -267,13 +265,9 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
}
|
||||
}
|
||||
|
||||
public void setImageBytesForGroup(@Nullable byte[] avatarBytes,
|
||||
@Nullable Recipient.FallbackPhotoProvider fallbackPhotoProvider,
|
||||
@NonNull AvatarColor color)
|
||||
public void setImageBytesForGroup(@Nullable byte[] avatarBytes, @NonNull AvatarColor color)
|
||||
{
|
||||
Drawable fallback = Util.firstNonNull(fallbackPhotoProvider, Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER)
|
||||
.getPhotoForGroup()
|
||||
.asDrawable(getContext(), color);
|
||||
Drawable fallback = new FallbackAvatarDrawable(getContext(), new FallbackAvatar.Resource.Group(color));
|
||||
|
||||
Glide.with(this)
|
||||
.load(avatarBytes)
|
||||
@@ -295,6 +289,14 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
setClickable(listener != null);
|
||||
}
|
||||
|
||||
public interface FallbackAvatarProvider {
|
||||
default @NonNull FallbackAvatar getFallbackAvatar(@NonNull Recipient recipient) {
|
||||
return recipient.getFallbackAvatar();
|
||||
}
|
||||
}
|
||||
|
||||
private static class DefaultFallbackAvatarProvider implements FallbackAvatarProvider {}
|
||||
|
||||
private static class RecipientContactPhoto {
|
||||
|
||||
private final @NonNull Recipient recipient;
|
||||
|
||||
@@ -20,13 +20,14 @@ import com.bumptech.glide.RequestManager;
|
||||
|
||||
import org.signal.ringrtc.CallLinkRootKey;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar;
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable;
|
||||
import org.thoughtcrime.securesms.calls.links.CallLinks;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColorHash;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview;
|
||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository;
|
||||
import org.thoughtcrime.securesms.mms.ImageSlide;
|
||||
import org.thoughtcrime.securesms.mms.SlidesClickedListener;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||
import org.thoughtcrime.securesms.util.views.Stub;
|
||||
@@ -224,10 +225,10 @@ public class LinkPreviewView extends FrameLayout {
|
||||
thumbnailState.applyState(thumbnail);
|
||||
thumbnail.get().setImageDrawable(
|
||||
requestManager,
|
||||
Recipient.DEFAULT_FALLBACK_PHOTO_PROVIDER
|
||||
.getPhotoForCallLink()
|
||||
.asDrawable(getContext(),
|
||||
AvatarColorHash.forCallLink(callLinkRootKey.getKeyBytes()))
|
||||
new FallbackAvatarDrawable(
|
||||
getContext(),
|
||||
new FallbackAvatar.Resource.CallLink(AvatarColorHash.forCallLink(callLinkRootKey.getKeyBytes()))
|
||||
)
|
||||
);
|
||||
thumbnail.get().showSecondaryText(false);
|
||||
} else {
|
||||
|
||||
@@ -7,11 +7,8 @@ import org.thoughtcrime.securesms.avatar.view.AvatarView
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.components.settings.PreferenceModel
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto
|
||||
import org.thoughtcrime.securesms.database.model.StoryViewState
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.util.ViewUtil
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.LayoutFactory
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingAdapter
|
||||
import org.thoughtcrime.securesms.util.adapter.mapping.MappingViewHolder
|
||||
@@ -43,9 +40,7 @@ object AvatarPreference {
|
||||
}
|
||||
|
||||
private class ViewHolder(itemView: View) : MappingViewHolder<Model>(itemView) {
|
||||
private val avatar: AvatarView = itemView.findViewById<AvatarView>(R.id.bio_preference_avatar).apply {
|
||||
setFallbackPhotoProvider(AvatarPreferenceFallbackPhotoProvider())
|
||||
}
|
||||
private val avatar: AvatarView = itemView.findViewById<AvatarView>(R.id.bio_preference_avatar)
|
||||
|
||||
private val badge: BadgeImageView = itemView.findViewById(R.id.bio_preference_badge)
|
||||
|
||||
@@ -73,9 +68,4 @@ object AvatarPreference {
|
||||
avatar.setOnClickListener { model.onAvatarClick(avatar) }
|
||||
}
|
||||
}
|
||||
|
||||
private class AvatarPreferenceFallbackPhotoProvider : Recipient.FallbackPhotoProvider() {
|
||||
override val photoForGroup: FallbackContactPhoto
|
||||
get() = FallbackPhoto(R.drawable.ic_group_outline_40, ViewUtil.dpToPx(8))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,13 +25,12 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable;
|
||||
import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors;
|
||||
import org.thoughtcrime.securesms.events.CallParticipant;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
@@ -50,8 +49,6 @@ import java.util.concurrent.TimeUnit;
|
||||
*/
|
||||
public class CallParticipantView extends ConstraintLayout {
|
||||
|
||||
private static final FallbackPhotoProvider FALLBACK_PHOTO_PROVIDER = new FallbackPhotoProvider();
|
||||
|
||||
private static final long DELAY_SHOWING_MISSING_MEDIA_KEYS = TimeUnit.SECONDS.toMillis(5);
|
||||
private static final int SMALL_AVATAR = ViewUtil.dpToPx(96);
|
||||
private static final int LARGE_AVATAR = ViewUtil.dpToPx(112);
|
||||
@@ -116,7 +113,6 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
raiseHandIcon = findViewById(R.id.call_participant_raise_hand_icon);
|
||||
nameLabel = findViewById(R.id.call_participant_name_label);
|
||||
|
||||
avatar.setFallbackPhotoProvider(FALLBACK_PHOTO_PROVIDER);
|
||||
useLargeAvatar();
|
||||
}
|
||||
|
||||
@@ -422,12 +418,13 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
private void setPipAvatar(@NonNull Recipient recipient) {
|
||||
ContactPhoto contactPhoto = recipient.isSelf() ? new ProfileContactPhoto(Recipient.self())
|
||||
: recipient.getContactPhoto();
|
||||
FallbackContactPhoto fallbackPhoto = recipient.getFallbackContactPhoto(FALLBACK_PHOTO_PROVIDER);
|
||||
|
||||
FallbackAvatarDrawable fallbackAvatarDrawable = new FallbackAvatarDrawable(getContext(), recipient.getFallbackAvatar());
|
||||
|
||||
Glide.with(this)
|
||||
.load(contactPhoto)
|
||||
.fallback(fallbackPhoto.asCallCard(getContext()))
|
||||
.error(fallbackPhoto.asCallCard(getContext()))
|
||||
.fallback(fallbackAvatarDrawable)
|
||||
.error(fallbackAvatarDrawable)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.fitCenter()
|
||||
.into(pipAvatar);
|
||||
@@ -455,20 +452,6 @@ public class CallParticipantView extends ConstraintLayout {
|
||||
.show();
|
||||
}
|
||||
|
||||
private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider {
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
|
||||
return super.getPhotoForRecipientWithoutName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() {
|
||||
ResourceContactPhoto photo = new ResourceContactPhoto(R.drawable.ic_profile_outline_120);
|
||||
photo.setScaleType(ImageView.ScaleType.CENTER_CROP);
|
||||
return photo;
|
||||
}
|
||||
}
|
||||
|
||||
public enum SelfPipMode {
|
||||
NOT_SELF_PIP,
|
||||
NORMAL_SELF_PIP,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -21,8 +21,6 @@ import org.signal.core.util.DimensionUnit;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.databinding.ConversationHeaderViewBinding;
|
||||
import org.thoughtcrime.securesms.fonts.SignalSymbols;
|
||||
@@ -50,8 +48,6 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
inflate(getContext(), R.layout.conversation_header_view, this);
|
||||
|
||||
binding = ConversationHeaderViewBinding.bind(this);
|
||||
|
||||
binding.messageRequestAvatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
|
||||
}
|
||||
|
||||
public void setBadge(@Nullable Recipient recipient) {
|
||||
@@ -197,21 +193,4 @@ public class ConversationHeaderView extends ConstraintLayout {
|
||||
.append(SpanUtil.space(8, DimensionUnit.SP))
|
||||
.append(input);
|
||||
}
|
||||
|
||||
private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider {
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() {
|
||||
return new ResourceContactPhoto(R.drawable.ic_profile_64);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForGroup() {
|
||||
return new ResourceContactPhoto(R.drawable.ic_group_64);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
|
||||
return new ResourceContactPhoto(R.drawable.ic_note_64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import android.os.Build
|
||||
import android.text.SpannableStringBuilder
|
||||
import androidx.core.content.pm.ShortcutInfoCompat
|
||||
import androidx.core.graphics.drawable.IconCompat
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.bumptech.glide.request.target.CustomTarget
|
||||
@@ -35,6 +36,7 @@ import org.signal.paging.PagingConfig
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.ShortcutLauncherActivity
|
||||
import org.thoughtcrime.securesms.attachments.TombstoneAttachment
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiStrings
|
||||
import org.thoughtcrime.securesms.components.reminder.BubbleOptOutReminder
|
||||
import org.thoughtcrime.securesms.components.reminder.ExpiredBuildReminder
|
||||
@@ -88,7 +90,6 @@ import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.sms.MessageSender
|
||||
import org.thoughtcrime.securesms.sms.MessageSender.PreUploadResult
|
||||
import org.thoughtcrime.securesms.util.BitmapUtil
|
||||
import org.thoughtcrime.securesms.util.DrawableUtil
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.thoughtcrime.securesms.util.MessageUtil
|
||||
@@ -489,7 +490,7 @@ class ConversationRepository(
|
||||
}
|
||||
|
||||
fun getRecipientContactPhotoBitmap(context: Context, requestManager: RequestManager, recipient: Recipient): Single<ShortcutInfoCompat> {
|
||||
val fallback = recipient.fallbackContactPhoto.asDrawable(context, recipient.avatarColor, false)
|
||||
val fallback = FallbackAvatarDrawable(context, recipient.getFallbackAvatar())
|
||||
|
||||
return Single
|
||||
.create { emitter ->
|
||||
@@ -618,7 +619,7 @@ class ConversationRepository(
|
||||
* The result of the Glide load to get a user's contact photo. This can then be transformed into
|
||||
* something that the Android system likes via [transformToFinalBitmap]
|
||||
*/
|
||||
sealed interface ContactPhotoResult {
|
||||
private sealed interface ContactPhotoResult {
|
||||
|
||||
companion object {
|
||||
private val SHORTCUT_ICON_SIZE = if (Build.VERSION.SDK_INT >= 26) 72.dp else (48 + 16 * 2).dp
|
||||
@@ -627,7 +628,7 @@ class ConversationRepository(
|
||||
class DrawableResult(private val drawable: Drawable) : ContactPhotoResult {
|
||||
override fun transformToFinalBitmap(): Single<Bitmap> {
|
||||
return Single.create {
|
||||
val bitmap = DrawableUtil.toBitmap(drawable, SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE)
|
||||
val bitmap = DrawableUtil.wrapBitmapForShortcutInfo(drawable.toBitmap(SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE))
|
||||
it.setCancellable {
|
||||
bitmap.recycle()
|
||||
}
|
||||
@@ -639,7 +640,7 @@ class ConversationRepository(
|
||||
class BitmapResult(private val bitmap: Bitmap) : ContactPhotoResult {
|
||||
override fun transformToFinalBitmap(): Single<Bitmap> {
|
||||
return Single.create {
|
||||
val bitmap = BitmapUtil.createScaledBitmap(bitmap, SHORTCUT_ICON_SIZE, SHORTCUT_ICON_SIZE)
|
||||
val bitmap = DrawableUtil.wrapBitmapForShortcutInfo(bitmap)
|
||||
it.setCancellable {
|
||||
bitmap.recycle()
|
||||
}
|
||||
|
||||
@@ -23,14 +23,11 @@ import org.signal.core.util.logging.Log;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiTextView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.groups.ui.managegroup.dialogs.GroupDescriptionDialog;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupDescriptionUtil;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
import org.thoughtcrime.securesms.util.LongClickMovementMethod;
|
||||
import org.thoughtcrime.securesms.util.ThemeUtil;
|
||||
@@ -87,7 +84,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
|
||||
groupCancelButton.setOnClickListener(v -> dismiss());
|
||||
|
||||
avatar.setImageBytesForGroup(null, new FallbackPhotoProvider(), AvatarColor.UNKNOWN);
|
||||
avatar.setImageBytesForGroup(null, AvatarColor.UNKNOWN);
|
||||
|
||||
return view;
|
||||
}
|
||||
@@ -118,7 +115,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
});
|
||||
groupJoinButton.setVisibility(View.VISIBLE);
|
||||
|
||||
avatar.setImageBytesForGroup(details.getAvatarBytes(), new FallbackPhotoProvider(), AvatarColor.UNKNOWN);
|
||||
avatar.setImageBytesForGroup(details.getAvatarBytes(), AvatarColor.UNKNOWN);
|
||||
|
||||
groupCancelButton.setVisibility(View.VISIBLE);
|
||||
});
|
||||
@@ -204,10 +201,4 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
BottomSheetUtil.show(manager, tag, this);
|
||||
}
|
||||
|
||||
private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider {
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForGroup() {
|
||||
return new ResourceContactPhoto(R.drawable.ic_group_outline_48);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ import com.bumptech.glide.Glide;
|
||||
import org.thoughtcrime.securesms.BaseActivity;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -62,7 +59,6 @@ public class CalleeMustAcceptMessageRequestActivity extends BaseActivity {
|
||||
avatar = findViewById(R.id.avatar);
|
||||
okay = findViewById(R.id.okay);
|
||||
|
||||
avatar.setFallbackPhotoProvider(new FallbackPhotoProvider());
|
||||
okay.setOnClickListener(v -> finish());
|
||||
|
||||
RecipientId recipientId = getIntent().getParcelableExtra(RECIPIENT_ID_EXTRA);
|
||||
@@ -88,11 +84,4 @@ public class CalleeMustAcceptMessageRequestActivity extends BaseActivity {
|
||||
|
||||
handler.removeCallbacks(finisher);
|
||||
}
|
||||
|
||||
private static class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider {
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForRecipientWithoutName() {
|
||||
return new ResourceContactPhoto(R.drawable.ic_profile_80);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import androidx.core.app.TaskStackBuilder
|
||||
import org.signal.core.util.PendingIntentFlags
|
||||
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.TurnOffContactJoinedNotificationsActivity
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
@@ -27,7 +28,6 @@ import org.thoughtcrime.securesms.service.KeyCachingService
|
||||
import org.thoughtcrime.securesms.stories.StoryViewerArgs
|
||||
import org.thoughtcrime.securesms.stories.viewer.StoryViewerActivity
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import java.lang.NullPointerException
|
||||
|
||||
/**
|
||||
* Encapsulate all the notifications for a given conversation (thread) and the top
|
||||
@@ -57,7 +57,7 @@ data class NotificationConversation(
|
||||
return if (SignalStore.settings().messageNotificationsPrivacy.isDisplayContact) {
|
||||
recipient.getContactDrawable(context)
|
||||
} else {
|
||||
GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, AvatarColor.UNKNOWN)
|
||||
FallbackAvatarDrawable(context, FallbackAvatar.forTextOrDefault("Unknown", AvatarColor.UNKNOWN)).circleCrop()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,9 +13,9 @@ import com.bumptech.glide.load.Transformation
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop
|
||||
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.FallbackContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.mms.DecryptableStreamUriLoader.DecryptableUri
|
||||
@@ -37,7 +37,7 @@ fun Drawable?.toLargeBitmap(context: Context): Bitmap? {
|
||||
|
||||
fun Recipient.getContactDrawable(context: Context): Drawable? {
|
||||
val contactPhoto: ContactPhoto? = if (isSelf) ProfileContactPhoto(this) else contactPhoto
|
||||
val fallbackContactPhoto: FallbackContactPhoto = if (isSelf) getFallback(context) else fallbackContactPhoto
|
||||
val fallbackAvatar: FallbackAvatar = if (isSelf) getFallback(context) else getFallbackAvatar()
|
||||
return if (contactPhoto != null) {
|
||||
try {
|
||||
val transforms: MutableList<Transformation<Bitmap>> = mutableListOf()
|
||||
@@ -56,12 +56,12 @@ fun Recipient.getContactDrawable(context: Context): Drawable? {
|
||||
)
|
||||
.get()
|
||||
} catch (e: InterruptedException) {
|
||||
fallbackContactPhoto.asDrawable(context, avatarColor)
|
||||
FallbackAvatarDrawable(context, fallbackAvatar).circleCrop()
|
||||
} catch (e: ExecutionException) {
|
||||
fallbackContactPhoto.asDrawable(context, avatarColor)
|
||||
FallbackAvatarDrawable(context, fallbackAvatar).circleCrop()
|
||||
}
|
||||
} else {
|
||||
fallbackContactPhoto.asDrawable(context, avatarColor)
|
||||
FallbackAvatarDrawable(context, fallbackAvatar).circleCrop()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ fun Intent.makeUniqueToPreventMerging(): Intent {
|
||||
return setData((Uri.parse("custom://" + System.currentTimeMillis())))
|
||||
}
|
||||
|
||||
fun Recipient.getFallback(context: Context): FallbackContactPhoto {
|
||||
return GeneratedContactPhoto(getDisplayName(context), R.drawable.ic_profile_outline_40)
|
||||
fun Recipient.getFallback(context: Context): FallbackAvatar {
|
||||
return FallbackAvatar.forTextOrDefault(getDisplayName(context), avatarColor)
|
||||
}
|
||||
|
||||
fun NotificationManager.isDisplayingSummaryNotification(): Boolean {
|
||||
|
||||
@@ -19,8 +19,9 @@ import org.signal.core.util.concurrent.SignalExecutors
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.MainActivity
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatarDrawable
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiStrings
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
@@ -390,7 +391,7 @@ object NotificationFactory {
|
||||
recipient.getContactDrawable(context)
|
||||
}
|
||||
} else {
|
||||
GeneratedContactPhoto("Unknown", R.drawable.ic_profile_outline_40).asDrawable(context, AvatarColor.UNKNOWN)
|
||||
FallbackAvatarDrawable(context, FallbackAvatar.forTextOrDefault("Unknown", AvatarColor.UNKNOWN)).circleCrop()
|
||||
}.toLargeBitmap(context)
|
||||
|
||||
val builder: NotificationBuilder = NotificationBuilder.create(context)
|
||||
|
||||
@@ -8,12 +8,8 @@ import android.widget.FrameLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto20dp;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto;
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor;
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar;
|
||||
import org.thoughtcrime.securesms.components.AvatarImageView;
|
||||
import org.thoughtcrime.securesms.databinding.ReviewBannerViewBinding;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
@@ -39,10 +35,10 @@ public class ReviewBannerView extends FrameLayout {
|
||||
|
||||
binding = ReviewBannerViewBinding.bind(this);
|
||||
|
||||
FallbackPhotoProvider provider = new FallbackPhotoProvider();
|
||||
FallbackAvatarProvider provider = new FallbackAvatarProvider();
|
||||
|
||||
binding.bannerBottomRightAvatar.setFallbackPhotoProvider(provider);
|
||||
binding.bannerTopLeftAvatar.setFallbackPhotoProvider(provider);
|
||||
binding.bannerBottomRightAvatar.setFallbackAvatarProvider(provider);
|
||||
binding.bannerTopLeftAvatar.setFallbackAvatarProvider(provider);
|
||||
|
||||
binding.bannerClose.setOnClickListener(v -> {
|
||||
if (onHideListener != null && onHideListener.onHide()) {
|
||||
@@ -86,44 +82,14 @@ public class ReviewBannerView extends FrameLayout {
|
||||
binding.bannerTapToReview.setOnClickListener(l);
|
||||
}
|
||||
|
||||
private static final class FallbackPhotoProvider extends Recipient.FallbackPhotoProvider {
|
||||
private static final class FallbackAvatarProvider implements AvatarImageView.FallbackAvatarProvider {
|
||||
@Override
|
||||
public @NonNull
|
||||
FallbackContactPhoto getPhotoForGroup() {
|
||||
throw new UnsupportedOperationException("This provider does not support groups");
|
||||
}
|
||||
public @NonNull FallbackAvatar getFallbackAvatar(@NonNull Recipient recipient) {
|
||||
if (recipient.isIndividual() && !recipient.isSelf()) {
|
||||
return new FallbackAvatar.Resource.Person(recipient.getAvatarColor());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForResolvingRecipient() {
|
||||
throw new UnsupportedOperationException("This provider does not support resolving recipients");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
|
||||
return new ResourceContactPhoto(R.drawable.symbol_note_light_24, R.drawable.symbol_note_light_24);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FallbackContactPhoto getPhotoForRecipientWithName(String name, int targetSize) {
|
||||
return new FixedSizeGeneratedContactPhoto(name, R.drawable.ic_profile_outline_20);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public FallbackContactPhoto getPhotoForRecipientWithoutName() {
|
||||
return new FallbackPhoto20dp(R.drawable.ic_profile_outline_20);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class FixedSizeGeneratedContactPhoto extends GeneratedContactPhoto {
|
||||
public FixedSizeGeneratedContactPhoto(@NonNull String name, int fallbackResId) {
|
||||
super(name, fallbackResId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Drawable newFallbackDrawable(@NonNull Context context, @NonNull AvatarColor color, boolean inverted) {
|
||||
return new FallbackPhoto20dp(getFallbackResId()).asDrawable(context, color, inverted);
|
||||
return recipient.getFallbackAvatar();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,8 +21,8 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper
|
||||
import org.thoughtcrime.securesms.recipients.Recipient
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId
|
||||
import org.thoughtcrime.securesms.service.KeyCachingService
|
||||
import org.thoughtcrime.securesms.util.AdaptiveBitmapMetrics
|
||||
import org.thoughtcrime.securesms.util.AvatarUtil
|
||||
import org.thoughtcrime.securesms.util.DrawableUtil
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
@@ -168,7 +168,7 @@ class AvatarProvider : BaseContentProvider() {
|
||||
ParcelFileDescriptor.AutoCloseOutputStream(pipe[1]).use { output ->
|
||||
if (VERBOSE) Log.i(TAG, "Writing to pipe:${recipient.id}")
|
||||
|
||||
AvatarUtil.getBitmapForNotification(context!!, recipient, DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE).apply {
|
||||
AvatarUtil.getBitmapForNotification(context!!, recipient, AdaptiveBitmapMetrics.innerWidth).apply {
|
||||
compress(Bitmap.CompressFormat.PNG, 100, output)
|
||||
}
|
||||
output.flush()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.thoughtcrime.securesms.recipients
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import androidx.annotation.AnyThread
|
||||
import androidx.annotation.WorkerThread
|
||||
@@ -14,15 +13,12 @@ import org.signal.core.util.logging.Log
|
||||
import org.signal.core.util.nullIfBlank
|
||||
import org.signal.libsignal.zkgroup.profiles.ExpiringProfileKeyCredential
|
||||
import org.thoughtcrime.securesms.R
|
||||
import org.thoughtcrime.securesms.avatar.fallback.FallbackAvatar
|
||||
import org.thoughtcrime.securesms.badges.models.Badge
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GroupRecordContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ProfileContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.ResourceContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.SystemContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.TransparentContactPhoto
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors
|
||||
import org.thoughtcrime.securesms.conversation.colors.ChatColors.Id.Auto
|
||||
@@ -47,7 +43,6 @@ import org.thoughtcrime.securesms.phonenumbers.NumberUtil
|
||||
import org.thoughtcrime.securesms.phonenumbers.PhoneNumberFormatter
|
||||
import org.thoughtcrime.securesms.profiles.ProfileName
|
||||
import org.thoughtcrime.securesms.service.webrtc.links.CallLinkRoomId
|
||||
import org.thoughtcrime.securesms.util.AvatarUtil
|
||||
import org.thoughtcrime.securesms.util.UsernameUtil.isValidUsernameForSearch
|
||||
import org.thoughtcrime.securesms.util.Util
|
||||
import org.thoughtcrime.securesms.wallpaper.ChatWallpaper
|
||||
@@ -258,10 +253,6 @@ class Recipient(
|
||||
null
|
||||
}
|
||||
|
||||
/** A photo you can use as a fallback if [contactPhoto] fails to load. */
|
||||
val fallbackContactPhoto: FallbackContactPhoto
|
||||
get() = getFallbackContactPhoto(DEFAULT_FALLBACK_PHOTO_PROVIDER)
|
||||
|
||||
/** The URI of the ringtone that should be used when receiving a message from this recipient, if set. */
|
||||
val messageRingtone: Uri? by lazy {
|
||||
if (messageRingtoneUri != null && messageRingtoneUri.scheme != null && messageRingtoneUri.scheme!!.startsWith("file")) {
|
||||
@@ -609,45 +600,29 @@ class Recipient(
|
||||
}
|
||||
}
|
||||
|
||||
fun getFallbackContactPhotoDrawable(context: Context?, inverted: Boolean): Drawable {
|
||||
return getFallbackContactPhotoDrawable(context, inverted, DEFAULT_FALLBACK_PHOTO_PROVIDER, AvatarUtil.UNDEFINED_SIZE)
|
||||
}
|
||||
|
||||
fun getFallbackContactPhotoDrawable(context: Context?, inverted: Boolean, fallbackPhotoProvider: FallbackPhotoProvider?, targetSize: Int): Drawable {
|
||||
return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER), targetSize).asDrawable(context!!, avatarColor, inverted)
|
||||
}
|
||||
|
||||
fun getSmallFallbackContactPhotoDrawable(context: Context?, inverted: Boolean, fallbackPhotoProvider: FallbackPhotoProvider?, targetSize: Int): Drawable {
|
||||
return getFallbackContactPhoto(Util.firstNonNull(fallbackPhotoProvider, DEFAULT_FALLBACK_PHOTO_PROVIDER), targetSize).asSmallDrawable(context!!, avatarColor, inverted)
|
||||
}
|
||||
|
||||
fun getFallbackContactPhoto(fallbackPhotoProvider: FallbackPhotoProvider): FallbackContactPhoto {
|
||||
return getFallbackContactPhoto(fallbackPhotoProvider, AvatarUtil.UNDEFINED_SIZE)
|
||||
}
|
||||
|
||||
private fun getFallbackContactPhoto(fallbackPhotoProvider: FallbackPhotoProvider, targetSize: Int): FallbackContactPhoto {
|
||||
fun getFallbackAvatar(): FallbackAvatar {
|
||||
return if (isSelf) {
|
||||
fallbackPhotoProvider.photoForLocalNumber
|
||||
FallbackAvatar.Resource.Local(avatarColor)
|
||||
} else if (isResolving) {
|
||||
fallbackPhotoProvider.photoForResolvingRecipient
|
||||
FallbackAvatar.Transparent
|
||||
} else if (isDistributionList) {
|
||||
fallbackPhotoProvider.photoForDistributionList
|
||||
FallbackAvatar.Resource.DistributionList(avatarColor)
|
||||
} else if (isCallLink) {
|
||||
fallbackPhotoProvider.photoForCallLink
|
||||
FallbackAvatar.Resource.CallLink(avatarColor)
|
||||
} else if (groupIdValue != null) {
|
||||
fallbackPhotoProvider.photoForGroup
|
||||
FallbackAvatar.Resource.Group(avatarColor)
|
||||
} else if (isGroup) {
|
||||
fallbackPhotoProvider.photoForGroup
|
||||
FallbackAvatar.Resource.Group(avatarColor)
|
||||
} else if (groupName.isNotNullOrBlank()) {
|
||||
fallbackPhotoProvider.getPhotoForRecipientWithName(groupName, targetSize)
|
||||
FallbackAvatar.forTextOrDefault(groupName, avatarColor, FallbackAvatar.Resource.Group(avatarColor))
|
||||
} else if (!nickname.isEmpty) {
|
||||
fallbackPhotoProvider.getPhotoForRecipientWithName(nickname.toString(), targetSize)
|
||||
FallbackAvatar.forTextOrDefault(nickname.toString(), avatarColor)
|
||||
} else if (systemContactName.isNotNullOrBlank()) {
|
||||
fallbackPhotoProvider.getPhotoForRecipientWithName(systemContactName, targetSize)
|
||||
FallbackAvatar.forTextOrDefault(systemContactName, avatarColor)
|
||||
} else if (!profileName.isEmpty) {
|
||||
fallbackPhotoProvider.getPhotoForRecipientWithName(profileName.toString(), targetSize)
|
||||
FallbackAvatar.forTextOrDefault(profileName.toString(), avatarColor)
|
||||
} else {
|
||||
fallbackPhotoProvider.photoForRecipientWithoutName
|
||||
FallbackAvatar.Resource.Person(avatarColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,30 +793,6 @@ class Recipient(
|
||||
return id.hashCode()
|
||||
}
|
||||
|
||||
open class FallbackPhotoProvider {
|
||||
open val photoForLocalNumber: FallbackContactPhoto
|
||||
get() = ResourceContactPhoto(R.drawable.ic_note_34, R.drawable.ic_note_24)
|
||||
|
||||
open val photoForResolvingRecipient: FallbackContactPhoto
|
||||
get() = TransparentContactPhoto()
|
||||
|
||||
open val photoForGroup: FallbackContactPhoto
|
||||
get() = ResourceContactPhoto(R.drawable.ic_group_outline_34, R.drawable.ic_group_outline_20, R.drawable.ic_group_outline_48)
|
||||
|
||||
open val photoForRecipientWithoutName: FallbackContactPhoto
|
||||
get() = ResourceContactPhoto(R.drawable.ic_profile_outline_40, R.drawable.ic_profile_outline_20, R.drawable.ic_profile_outline_48)
|
||||
|
||||
val photoForDistributionList: FallbackContactPhoto
|
||||
get() = ResourceContactPhoto(R.drawable.symbol_stories_24, R.drawable.symbol_stories_24, R.drawable.symbol_stories_24)
|
||||
|
||||
val photoForCallLink: FallbackContactPhoto
|
||||
get() = ResourceContactPhoto(R.drawable.symbol_video_24, R.drawable.symbol_video_24, R.drawable.symbol_video_24)
|
||||
|
||||
open fun getPhotoForRecipientWithName(name: String, targetSize: Int): FallbackContactPhoto {
|
||||
return GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40, targetSize)
|
||||
}
|
||||
}
|
||||
|
||||
private class MissingAddressError(recipientId: RecipientId) : AssertionError("Missing address for " + recipientId.serialize())
|
||||
|
||||
companion object {
|
||||
@@ -850,9 +801,6 @@ class Recipient(
|
||||
@JvmField
|
||||
val UNKNOWN = Recipient()
|
||||
|
||||
@JvmField
|
||||
val DEFAULT_FALLBACK_PHOTO_PROVIDER = FallbackPhotoProvider()
|
||||
|
||||
private const val MAX_MEMBER_NAMES = 10
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,8 +31,6 @@ import org.thoughtcrime.securesms.badges.BadgeImageView;
|
||||
import org.thoughtcrime.securesms.badges.view.ViewBadgeBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.components.settings.DSLSettingsIcon;
|
||||
import org.thoughtcrime.securesms.components.settings.conversation.preferences.ButtonStripPreference;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto;
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto80dp;
|
||||
import org.thoughtcrime.securesms.fonts.SignalSymbols;
|
||||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.nicknames.NicknameActivity;
|
||||
@@ -167,13 +165,6 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
||||
|
||||
viewModel.getRecipient().observe(getViewLifecycleOwner(), recipient -> {
|
||||
interactionsContainer.setVisibility(recipient.isSelf() ? View.GONE : View.VISIBLE);
|
||||
|
||||
avatar.setFallbackPhotoProvider(new Recipient.FallbackPhotoProvider() {
|
||||
@Override
|
||||
public @NonNull FallbackContactPhoto getPhotoForLocalNumber() {
|
||||
return new FallbackPhoto80dp(R.drawable.ic_note_80, recipient.getAvatarColor());
|
||||
}
|
||||
});
|
||||
avatar.displayChatAvatar(recipient);
|
||||
|
||||
if (!recipient.isSelf()) {
|
||||
|
||||
@@ -4,10 +4,8 @@ import android.animation.Animator
|
||||
import android.animation.AnimatorSet
|
||||
import android.animation.ObjectAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.media.AudioManager
|
||||
import android.os.Bundle
|
||||
import android.text.SpannableString
|
||||
@@ -52,13 +50,9 @@ import org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
||||
import org.thoughtcrime.securesms.components.segmentedprogressbar.SegmentedProgressBar
|
||||
import org.thoughtcrime.securesms.components.segmentedprogressbar.SegmentedProgressBarListener
|
||||
import org.thoughtcrime.securesms.components.spoiler.SpoilerAnnotation
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.avatars.FallbackPhoto20dp
|
||||
import org.thoughtcrime.securesms.contacts.avatars.GeneratedContactPhoto
|
||||
import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey
|
||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||
import org.thoughtcrime.securesms.conversation.MessageStyler
|
||||
import org.thoughtcrime.securesms.conversation.colors.AvatarColor
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardBottomSheet
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragment
|
||||
import org.thoughtcrime.securesms.conversation.mutiselect.forward.MultiselectForwardFragmentArgs
|
||||
@@ -229,9 +223,6 @@ class StoryViewerPageFragment :
|
||||
addToGroupStoryButtonWrapper
|
||||
)
|
||||
|
||||
senderAvatar.setFallbackPhotoProvider(FallbackPhotoProvider())
|
||||
groupAvatar.setFallbackPhotoProvider(FallbackPhotoProvider())
|
||||
|
||||
closeView.setOnClickListener {
|
||||
requireActivity().onBackPressed()
|
||||
}
|
||||
@@ -1376,30 +1367,6 @@ class StoryViewerPageFragment :
|
||||
}
|
||||
}
|
||||
|
||||
private class FallbackPhotoProvider : Recipient.FallbackPhotoProvider() {
|
||||
override val photoForGroup: FallbackContactPhoto
|
||||
get() = FallbackPhoto20dp(R.drawable.symbol_group_20)
|
||||
|
||||
override val photoForResolvingRecipient: FallbackContactPhoto
|
||||
get() = throw UnsupportedOperationException("This provider does not support resolving recipients")
|
||||
|
||||
override val photoForLocalNumber: FallbackContactPhoto
|
||||
get() = throw UnsupportedOperationException("This provider does not support local number")
|
||||
|
||||
override fun getPhotoForRecipientWithName(name: String, targetSize: Int): FallbackContactPhoto {
|
||||
return FixedSizeGeneratedContactPhoto(name, R.drawable.symbol_person_20)
|
||||
}
|
||||
|
||||
override val photoForRecipientWithoutName: FallbackContactPhoto
|
||||
get() = FallbackPhoto20dp(R.drawable.symbol_person_20)
|
||||
}
|
||||
|
||||
private class FixedSizeGeneratedContactPhoto(name: String, fallbackResId: Int) : GeneratedContactPhoto(name, fallbackResId) {
|
||||
override fun newFallbackDrawable(context: Context, color: AvatarColor, inverted: Boolean): Drawable {
|
||||
return FallbackPhoto20dp(fallbackResId).asDrawable(context, color, inverted)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onContentReady() {
|
||||
Log.d(TAG, "Story content is ready.")
|
||||
sharedViewModel.setContentIsReady()
|
||||
|
||||
@@ -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