make avatar shape configurable (in code)

This commit is contained in:
Daniel Gultsch 2023-03-08 09:52:01 +01:00
parent 26d856e91f
commit eb15dc1260
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
6 changed files with 50 additions and 14 deletions

View file

@ -10,6 +10,7 @@ import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import im.conversations.android.R;
import im.conversations.android.database.model.AddressWithName; import im.conversations.android.database.model.AddressWithName;
import im.conversations.android.database.model.AvatarWithAccount; import im.conversations.android.database.model.AvatarWithAccount;
import im.conversations.android.ui.graphics.drawable.AvatarDrawable; import im.conversations.android.ui.graphics.drawable.AvatarDrawable;
@ -59,9 +60,18 @@ public class AvatarFetcher {
LOGGER.info("ImageView reference was gone after fetching avatar"); LOGGER.info("ImageView reference was gone after fetching avatar");
return; return;
} }
final var resources = imageView.getResources();
final var roundedBitmapDrawable = final var roundedBitmapDrawable =
RoundedBitmapDrawableFactory.create(imageView.getResources(), result); RoundedBitmapDrawableFactory.create(resources, result);
final boolean circular = resources.getBoolean(R.bool.avatar_chat_overview_circular);
if (circular) {
roundedBitmapDrawable.setCircular(true); roundedBitmapDrawable.setCircular(true);
} else {
final float radius =
resources.getDimension(R.dimen.avatar_chat_overview_radius)
/ resources.getDimension(R.dimen.avatar_chat_overview_size);
roundedBitmapDrawable.setCornerRadius(result.getWidth() * radius);
}
imageView.setImageDrawable(roundedBitmapDrawable); imageView.setImageDrawable(roundedBitmapDrawable);
} }

View file

@ -20,6 +20,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import com.google.android.material.elevation.SurfaceColors; import com.google.android.material.elevation.SurfaceColors;
@ -44,17 +45,22 @@ public class AvatarDrawable extends ColorDrawable {
private final int intrinsicWidth; private final int intrinsicWidth;
private final Context context; private final Context context;
private final boolean circular;
private final int roundedCornerRadius;
public AvatarDrawable(final Context context, final AddressWithName addressWithName) { public AvatarDrawable(final Context context, final AddressWithName addressWithName) {
this.context = context; this.context = context;
final String name = addressWithName.name; final String name = addressWithName.name;
final Jid key = addressWithName.address;
this.paint = getPaint(addressWithName.address); this.paint = getPaint(addressWithName.address);
this.textPaint = getTextPaint(); this.textPaint = getTextPaint();
final Matcher matcher = LETTER_PATTERN.matcher(Strings.nullToEmpty(name)); final Matcher matcher = LETTER_PATTERN.matcher(Strings.nullToEmpty(name));
this.letter = matcher.find() ? matcher.group().toUpperCase(Locale.ROOT) : null; this.letter = matcher.find() ? matcher.group().toUpperCase(Locale.ROOT) : null;
final var resources = context.getResources();
final int avatarDrawableSize = final int avatarDrawableSize =
context.getResources().getDimensionPixelSize(R.dimen.avatar_drawable_size); resources.getDimensionPixelSize(R.dimen.avatar_chat_overview_size);
this.circular = resources.getBoolean(R.bool.avatar_chat_overview_circular);
this.roundedCornerRadius =
resources.getDimensionPixelSize(R.dimen.avatar_chat_overview_radius);
this.intrinsicHeight = avatarDrawableSize; this.intrinsicHeight = avatarDrawableSize;
this.intrinsicWidth = avatarDrawableSize; this.intrinsicWidth = avatarDrawableSize;
} }
@ -78,16 +84,25 @@ public class AvatarDrawable extends ColorDrawable {
@Override @Override
public void draw(final Canvas canvas) { public void draw(final Canvas canvas) {
final float midX = getBounds().width() / 2.0f;
final float midY = getBounds().height() / 2.0f;
final float radius = Math.min(getBounds().width(), getBounds().height()) / 2.0f; final float radius = Math.min(getBounds().width(), getBounds().height()) / 2.0f;
textPaint.setTextSize(radius); this.textPaint.setTextSize(radius);
final Rect r = new Rect(); final Rect r = new Rect();
canvas.getClipBounds(r); canvas.getClipBounds(r);
final int cHeight = r.height(); final int cHeight = r.height();
final int cWidth = r.width(); final int cWidth = r.width();
// TODO if we ever want to do rounded corners we can use drawRoundRect() final var roundedSquareRadius =
context.getResources().getDimensionPixelSize(R.dimen.avatar_chat_overview_radius);
if (this.circular) {
final float midX = getBounds().width() / 2.0f;
final float midY = getBounds().height() / 2.0f;
canvas.drawCircle(midX, midY, radius, paint); canvas.drawCircle(midX, midY, radius, paint);
} else {
canvas.drawRoundRect(
new RectF(0, 0, getBounds().width(), getBounds().height()),
roundedSquareRadius,
roundedSquareRadius,
paint);
}
if (letter == null) { if (letter == null) {
return; return;
} }

View file

@ -3,6 +3,7 @@ package im.conversations.android.xmpp.manager;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import com.google.common.collect.Collections2; import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.hash.Hashing; import com.google.common.hash.Hashing;
@ -102,7 +103,12 @@ public class AvatarManager extends AbstractManager {
return Futures.transform( return Futures.transform(
getAvatar(address, type, id), getAvatar(address, type, id),
bytes -> { bytes -> {
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length); final var sourceBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
final var size = Math.min(sourceBitmap.getHeight(), sourceBitmap.getWidth());
// return a square version of the avatar. we are not displaying the rest and can
// save the bytes plus the corner radius calculation in AvatarFetcher requires a
// square
return ThumbnailUtils.extractThumbnail(sourceBitmap, size, size);
}, },
CPU_EXECUTOR); CPU_EXECUTOR);
} }

View file

@ -10,9 +10,10 @@
<ImageView <ImageView
android:id="@+id/avatar" android:id="@+id/avatar"
android:layout_width="@dimen/avatar_drawable_size" android:layout_width="@dimen/avatar_chat_overview_size"
android:layout_height="@dimen/avatar_drawable_size" android:layout_height="@dimen/avatar_chat_overview_size"
android:layout_margin="16dp" android:layout_marginStart="16dp"
android:scaleType="centerCrop"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />

View file

@ -0,0 +1,5 @@
<resources>
<dimen name="avatar_chat_overview_size">40dp</dimen>
<bool name="avatar_chat_overview_circular">false</bool>
<dimen name="avatar_chat_overview_radius">10dp</dimen>
</resources>

View file

@ -8,5 +8,4 @@
<dimen name="in_call_fab_margin">8dp</dimen> <dimen name="in_call_fab_margin">8dp</dimen>
<dimen name="in_call_fab_margin_center">12dp</dimen> <dimen name="in_call_fab_margin_center">12dp</dimen>
<dimen name="publish_avatar_size">96dp</dimen> <dimen name="publish_avatar_size">96dp</dimen>
<dimen name="avatar_drawable_size">40dp</dimen>
</resources> </resources>