Removed com.amulyakhare.textdrawable

Cleaned up references to TextDrawable.
Also cleaned up the way we  load layered drawables, used in ProfilePictureView for the load state as the icons were stretched across and didn't look nice.
This commit is contained in:
ThomasSession 2024-08-08 17:04:59 +10:00 committed by fanchao
parent 518fe5e712
commit 62873ee773
10 changed files with 40 additions and 309 deletions

View File

@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-sdk tools:overrideLibrary="com.amulyakhare.textdrawable,com.astuetz.pagerslidingtabstrip,pl.tajchert.waitingdots,com.h6ah4i.android.multiselectlistpreferencecompat,android.support.v13,com.davemorrissey.labs.subscaleview,com.tomergoldst.tooltips,com.klinker.android.send_message,com.takisoft.colorpicker,android.support.v14.preference" /> <uses-sdk tools:overrideLibrary="com.astuetz.pagerslidingtabstrip,pl.tajchert.waitingdots,com.h6ah4i.android.multiselectlistpreferencecompat,android.support.v13,com.davemorrissey.labs.subscaleview,com.tomergoldst.tooltips,com.klinker.android.send_message,com.takisoft.colorpicker,android.support.v14.preference" />
<permission <permission
android:name="network.loki.messenger.ACCESS_SESSION_SECRETS" android:name="network.loki.messenger.ACCESS_SESSION_SECRETS"

View File

