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}