1package eu.siacs.conversations.ui.util;
  2
  3import android.content.Context;
  4import android.content.res.Resources;
  5import android.graphics.Bitmap;
  6import android.graphics.drawable.AnimatedImageDrawable;
  7import android.graphics.drawable.BitmapDrawable;
  8import android.graphics.drawable.Drawable;
  9import android.os.AsyncTask;
 10import android.os.Build;
 11import android.widget.ImageView;
 12import androidx.annotation.DimenRes;
 13import eu.siacs.conversations.R;
 14import eu.siacs.conversations.entities.Account;
 15import eu.siacs.conversations.services.AvatarService;
 16import eu.siacs.conversations.ui.XmppActivity;
 17import java.lang.ref.WeakReference;
 18import java.util.concurrent.RejectedExecutionException;
 19
 20public class AvatarWorkerTask extends AsyncTask<AvatarService.Avatarable, Void, Drawable> {
 21    private final WeakReference<ImageView> imageViewReference;
 22    private final WeakReference<XmppActivity> activityReference;
 23    private AvatarService.Avatarable avatarable = null;
 24    private @DimenRes final int size;
 25
 26    public AvatarWorkerTask(ImageView imageView, @DimenRes int size) {
 27        imageViewReference = new WeakReference<>(imageView);
 28        activityReference = null;
 29        this.size = size;
 30    }
 31
 32
 33    public AvatarWorkerTask(XmppActivity activity, @DimenRes int size) {
 34        activityReference = new WeakReference<>(activity);
 35        imageViewReference = new WeakReference<>(null);
 36        this.size = size;
 37    }
 38
 39    @Override
 40    protected Drawable doInBackground(AvatarService.Avatarable... params) {
 41        this.avatarable = params[0];
 42        final XmppActivity activity = activityReference == null ? XmppActivity.find(imageViewReference) : activityReference.get();
 43        if (activity == null) {
 44            return null;
 45        }
 46        return activity.avatarService()
 47                .get(avatarable, (int) activity.getResources().getDimension(size), isCancelled());
 48    }
 49
 50    @Override
 51    protected void onPostExecute(Drawable bitmap) {
 52        if (bitmap != null && !isCancelled()) {
 53            final ImageView imageView = imageViewReference.get();
 54            if (imageView != null) {
 55                imageView.setImageDrawable(bitmap);
 56                imageView.setBackgroundColor(0x00000000);
 57            }
 58            if (Build.VERSION.SDK_INT >= 28 && bitmap instanceof AnimatedImageDrawable) {
 59                ((AnimatedImageDrawable) bitmap).start();
 60            }
 61            if (activityReference != null) {
 62                final var activity = activityReference.get();
 63                if (activity != null) {
 64                    activity.refreshUi();
 65                }
 66            }
 67        }
 68    }
 69
 70    public static boolean cancelPotentialWork(
 71            AvatarService.Avatarable avatarable, ImageView imageView) {
 72        final AvatarWorkerTask workerTask = getBitmapWorkerTask(imageView);
 73
 74        if (workerTask != null) {
 75            final AvatarService.Avatarable old = workerTask.avatarable;
 76            if (old == null || avatarable != old) {
 77                workerTask.cancel(true);
 78            } else {
 79                return false;
 80            }
 81        }
 82        return true;
 83    }
 84
 85    public static AvatarWorkerTask getBitmapWorkerTask(ImageView imageView) {
 86        if (imageView != null) {
 87            final Drawable drawable = imageView.getDrawable();
 88            if (drawable instanceof AsyncDrawable asyncDrawable) {
 89                return asyncDrawable.getAvatarWorkerTask();
 90            }
 91        }
 92        return null;
 93    }
 94
 95    public static void loadAvatar(
 96            final AvatarService.Avatarable avatarable,
 97            final ImageView imageView,
 98            final @DimenRes int size) {
 99        if (cancelPotentialWork(avatarable, imageView)) {
100            final XmppActivity activity = XmppActivity.find(imageView);
101            if (activity == null) {
102                return;
103            }
104            final Drawable bm = activity.avatarService().get(avatarable, (int) activity.getResources().getDimension(size), true);
105            setContentDescription(avatarable, imageView);
106            if (bm != null) {
107                cancelPotentialWork(avatarable, imageView);
108                imageView.setImageDrawable(bm);
109                imageView.setBackgroundColor(0x00000000);
110                if (Build.VERSION.SDK_INT >= 28 && bm instanceof AnimatedImageDrawable) {
111                    ((AnimatedImageDrawable) bm).start();
112                }
113            } else {
114                imageView.setBackgroundColor(avatarable.getAvatarBackgroundColor());
115                imageView.setImageDrawable(null);
116                final AvatarWorkerTask task = new AvatarWorkerTask(imageView, size);
117                final AsyncDrawable asyncDrawable =
118                        new AsyncDrawable(activity.getResources(), null, task);
119                imageView.setImageDrawable(asyncDrawable);
120                try {
121                    task.execute(avatarable);
122                } catch (final RejectedExecutionException ignored) {
123                }
124            }
125        }
126    }
127
128    private static void setContentDescription(
129            final AvatarService.Avatarable avatarable, final ImageView imageView) {
130        final Context context = imageView.getContext();
131        if (avatarable instanceof Account) {
132            imageView.setContentDescription(context.getString(R.string.your_avatar));
133        } else {
134            imageView.setContentDescription(
135                    context.getString(R.string.avatar_for_x, avatarable.getAvatarName()));
136        }
137    }
138
139    static class AsyncDrawable extends BitmapDrawable {
140        private final WeakReference<AvatarWorkerTask> avatarWorkerTaskReference;
141
142        AsyncDrawable(Resources res, Bitmap bitmap, AvatarWorkerTask workerTask) {
143            super(res, bitmap);
144            avatarWorkerTaskReference = new WeakReference<>(workerTask);
145        }
146
147        AvatarWorkerTask getAvatarWorkerTask() {
148            return avatarWorkerTaskReference.get();
149        }
150    }
151}