@ -6,13 +6,9 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.graphics.Outline import android.graphics.Outline
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorManager
import android.media.AudioManager import android.media.AudioManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.Settings
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewOutlineProvider import android.view.ViewOutlineProvider
@ -22,7 +18,6 @@ import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.bumptech.glide.load.engine.DiskCacheStrategy
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ -31,7 +26,6 @@ import kotlinx.coroutines.launch
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ActivityWebrtcBinding import network.loki.messenger.databinding.ActivityWebrtcBinding
import org.apache.commons.lang3.time.DurationFormatUtils import org.apache.commons.lang3.time.DurationFormatUtils
import org.session.libsession.avatars.ProfileContactPhoto
import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.contacts.Contact
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.truncateIdForDisplay import org.session.libsession.utilities.truncateIdForDisplay
@ -41,7 +35,6 @@ import org.thoughtcrime.securesms.dependencies.DatabaseComponent
import org.thoughtcrime.securesms.mms.GlideApp import org.thoughtcrime.securesms.mms.GlideApp
import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.permissions.Permissions
import org.thoughtcrime.securesms.service.WebRtcCallService import org.thoughtcrime.securesms.service.WebRtcCallService
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator
import org.thoughtcrime.securesms.webrtc.AudioManagerCommand import org.thoughtcrime.securesms.webrtc.AudioManagerCommand
import org.thoughtcrime.securesms.webrtc.CallViewModel import org.thoughtcrime.securesms.webrtc.CallViewModel
import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_CONNECTED import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_CONNECTED
@ -53,7 +46,6 @@ import org.thoughtcrime.securesms.webrtc.CallViewModel.State.CALL_RINGING
import org.thoughtcrime.securesms.webrtc.Orientation import org.thoughtcrime.securesms.webrtc.Orientation
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.EARPIECE
import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE import org.thoughtcrime.securesms.webrtc.audio.SignalAudioManager.AudioDevice.SPEAKER_PHONE
import kotlin.math.asin
@AndroidEntryPoint @AndroidEntryPoint
class WebRtcCallActivity : PassphraseRequiredActionBarActivity() { class WebRtcCallActivity : PassphraseRequiredActionBarActivity() {

View File

@ -1,198 +0,0 @@
package org.thoughtcrime.securesms.components;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.provider.ContactsContract;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewOutlineProvider;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.session.libsession.avatars.ContactColors;
import org.session.libsession.avatars.ContactPhoto;
import org.session.libsession.avatars.ResourceContactPhoto;
import org.session.libsession.utilities.Address;
import org.session.libsession.utilities.ThemeUtil;
import org.session.libsession.utilities.recipients.Recipient;
import org.session.libsession.utilities.recipients.RecipientExporter;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.AvatarPlaceholderGenerator;
import java.util.Objects;
import network.loki.messenger.R;
public class AvatarImageView extends AppCompatImageView {
private static final String TAG = AvatarImageView.class.getSimpleName();
private static final Paint LIGHT_THEME_OUTLINE_PAINT = new Paint();
private static final Paint DARK_THEME_OUTLINE_PAINT = new Paint();
static {
LIGHT_THEME_OUTLINE_PAINT.setColor(Color.argb((int) (255 * 0.2), 0, 0, 0));
LIGHT_THEME_OUTLINE_PAINT.setStyle(Paint.Style.STROKE);
LIGHT_THEME_OUTLINE_PAINT.setStrokeWidth(1f);
LIGHT_THEME_OUTLINE_PAINT.setAntiAlias(true);
DARK_THEME_OUTLINE_PAINT.setColor(Color.argb((int) (255 * 0.2), 255, 255, 255));
DARK_THEME_OUTLINE_PAINT.setStyle(Paint.Style.STROKE);
DARK_THEME_OUTLINE_PAINT.setStrokeWidth(1f);
DARK_THEME_OUTLINE_PAINT.setAntiAlias(true);
}
private boolean inverted;
private Paint outlinePaint;
private OnClickListener listener;
private @Nullable RecipientContactPhoto recipientContactPhoto;
private @NonNull Drawable unknownRecipientDrawable;
public AvatarImageView(Context context) {
super(context);
initialize(context, null);
}
public AvatarImageView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs);
}
private void initialize(@NonNull Context context, @Nullable AttributeSet attrs) {
setScaleType(ScaleType.CENTER_CROP);
if (attrs != null) {
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AvatarImageView, 0, 0);
inverted = typedArray.getBoolean(0, false);
typedArray.recycle();
}
outlinePaint = ThemeUtil.isDarkTheme(getContext()) ? DARK_THEME_OUTLINE_PAINT : LIGHT_THEME_OUTLINE_PAINT;
setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setOval(0, 0, view.getWidth(), view.getHeight());
}
});
setClipToOutline(true);
unknownRecipientDrawable = new ResourceContactPhoto(R.drawable.ic_profile_default).asDrawable(getContext(), ContactColors.UNKNOWN_COLOR.toConversationColor(getContext()), inverted);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float width = getWidth() - getPaddingRight() - getPaddingLeft();
float height = getHeight() - getPaddingBottom() - getPaddingTop();
float cx = width / 2f;
float cy = height / 2f;
float radius = Math.min(cx, cy) - (outlinePaint.getStrokeWidth() / 2f);
canvas.translate(getPaddingLeft(), getPaddingTop());
canvas.drawCircle(cx, cy, radius, outlinePaint);
}
@Override
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
super.setOnClickListener(listener);
}
public void update(String hexEncodedPublicKey) {
Address address = Address.fromSerialized(hexEncodedPublicKey);
Recipient recipient = Recipient.from(getContext(), address, false);
updateAvatar(recipient);
}
private void updateAvatar(Recipient recipient) {
setAvatar(GlideApp.with(getContext()), recipient, false);
}
public void setAvatar(@NonNull GlideRequests requestManager, @Nullable Recipient recipient, boolean quickContactEnabled) {
if (recipient != null) {
if (recipient.isLocalNumber()) {
setImageDrawable(new ResourceContactPhoto(R.drawable.ic_note_to_self).asDrawable(getContext(), recipient.getColor().toAvatarColor(getContext()), inverted));
} else {
RecipientContactPhoto photo = new RecipientContactPhoto(recipient);
if (!photo.equals(recipientContactPhoto)) {
requestManager.clear(this);
recipientContactPhoto = photo;
Drawable photoPlaceholderDrawable = AvatarPlaceholderGenerator.generate(
getContext(), 128, recipient.getAddress().serialize(), recipient.getName());
if (photo.contactPhoto != null) {
requestManager.load(photo.contactPhoto)
.fallback(photoPlaceholderDrawable)
.error(photoPlaceholderDrawable)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.circleCrop()
.into(this);
} else {
requestManager.load(photoPlaceholderDrawable)
.circleCrop()
.into(this);
// setImageDrawable(photoPlaceholderDrawable);
}
}
}
} else {
recipientContactPhoto = null;
requestManager.clear(this);
setImageDrawable(unknownRecipientDrawable);
super.setOnClickListener(listener);
}
}
public void clear(@NonNull GlideRequests glideRequests) {
glideRequests.clear(this);
}
private void setAvatarClickHandler(final Recipient recipient, boolean quickContactEnabled) {
if (!recipient.isGroupRecipient() && quickContactEnabled) {
super.setOnClickListener(v -> {
if (recipient.getContactUri() != null) {
ContactsContract.QuickContact.showQuickContact(getContext(), AvatarImageView.this, recipient.getContactUri(), ContactsContract.QuickContact.MODE_LARGE, null);
} else {
getContext().startActivity(RecipientExporter.export(recipient).asAddContactIntent());
}
});
} else {
super.setOnClickListener(listener);
}
}
private static class RecipientContactPhoto {
private final @NonNull Recipient recipient;
private final @Nullable ContactPhoto contactPhoto;
private final boolean ready;
RecipientContactPhoto(@NonNull Recipient recipient) {
this.recipient = recipient;
this.ready = !recipient.isResolving();
this.contactPhoto = recipient.getContactPhoto();
}
public boolean equals(@Nullable RecipientContactPhoto other) {
if (other == null) return false;
return other.recipient.equals(recipient) &&
other.recipient.getColor().equals(recipient.getColor()) &&
other.ready == ready &&
Objects.equals(other.contactPhoto, contactPhoto);
}
}
}

