mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-11 17:23:38 +00:00
Fix for media thumbnails flickering on model updates.
Only update ImageView contents when they have changed. Fixes #1004 Fixes #2663 Closes #3184 // FREEBIE
This commit is contained in:
parent
636b11abea
commit
082985276f
@ -200,7 +200,7 @@ public class ConversationItem extends LinearLayout {
|
||||
}
|
||||
|
||||
bubbleContainer.setState(transportationState, mediaCaptionState);
|
||||
}
|
||||
}
|
||||
|
||||
private void setSelectionBackgroundDrawables(MessageRecord messageRecord) {
|
||||
int[] attributes = new int[]{R.attr.conversation_list_item_background_selected,
|
||||
@ -354,7 +354,9 @@ public class ConversationItem extends LinearLayout {
|
||||
|
||||
private void resolveMedia(MediaMmsMessageRecord messageRecord) {
|
||||
if (hasMedia(messageRecord)) {
|
||||
mediaThumbnail.setImageResource(messageRecord.getSlideDeckFuture(), masterSecret);
|
||||
mediaThumbnail.setImageResource(masterSecret, messageRecord.getId(),
|
||||
messageRecord.getDateReceived(),
|
||||
messageRecord.getSlideDeckFuture());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,10 +131,8 @@ public abstract class BubbleContainer extends RelativeLayout {
|
||||
}
|
||||
|
||||
private void setMediaVisibility(@MediaState int mediaState) {
|
||||
media.reset();
|
||||
if (!isMediaPresent(mediaState)) {
|
||||
media.hide();
|
||||
}
|
||||
if (!isMediaPresent(mediaState)) media.setVisibility(View.GONE);
|
||||
else media.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void setMediaPendingMask(@TransportState int transportState) {
|
||||
|
@ -122,15 +122,6 @@ public class ForegroundImageView extends RoundedImageView {
|
||||
return ActivityOptions.makeScaleUpAnimation(this, 0, 0, getWidth(), getHeight());
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
setImageDrawable(null);
|
||||
setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean verifyDrawable(Drawable who) {
|
||||
return super.verifyDrawable(who) || (who == mForeground);
|
||||
|
@ -4,6 +4,7 @@ import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
@ -27,13 +28,17 @@ import org.thoughtcrime.securesms.mms.SlideDeck;
|
||||
import org.thoughtcrime.securesms.mms.ThumbnailTransform;
|
||||
import org.thoughtcrime.securesms.util.FutureTaskListener;
|
||||
import org.thoughtcrime.securesms.util.ListenableFutureTask;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
public class ThumbnailView extends ForegroundImageView {
|
||||
|
||||
private ListenableFutureTask<SlideDeck> slideDeckFuture = null;
|
||||
private SlideDeckListener slideDeckListener = null;
|
||||
private ThumbnailClickListener thumbnailClickListener = null;
|
||||
private String slideId = null;
|
||||
private Slide slide = null;
|
||||
private Handler handler = new Handler();
|
||||
|
||||
public ThumbnailView(Context context) {
|
||||
@ -53,31 +58,41 @@ public class ThumbnailView extends ForegroundImageView {
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
public void setImageResource(@NonNull ListenableFutureTask<SlideDeck> slideDeckFuture,
|
||||
@Nullable MasterSecret masterSecret)
|
||||
public void setImageResource(@Nullable MasterSecret masterSecret,
|
||||
long id, long timestamp,
|
||||
@NonNull ListenableFutureTask<SlideDeck> slideDeckFuture)
|
||||
{
|
||||
if (this.slideDeckFuture != null && this.slideDeckListener != null) {
|
||||
this.slideDeckFuture.removeListener(this.slideDeckListener);
|
||||
}
|
||||
|
||||
String slideId = id + "::" + timestamp;
|
||||
|
||||
if (!slideId.equals(this.slideId)) {
|
||||
setImageDrawable(null);
|
||||
this.slide = null;
|
||||
this.slideId = slideId;
|
||||
}
|
||||
|
||||
this.slideDeckListener = new SlideDeckListener(masterSecret);
|
||||
this.slideDeckFuture = slideDeckFuture;
|
||||
this.slideDeckFuture.addListener(this.slideDeckListener);
|
||||
}
|
||||
|
||||
public void setImageResource(@NonNull Slide slide) {
|
||||
setImageResource(slide, null);
|
||||
}
|
||||
|
||||
public void setImageResource(@NonNull Slide slide, @Nullable MasterSecret masterSecret) {
|
||||
if (isContextValid()) {
|
||||
buildGlideRequest(slide, masterSecret).into(ThumbnailView.this);
|
||||
if (!Util.equals(slide, this.slide)) buildGlideRequest(slide, masterSecret).into(this);
|
||||
this.slide = slide;
|
||||
setOnClickListener(new ThumbnailClickDispatcher(thumbnailClickListener, slide));
|
||||
} else {
|
||||
Log.w(TAG, "Not going to load resource, context is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
public void setImageResource(@NonNull Slide slide) {
|
||||
setImageResource(slide, null);
|
||||
}
|
||||
|
||||
public void setThumbnailClickListener(ThumbnailClickListener listener) {
|
||||
this.thumbnailClickListener = listener;
|
||||
}
|
||||
@ -131,7 +146,7 @@ public class ThumbnailView extends ForegroundImageView {
|
||||
}
|
||||
|
||||
return Glide.with(getContext()).load(new DecryptableUri(masterSecret, slide.getThumbnailUri()))
|
||||
.crossFade().transform(new ThumbnailTransform(getContext()));
|
||||
.transform(new ThumbnailTransform(getContext()));
|
||||
}
|
||||
|
||||
private GenericRequestBuilder buildPlaceholderGlideRequest(Slide slide) {
|
||||
@ -163,7 +178,8 @@ public class ThumbnailView extends ForegroundImageView {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hide();
|
||||
Log.w(TAG, "Resolved slide was null!");
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -175,7 +191,8 @@ public class ThumbnailView extends ForegroundImageView {
|
||||
handler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
hide();
|
||||
Log.w(TAG, "onFailure!");
|
||||
setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -42,12 +42,12 @@ public class AudioSlide extends Slide {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasImage() {
|
||||
public boolean hasImage() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAudio() {
|
||||
public boolean hasAudio() {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ import android.content.Context;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.net.Uri;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
@ -36,28 +37,16 @@ public abstract class Slide {
|
||||
protected final Context context;
|
||||
protected MasterSecret masterSecret;
|
||||
|
||||
public Slide(Context context, PduPart part) {
|
||||
public Slide(Context context, @NonNull PduPart part) {
|
||||
this.part = part;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public Slide(Context context, MasterSecret masterSecret, PduPart part) {
|
||||
public Slide(Context context, @NonNull MasterSecret masterSecret, @NonNull PduPart part) {
|
||||
this(context, part);
|
||||
this.masterSecret = masterSecret;
|
||||
}
|
||||
|
||||
protected byte[] getPartData() {
|
||||
try {
|
||||
if (part.getData() != null)
|
||||
return part.getData();
|
||||
|
||||
return Util.readFully(PartAuthority.getPartStream(context, masterSecret, part.getDataUri()));
|
||||
} catch (IOException e) {
|
||||
Log.w("Slide", e);
|
||||
return new byte[0];
|
||||
}
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return new String(part.getContentType());
|
||||
}
|
||||
@ -107,4 +96,28 @@ public abstract class Slide {
|
||||
if (size > MmsMediaConstraints.MAX_MESSAGE_SIZE) throw new MediaTooLargeException("Media exceeds maximum message size.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof Slide)) return false;
|
||||
|
||||
Slide that = (Slide)other;
|
||||
|
||||
return Util.equals(this.getContentType(), that.getContentType()) &&
|
||||
this.hasAudio() == that.hasAudio() &&
|
||||
this.hasImage() == that.hasImage() &&
|
||||
this.hasVideo() == that.hasVideo() &&
|
||||
this.isDraft() == that.isDraft() &&
|
||||
Util.equals(this.getUri(), that.getUri()) &&
|
||||
Util.equals(this.getThumbnailUri(), that.getThumbnailUri());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Util.hashCode(getContentType(), hasAudio(), hasImage(),
|
||||
hasVideo(), isDraft(), getUri(), getThumbnailUri());
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
@ -17,16 +17,9 @@
|
||||
package org.thoughtcrime.securesms.mms;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
|
||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||
import org.thoughtcrime.securesms.util.LRUCache;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import ws.com.google.android.mms.ContentType;
|
||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
||||
@ -34,14 +27,6 @@ import ws.com.google.android.mms.pdu.PduPart;
|
||||
|
||||
public class TextSlide extends Slide {
|
||||
|
||||
private static final int MAX_CACHE_SIZE = 10;
|
||||
private static final Map<Uri, SoftReference<String>> textCache =
|
||||
Collections.synchronizedMap(new LRUCache<Uri, SoftReference<String>>(MAX_CACHE_SIZE));
|
||||
|
||||
public TextSlide(Context context, MasterSecret masterSecret, PduPart part) {
|
||||
super(context, masterSecret, part);
|
||||
}
|
||||
|
||||
public TextSlide(Context context, String message) {
|
||||
super(context, getPartForMessage(message));
|
||||
}
|
||||
|
@ -21,11 +21,13 @@ import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Typeface;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Build.VERSION;
|
||||
import android.os.Build.VERSION_CODES;
|
||||
import android.os.Looper;
|
||||
import android.provider.Telephony;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableString;
|
||||
@ -45,6 +47,7 @@ import java.io.OutputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
@ -303,4 +306,13 @@ public class Util {
|
||||
throw new AssertionError("Main-thread assertion failed.");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean equals(@Nullable Object a, @Nullable Object b) {
|
||||
return a == b || (a != null && a.equals(b));
|
||||
}
|
||||
|
||||
public static int hashCode(@Nullable Object... objects) {
|
||||
return Arrays.hashCode(objects);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user