diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 7a16fe6cc..87748420d 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -123,9 +123,6 @@ dependencies { exclude group: 'com.android.support' exclude module: 'disklrucache' // when there's no disk cache, we can't accidentally use it } - implementation('jp.wasabeef:glide-transformations:3.3.0') { - exclude module: 'disklrucache' // this gets pulled in here otherwise - } annotationProcessor 'com.google.dagger:dagger-compiler:2.19' annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java index 3ced99cd9..50fe30e40 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationMessageViewHolder.java @@ -9,21 +9,16 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; -import com.bumptech.glide.load.MultiTransformation; import com.bumptech.glide.load.Transformation; -import com.bumptech.glide.load.resource.bitmap.CenterCrop; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.R; +import org.briarproject.briar.android.conversation.glide.BriarImageTransformation; import org.briarproject.briar.android.conversation.glide.GlideApp; -import jp.wasabeef.glide.transformations.RoundedCornersTransformation; - import static com.bumptech.glide.load.engine.DiskCacheStrategy.NONE; import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade; -import static jp.wasabeef.glide.transformations.RoundedCornersTransformation.CornerType.BOTTOM; -import static jp.wasabeef.glide.transformations.RoundedCornersTransformation.CornerType.TOP_LEFT; -import static jp.wasabeef.glide.transformations.RoundedCornersTransformation.CornerType.TOP_RIGHT; +import static java.util.Objects.requireNonNull; @UiThread @NotNullByDefault @@ -95,7 +90,8 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder { private void bindImageItem(ConversationMessageItem item) { // TODO show more than just the first image - AttachmentItem attachment = item.getAttachments().get(0); + AttachmentItem attachment = + requireNonNull(item.getAttachments()).get(0); ConstraintSet constraintSet; if (item.getText() == null) { @@ -133,24 +129,10 @@ class ConversationMessageViewHolder extends ConversationItemViewHolder { private void loadImage(ConversationMessageItem item, AttachmentItem attachment) { - // these transformations can be optimized by writing our own - Transformation transformation; - if (item.getText() == null) { - transformation = new MultiTransformation<>(new CenterCrop(), - new RoundedCornersTransformation(radiusSmall, 0, - isIncoming() ? TOP_LEFT : TOP_RIGHT), - new RoundedCornersTransformation(radiusBig, 0, - isIncoming() ? TOP_RIGHT : TOP_LEFT), - new RoundedCornersTransformation(radiusBig, 0, BOTTOM) - ); - } else { - transformation = new MultiTransformation<>(new CenterCrop(), - new RoundedCornersTransformation(radiusSmall, 0, - isIncoming() ? TOP_LEFT : TOP_RIGHT), - new RoundedCornersTransformation(radiusBig, 0, - isIncoming() ? TOP_RIGHT : TOP_LEFT) - ); - } + boolean leftCornerSmall = isIncoming(); + boolean bottomRound = item.getText() == null; + Transformation transformation = new BriarImageTransformation( + radiusSmall, radiusBig, leftCornerSmall, bottomRound); GlideApp.with(imageView) .load(attachment) diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarImageTransformation.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarImageTransformation.java new file mode 100644 index 000000000..5488efa40 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/BriarImageTransformation.java @@ -0,0 +1,16 @@ +package org.briarproject.briar.android.conversation.glide; + +import android.graphics.Bitmap; + +import com.bumptech.glide.load.MultiTransformation; +import com.bumptech.glide.load.resource.bitmap.CenterCrop; + +public class BriarImageTransformation extends MultiTransformation { + + public BriarImageTransformation(int smallRadius, int radius, + boolean leftCornerSmall, boolean bottomRound) { + super(new CenterCrop(), new ImageCornerTransformation( + smallRadius, radius, leftCornerSmall, bottomRound)); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/ImageCornerTransformation.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/ImageCornerTransformation.java new file mode 100644 index 000000000..fb418d6d7 --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/glide/ImageCornerTransformation.java @@ -0,0 +1,111 @@ +package org.briarproject.briar.android.conversation.glide; + +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; +import android.support.annotation.NonNull; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import java.security.MessageDigest; + +import javax.annotation.concurrent.Immutable; + +import static android.graphics.Bitmap.Config.ARGB_8888; +import static android.graphics.Shader.TileMode.CLAMP; + +@Immutable +@NotNullByDefault +class ImageCornerTransformation extends BitmapTransformation { + + private static final String ID = ImageCornerTransformation.class.getName(); + + private final int smallRadius, radius; + private final boolean leftCornerSmall, bottomRound; + + ImageCornerTransformation(int smallRadius, int radius, + boolean leftCornerSmall, boolean bottomRound) { + this.smallRadius = smallRadius; + this.radius = radius; + this.leftCornerSmall = leftCornerSmall; + this.bottomRound = bottomRound; + } + + @Override + protected Bitmap transform(BitmapPool pool, Bitmap toTransform, + int outWidth, int outHeight) { + int width = toTransform.getWidth(); + int height = toTransform.getHeight(); + + Bitmap bitmap = pool.get(width, height, ARGB_8888); + bitmap.setHasAlpha(true); + + Canvas canvas = new Canvas(bitmap); + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setShader(new BitmapShader(toTransform, CLAMP, CLAMP)); + drawRect(canvas, paint, width, height); + return bitmap; + } + + private void drawRect(Canvas canvas, Paint paint, float width, + float height) { + drawSmallCorner(canvas, paint, width); + drawBigCorners(canvas, paint, width, height); + } + + private void drawSmallCorner(Canvas canvas, Paint paint, float width) { + float left = leftCornerSmall ? 0 : width - radius; + float right = leftCornerSmall ? radius : width; + canvas.drawRoundRect(new RectF(left, 0, right, radius), + smallRadius, smallRadius, paint); + } + + private void drawBigCorners(Canvas canvas, Paint paint, float width, + float height) { + float top = bottomRound ? 0 : radius; + RectF rect = new RectF(0, top, width, height); + if (bottomRound) { + canvas.drawRoundRect(rect, radius, radius, paint); + } else { + canvas.drawRect(rect, paint); + canvas.drawRoundRect(new RectF(0, 0, width, radius * 2), + radius, radius, paint); + } + } + + @Override + public String toString() { + return "ImageCornerTransformation(smallRadius=" + smallRadius + + ", radius=" + radius + ", leftCornerSmall=" + leftCornerSmall + + ", bottomRound=" + bottomRound + ")"; + } + + @Override + public boolean equals(Object o) { + return o instanceof ImageCornerTransformation && + ((ImageCornerTransformation) o).smallRadius == smallRadius && + ((ImageCornerTransformation) o).radius == radius && + ((ImageCornerTransformation) o).leftCornerSmall == + leftCornerSmall && + ((ImageCornerTransformation) o).bottomRound == bottomRound; + } + + @Override + public int hashCode() { + return ID.hashCode() + smallRadius * 100 + radius * 10 + + (leftCornerSmall ? 9 : 8) + (bottomRound ? 7 : 6); + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + messageDigest.update((ID + smallRadius + radius + leftCornerSmall + + bottomRound).getBytes(CHARSET)); + } + +} diff --git a/briar-android/witness.gradle b/briar-android/witness.gradle index a5213326c..29a9c2f01 100644 --- a/briar-android/witness.gradle +++ b/briar-android/witness.gradle @@ -130,7 +130,6 @@ dependencyVerification { 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2', - 'jp.wasabeef:glide-transformations:3.3.0:glide-transformations-3.3.0.aar:340c482364b84be768e7cb96975aa78b448b9f067913c8a23ae2339e0e908adf', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'nekohtml:nekohtml:1.9.6.2:nekohtml-1.9.6.2.jar:fdff6cfa9ed9cc911c842a5d2395f209ec621ef1239d46810e9e495809d3ae09', 'nekohtml:xercesMinimal:1.9.6.2:xercesMinimal-1.9.6.2.jar:95b8b357d19f63797dd7d67622fd3f18374d64acbc6584faba1c7759a31e8438',