View File

@ -38,10 +38,13 @@ class ProfilePictureView @JvmOverloads constructor(
var additionalDisplayName: String? = null var additionalDisplayName: String? = null
private val profilePicturesCache = mutableMapOf<View, Recipient>() private val profilePicturesCache = mutableMapOf<View, Recipient>()
private val resourcePadding by lazy {
context.resources.getDimensionPixelSize(R.dimen.normal_padding).toFloat()
}
private val unknownRecipientDrawable by lazy { ResourceContactPhoto(R.drawable.ic_profile_default) private val unknownRecipientDrawable by lazy { ResourceContactPhoto(R.drawable.ic_profile_default)
.asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context), false) } .asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context), false, resourcePadding) }
private val unknownOpenGroupDrawable by lazy { ResourceContactPhoto(R.drawable.ic_notification) private val unknownOpenGroupDrawable by lazy { ResourceContactPhoto(R.drawable.ic_notification)
.asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context), false) } .asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context), false, resourcePadding) }
constructor(context: Context, sender: Recipient): this(context) { constructor(context: Context, sender: Recipient): this(context) {
update(sender) update(sender)

View File

@ -28,7 +28,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
import org.session.libsession.avatars.ContactColors; import org.session.libsession.avatars.ContactColors;
import org.session.libsession.avatars.ContactPhoto; import org.session.libsession.avatars.ContactPhoto;
import org.session.libsession.avatars.GeneratedContactPhoto; import org.session.libsession.avatars.ResourceContactPhoto;
import org.session.libsession.messaging.contacts.Contact; import org.session.libsession.messaging.contacts.Contact;
import org.session.libsession.utilities.NotificationPrivacyPreference; import org.session.libsession.utilities.NotificationPrivacyPreference;
import org.session.libsession.utilities.TextSecurePreferences; import org.session.libsession.utilities.TextSecurePreferences;
@ -60,6 +60,8 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
private CharSequence contentTitle; private CharSequence contentTitle;
private CharSequence contentText; private CharSequence contentText;
private static final Integer ICON_SIZE = 128;
public SingleRecipientNotificationBuilder(@NonNull Context context, @NonNull NotificationPrivacyPreference privacy) public SingleRecipientNotificationBuilder(@NonNull Context context, @NonNull NotificationPrivacyPreference privacy)
{ {
super(context, privacy); super(context, privacy);
@ -108,7 +110,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
} else { } else {
setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal)); setContentTitle(context.getString(R.string.SingleRecipientNotificationBuilder_signal));
setLargeIcon(new GeneratedContactPhoto("Unknown", R.drawable.ic_profile_default).asDrawable(context, ContactColors.UNKNOWN_COLOR.toConversationColor(context))); setLargeIcon(AvatarPlaceholderGenerator.generate(context, ICON_SIZE, "", "Unknown"));
} }
} }
@ -330,7 +332,7 @@ public class SingleRecipientNotificationBuilder extends AbstractNotificationBuil
private static Drawable getPlaceholderDrawable(Context context, Recipient recipient) { private static Drawable getPlaceholderDrawable(Context context, Recipient recipient) {
String publicKey = recipient.getAddress().serialize(); String publicKey = recipient.getAddress().serialize();
String displayName = recipient.getName(); String displayName = recipient.getName();
return AvatarPlaceholderGenerator.generate(context, 128, publicKey, displayName); return AvatarPlaceholderGenerator.generate(context, ICON_SIZE, publicKey, displayName);
} }
/** /**

View File

@ -32,7 +32,6 @@ dependencies {
androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
implementation "com.github.bumptech.glide:glide:$glideVersion" implementation "com.github.bumptech.glide:glide:$glideVersion"
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
implementation 'com.annimon:stream:1.1.8' implementation 'com.annimon:stream:1.1.8'
implementation 'com.makeramen:roundedimageview:2.1.0' implementation 'com.makeramen:roundedimageview:2.1.0'
implementation 'com.esotericsoftware:kryo:5.1.1' implementation 'com.esotericsoftware:kryo:5.1.1'

View File

@ -5,6 +5,6 @@ import android.graphics.drawable.Drawable;
public interface FallbackContactPhoto { public interface FallbackContactPhoto {
public Drawable asDrawable(Context context, int color);
public Drawable asDrawable(Context context, int color, boolean inverted); public Drawable asDrawable(Context context, int color, boolean inverted);
public Drawable asDrawable(Context context, int color, boolean inverted, Float padding);
} }

View File

@ -1,83 +0,0 @@
package org.session.libsession.avatars;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.text.TextUtils;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.amulyakhare.textdrawable.TextDrawable;
import org.session.libsession.R;
import org.session.libsession.utilities.ThemeUtil;
import org.session.libsession.utilities.ViewUtil;
import java.util.regex.Pattern;
public class GeneratedContactPhoto implements FallbackContactPhoto {
private static final Pattern PATTERN = Pattern.compile("[^\\p{L}\\p{Nd}\\p{S}]+");
private static final Typeface TYPEFACE = Typeface.create("sans-serif-medium", Typeface.NORMAL);
private final String name;
private final int fallbackResId;
public GeneratedContactPhoto(@NonNull String name, @DrawableRes int fallbackResId) {
this.name = name;
this.fallbackResId = fallbackResId;
}
@Override
public Drawable asDrawable(Context context, int color) {
return asDrawable(context, color,false);
}
@Override
public Drawable asDrawable(Context context, int color, boolean inverted) {
int targetSize = context.getResources().getDimensionPixelSize(R.dimen.contact_photo_target_size);
String character = getAbbreviation(name);
if (!TextUtils.isEmpty(character)) {
Drawable base = TextDrawable.builder()
.beginConfig()
.width(targetSize)
.height(targetSize)
.useFont(TYPEFACE)
.fontSize(ViewUtil.dpToPx(context, 24))
.textColor(inverted ? color : Color.WHITE)
.endConfig()
.buildRound(character, inverted ? Color.WHITE : color);
Drawable gradient = context.getResources().getDrawable(ThemeUtil.isDarkTheme(context) ? R.drawable.avatar_gradient_dark
: R.drawable.avatar_gradient_light);
return new LayerDrawable(new Drawable[] { base, gradient });
}
return new ResourceContactPhoto(fallbackResId).asDrawable(context, color, inverted);
}
private @Nullable String getAbbreviation(String name) {
String[] parts = name.split(" ");
StringBuilder builder = new StringBuilder();
int count = 0;
for (int i = 0; i < parts.length && count < 2; i++) {
String cleaned = PATTERN.matcher(parts[i]).replaceFirst("");
if (!TextUtils.isEmpty(cleaned)) {
builder.appendCodePoint(cleaned.codePointAt(0));
count++;
}
}
if (builder.length() == 0) {
return null;
} else {
return builder.toString();
}
}
}

View File

@ -4,13 +4,13 @@ import android.content.Context;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.PorterDuff; import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable; import android.graphics.drawable.LayerDrawable;
import android.widget.ImageView; import android.widget.ImageView;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.content.res.AppCompatResources;
import com.amulyakhare.textdrawable.TextDrawable;
import com.makeramen.roundedimageview.RoundedDrawable; import com.makeramen.roundedimageview.RoundedDrawable;
import org.session.libsession.R; import org.session.libsession.R;
@ -25,19 +25,33 @@ public class ResourceContactPhoto implements FallbackContactPhoto {
} }
@Override @Override
public Drawable asDrawable(Context context, int color) { public Drawable asDrawable(Context context, int color, boolean inverted) {
return asDrawable(context, color, false); return asDrawable(context, 0, false, 0f);
} }
@Override @Override
public Drawable asDrawable(Context context, int color, boolean inverted) { public Drawable asDrawable(Context context, int color, boolean inverted, Float padding) {
Drawable background = TextDrawable.builder().buildRound(" ", inverted ? Color.WHITE : color); // rounded colored background
GradientDrawable background = new GradientDrawable();
background.setShape(GradientDrawable.OVAL);
background.setColor(inverted ? Color.WHITE : color);
// resource image in the foreground
RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(AppCompatResources.getDrawable(context, resourceId)); RoundedDrawable foreground = (RoundedDrawable) RoundedDrawable.fromDrawable(AppCompatResources.getDrawable(context, resourceId));
foreground.setScaleType(ImageView.ScaleType.CENTER_CROP); if (foreground != null) {
if(padding == 0f){
foreground.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
// apply padding via a transparent border oterhwise things get misaligned
foreground.setScaleType(ImageView.ScaleType.FIT_CENTER);
foreground.setBorderColor(Color.TRANSPARENT);
foreground.setBorderWidth(padding);
}
if (inverted) { if (inverted) {
foreground.setColorFilter(color, PorterDuff.Mode.SRC_ATOP); foreground.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
}
} }
Drawable gradient = AppCompatResources.getDrawable( Drawable gradient = AppCompatResources.getDrawable(

View File

@ -3,6 +3,8 @@ package org.session.libsession.avatars;
import android.content.Context; import android.content.Context;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import androidx.core.content.ContextCompat;
import com.makeramen.roundedimageview.RoundedDrawable; import com.makeramen.roundedimageview.RoundedDrawable;
public class TransparentContactPhoto implements FallbackContactPhoto { public class TransparentContactPhoto implements FallbackContactPhoto {
@ -10,13 +12,13 @@ public class TransparentContactPhoto implements FallbackContactPhoto {
public TransparentContactPhoto() {} public TransparentContactPhoto() {}
@Override @Override
public Drawable asDrawable(Context context, int color) { public Drawable asDrawable(Context context, int color, boolean inverted) {
return asDrawable(context, color, false); return asDrawable(context, color, inverted, 0f);
} }
@Override @Override
public Drawable asDrawable(Context context, int color, boolean inverted) { public Drawable asDrawable(Context context, int color, boolean inverted, Float padding) {
return RoundedDrawable.fromDrawable(context.getResources().getDrawable(android.R.color.transparent)); return RoundedDrawable.fromDrawable(ContextCompat.getDrawable(context, android.R.color.transparent));
} }
} }