mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Re-download profile avatars if they fail to load.
This commit is contained in:
@@ -15,12 +15,16 @@ import androidx.annotation.Px;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.MultiTransformation;
|
||||
import com.bumptech.glide.load.Transformation;
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.load.resource.bitmap.CircleCrop;
|
||||
import com.bumptech.glide.load.resource.bitmap.DownsampleStrategy;
|
||||
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 org.signal.core.util.logging.Log;
|
||||
@@ -32,6 +36,7 @@ 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.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobs.RetrieveProfileAvatarJob;
|
||||
import org.thoughtcrime.securesms.mms.GlideApp;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequest;
|
||||
import org.thoughtcrime.securesms.mms.GlideRequests;
|
||||
@@ -54,6 +59,8 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String TAG = Log.tag(AvatarImageView.class);
|
||||
|
||||
private final RequestListener<Drawable> redownloadRequestListener = new RedownloadRequestListener();
|
||||
|
||||
private int size;
|
||||
private boolean inverted;
|
||||
private OnClickListener listener;
|
||||
@@ -198,7 +205,8 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
.error(fallbackContactPhotoDrawable)
|
||||
.diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.downsample(DownsampleStrategy.CENTER_INSIDE)
|
||||
.transform(new MultiTransformation<>(transforms));
|
||||
.transform(new MultiTransformation<>(transforms))
|
||||
.addListener(redownloadRequestListener);
|
||||
|
||||
if (avatarOptions.fixedSize > 0) {
|
||||
fixedSizeTarget = new FixedSizeTarget(avatarOptions.fixedSize);
|
||||
@@ -363,4 +371,19 @@ public final class AvatarImageView extends AppCompatImageView {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RedownloadRequestListener implements RequestListener<Drawable> {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
|
||||
if (model instanceof ProfileContactPhoto) {
|
||||
RetrieveProfileAvatarJob.enqueueForceUpdate(((ProfileContactPhoto) model).getRecipient());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,4 +66,8 @@ public class ProfileContactPhoto implements ContactPhoto {
|
||||
public int hashCode() {
|
||||
return Objects.hash(recipient, avatarObject, profileAvatarFileDetails);
|
||||
}
|
||||
|
||||
public @NonNull Recipient getRecipient() {
|
||||
return recipient;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,11 +6,13 @@ import android.text.TextUtils;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.core.util.logging.Log;
|
||||
import org.signal.libsignal.zkgroup.profiles.ProfileKey;
|
||||
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil;
|
||||
import org.thoughtcrime.securesms.database.RecipientTable;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.database.model.ProfileAvatarFileDetails;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.jobmanager.JsonJobData;
|
||||
import org.thoughtcrime.securesms.jobmanager.Job;
|
||||
@@ -34,35 +36,47 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
||||
|
||||
private static final String TAG = Log.tag(RetrieveProfileAvatarJob.class);
|
||||
|
||||
private static final int MAX_PROFILE_SIZE_BYTES = 20 * 1024 * 1024;
|
||||
private static final long MIN_TIME_BETWEEN_FORCE_RETRY = TimeUnit.DAYS.toMillis(1);
|
||||
|
||||
private static final String KEY_PROFILE_AVATAR = "profile_avatar";
|
||||
private static final String KEY_RECIPIENT = "recipient";
|
||||
private static final String KEY_FORCE_UPDATE = "force";
|
||||
|
||||
private final String profileAvatar;
|
||||
private final Recipient recipient;
|
||||
private final boolean forceUpdate;
|
||||
|
||||
public RetrieveProfileAvatarJob(Recipient recipient, String profileAvatar) {
|
||||
this(new Job.Parameters.Builder()
|
||||
.setQueue("RetrieveProfileAvatarJob::" + recipient.getId().toQueueKey())
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.HOURS.toMillis(1))
|
||||
.build(),
|
||||
recipient,
|
||||
profileAvatar);
|
||||
public static void enqueueForceUpdate(Recipient recipient) {
|
||||
SignalExecutors.BOUNDED.execute(() -> ApplicationDependencies.getJobManager().add(new RetrieveProfileAvatarJob(recipient, recipient.resolve().getProfileAvatar(), true)));
|
||||
}
|
||||
|
||||
private RetrieveProfileAvatarJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient, String profileAvatar) {
|
||||
public RetrieveProfileAvatarJob(Recipient recipient, String profileAvatar) {
|
||||
this(recipient, profileAvatar, false);
|
||||
}
|
||||
|
||||
public RetrieveProfileAvatarJob(Recipient recipient, String profileAvatar, boolean forceUpdate) {
|
||||
this(new Job.Parameters.Builder().setQueue("RetrieveProfileAvatarJob::" + recipient.getId().toQueueKey())
|
||||
.addConstraint(NetworkConstraint.KEY)
|
||||
.setLifespan(TimeUnit.HOURS.toMillis(1))
|
||||
.build(),
|
||||
recipient,
|
||||
profileAvatar,
|
||||
forceUpdate);
|
||||
}
|
||||
|
||||
private RetrieveProfileAvatarJob(@NonNull Job.Parameters parameters, @NonNull Recipient recipient, String profileAvatar, boolean forceUpdate) {
|
||||
super(parameters);
|
||||
|
||||
this.recipient = recipient;
|
||||
this.profileAvatar = profileAvatar;
|
||||
this.forceUpdate = forceUpdate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable byte[] serialize() {
|
||||
return new JsonJobData.Builder().putString(KEY_PROFILE_AVATAR, profileAvatar)
|
||||
.putString(KEY_RECIPIENT, recipient.getId().serialize())
|
||||
.putBoolean(KEY_FORCE_UPDATE, forceUpdate)
|
||||
.serialize();
|
||||
}
|
||||
|
||||
@@ -81,7 +95,15 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
||||
return;
|
||||
}
|
||||
|
||||
if (profileAvatar != null && profileAvatar.equals(recipient.resolve().getProfileAvatar())) {
|
||||
if (forceUpdate) {
|
||||
ProfileAvatarFileDetails details = recipient.getProfileAvatarFileDetails();
|
||||
if (!details.hasFile() || (details.getLastModified() > System.currentTimeMillis() || details.getLastModified() + MIN_TIME_BETWEEN_FORCE_RETRY < System.currentTimeMillis())) {
|
||||
Log.i(TAG, "Forcing re-download of avatar.");
|
||||
} else {
|
||||
Log.i(TAG, "Too early to force re-download avatar.");
|
||||
return;
|
||||
}
|
||||
} else if (profileAvatar != null && profileAvatar.equals(recipient.resolve().getProfileAvatar())) {
|
||||
Log.w(TAG, "Already retrieved profile avatar: " + profileAvatar);
|
||||
return;
|
||||
}
|
||||
@@ -142,7 +164,8 @@ public class RetrieveProfileAvatarJob extends BaseJob {
|
||||
|
||||
return new RetrieveProfileAvatarJob(parameters,
|
||||
Recipient.resolved(RecipientId.from(data.getString(KEY_RECIPIENT))),
|
||||
data.getString(KEY_PROFILE_AVATAR));
|
||||
data.getString(KEY_PROFILE_AVATAR),
|
||||
data.getBooleanOrDefault(KEY_FORCE_UPDATE, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user