That fuck shit the fascists are using
1package org.tm.archive.util;
2
3import android.content.Context;
4import android.graphics.Bitmap;
5import android.graphics.drawable.Drawable;
6import android.os.Build;
7import android.view.View;
8import android.widget.ImageView;
9
10import androidx.annotation.NonNull;
11import androidx.annotation.Nullable;
12import androidx.annotation.WorkerThread;
13import androidx.appcompat.widget.AppCompatImageView;
14import androidx.core.content.ContextCompat;
15import androidx.core.graphics.drawable.IconCompat;
16
17import com.bumptech.glide.Glide;
18import com.bumptech.glide.RequestBuilder;
19import com.bumptech.glide.RequestManager;
20import com.bumptech.glide.load.engine.DiskCacheStrategy;
21import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
22import com.bumptech.glide.load.resource.bitmap.CenterCrop;
23import com.bumptech.glide.load.resource.bitmap.CircleCrop;
24import com.bumptech.glide.request.target.CustomTarget;
25import com.bumptech.glide.request.target.CustomViewTarget;
26import com.bumptech.glide.request.transition.Transition;
27
28import org.signal.core.util.ThreadUtil;
29import org.signal.core.util.logging.Log;
30import org.tm.archive.R;
31import org.tm.archive.contacts.avatars.ContactPhoto;
32import org.tm.archive.contacts.avatars.GeneratedContactPhoto;
33import org.tm.archive.contacts.avatars.ProfileContactPhoto;
34import org.tm.archive.providers.AvatarProvider;
35import org.tm.archive.recipients.Recipient;
36
37import java.util.Objects;
38import java.util.Optional;
39import java.util.concurrent.CountDownLatch;
40import java.util.concurrent.ExecutionException;
41import java.util.concurrent.TimeUnit;
42import java.util.concurrent.atomic.AtomicReference;
43
44public final class AvatarUtil {
45
46 private static final String TAG = Log.tag(AvatarUtil.class);
47
48 public static final int UNDEFINED_SIZE = -1;
49
50 private AvatarUtil() {
51 }
52
53 public static void loadBlurredIconIntoImageView(@NonNull Recipient recipient, @NonNull AppCompatImageView target) {
54 Context context = target.getContext();
55
56 ContactPhoto photo;
57
58 if (recipient.isSelf()) {
59 photo = new ProfileContactPhoto(Recipient.self());
60 } else if (recipient.getContactPhoto() == null) {
61 target.setImageDrawable(null);
62 target.setBackgroundColor(ContextCompat.getColor(target.getContext(), R.color.black));
63 return;
64 } else {
65 photo = recipient.getContactPhoto();
66 }
67
68 Glide.with(target)
69 .load(photo)
70 .transform(new BlurTransformation(context, 0.25f, BlurTransformation.MAX_RADIUS), new CenterCrop())
71 .into(new CustomViewTarget<View, Drawable>(target) {
72 @Override
73 public void onLoadFailed(@Nullable Drawable errorDrawable) {
74 target.setImageDrawable(null);
75 target.setBackgroundColor(ContextCompat.getColor(target.getContext(), R.color.black));
76 }
77
78 @Override
79 public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
80 target.setImageDrawable(resource);
81 }
82
83 @Override
84 protected void onResourceCleared(@Nullable Drawable placeholder) {
85 target.setImageDrawable(placeholder);
86 }
87 });
88 }
89
90 public static void loadIconIntoImageView(@NonNull Recipient recipient, @NonNull ImageView target) {
91 loadIconIntoImageView(recipient, target, -1);
92 }
93
94 public static void loadIconIntoImageView(@NonNull Recipient recipient, @NonNull ImageView target, int requestedSize) {
95 Context context = target.getContext();
96
97 requestCircle(Glide.with(context).asDrawable(), context, recipient, requestedSize).into(target);
98 }
99
100 public static Bitmap loadIconBitmapSquareNoCache(@NonNull Context context,
101 @NonNull Recipient recipient,
102 int width,
103 int height)
104 throws ExecutionException, InterruptedException
105 {
106 return requestSquare(Glide.with(context).asBitmap(), context, recipient)
107 .skipMemoryCache(true)
108 .diskCacheStrategy(DiskCacheStrategy.NONE)
109 .submit(width, height)
110 .get();
111 }
112
113 @WorkerThread
114 public static @NonNull IconCompat getIconCompat(@NonNull Context context, @NonNull Recipient recipient) {
115 if (Build.VERSION.SDK_INT > 29) {
116 return IconCompat.createWithContentUri(AvatarProvider.getContentUri(recipient.getId()));
117 } else {
118 return IconCompat.createWithBitmap(getBitmapForNotification(context, recipient, DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE));
119 }
120 }
121
122 @WorkerThread
123 public static Bitmap getBitmapForNotification(@NonNull Context context, @NonNull Recipient recipient) {
124 return getBitmapForNotification(context, recipient, UNDEFINED_SIZE);
125 }
126
127 @WorkerThread
128 public static @NonNull Bitmap getBitmapForNotification(@NonNull Context context, @NonNull Recipient recipient, int size) {
129 ThreadUtil.assertNotMainThread();
130
131 try {
132 AvatarTarget avatarTarget = new AvatarTarget(size);
133 RequestManager requestManager = Glide.with(context);
134
135 requestCircle(requestManager.asBitmap(), context, recipient, size).into(avatarTarget);
136
137 Bitmap bitmap = avatarTarget.await();
138 return Objects.requireNonNullElseGet(bitmap, () -> DrawableUtil.toBitmap(getFallback(context, recipient, size), size, size));
139 } catch (InterruptedException e) {
140 return DrawableUtil.toBitmap(getFallback(context, recipient, size), size, size);
141 }
142 }
143
144 private static <T> RequestBuilder<T> requestCircle(@NonNull RequestBuilder<T> requestBuilder, @NonNull Context context, @NonNull Recipient recipient, int targetSize) {
145 return request(requestBuilder, context, recipient, targetSize, new CircleCrop());
146 }
147
148 private static <T> RequestBuilder<T> requestSquare(@NonNull RequestBuilder<T> requestBuilder, @NonNull Context context, @NonNull Recipient recipient) {
149 return request(requestBuilder, context, recipient, UNDEFINED_SIZE, new CenterCrop());
150 }
151
152 private static <T> RequestBuilder<T> request(@NonNull RequestBuilder<T> requestBuilder, @NonNull Context context, @NonNull Recipient recipient, int targetSize, @Nullable BitmapTransformation transformation) {
153 return request(requestBuilder, context, recipient, true, targetSize, transformation);
154 }
155
156 private static <T> RequestBuilder<T> request(@NonNull RequestBuilder<T> requestBuilder, @NonNull Context context, @NonNull Recipient recipient, boolean loadSelf, int targetSize, @Nullable BitmapTransformation transformation) {
157
158 final ContactPhoto photo;
159 if (Recipient.self().equals(recipient) && loadSelf) {
160 photo = new ProfileContactPhoto(recipient);
161 } else {
162 photo = recipient.getContactPhoto();
163 }
164
165 final int size = targetSize == -1 ? DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE : targetSize;
166 final RequestBuilder<T> request = requestBuilder.load(photo)
167 .error(getFallback(context, recipient, size))
168 .diskCacheStrategy(DiskCacheStrategy.ALL)
169 .override(size);
170
171 if (recipient.shouldBlurAvatar()) {
172 BlurTransformation blur = new BlurTransformation(context, 0.25f, BlurTransformation.MAX_RADIUS);
173 if (transformation != null) {
174 return request.transform(blur, transformation);
175 } else {
176 return request.transform(blur);
177 }
178 } else if (transformation != null) {
179 return request.transform(transformation);
180 } else {
181 return request;
182 }
183 }
184
185 private static Drawable getFallback(@NonNull Context context, @NonNull Recipient recipient, int targetSize) {
186 String name = Optional.of(recipient.getDisplayName(context)).orElse("");
187
188 return new GeneratedContactPhoto(name, R.drawable.ic_profile_outline_40, targetSize).asDrawable(context, recipient.getAvatarColor());
189 }
190
191 /**
192 * Allows caller to synchronously await for a bitmap of an avatar.
193 */
194 private static class AvatarTarget extends CustomTarget<Bitmap> {
195
196 private final CountDownLatch countDownLatch = new CountDownLatch(1);
197 private final AtomicReference<Bitmap> bitmap = new AtomicReference<>();
198
199 private final int size;
200
201 private AvatarTarget(int size) {
202 this.size = size == UNDEFINED_SIZE ? DrawableUtil.SHORTCUT_INFO_WRAPPED_SIZE : size;
203 }
204
205 public @Nullable Bitmap await() throws InterruptedException {
206 if (countDownLatch.await(1, TimeUnit.SECONDS)) {
207 return bitmap.get();
208 } else {
209 Log.w(TAG, "AvatarTarget#await: Failed to load avatar in time! Returning null");
210 return null;
211 }
212 }
213
214 @Override
215 public void onDestroy() {
216 Log.d(TAG, "AvatarTarget: onDestroy");
217 super.onDestroy();
218 }
219
220 @Override
221 public void onLoadStarted(@Nullable Drawable placeholder) {
222 Log.d(TAG, "AvatarTarget: onLoadStarted");
223 super.onLoadStarted(placeholder);
224 }
225
226 @Override
227 public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
228 Log.d(TAG, "AvatarTarget: onResourceReady");
229 bitmap.set(resource);
230 countDownLatch.countDown();
231 }
232
233 @Override
234 public void onLoadFailed(@Nullable Drawable errorDrawable) {
235 Log.d(TAG, "AvatarTarget: onLoadFailed");
236 if (errorDrawable == null) {
237 throw new AssertionError("Expected an error drawable.");
238 }
239
240 Bitmap errorBitmap = DrawableUtil.toBitmap(errorDrawable, size, size);
241 bitmap.set(errorBitmap);
242 countDownLatch.countDown();
243 }
244
245 @Override
246 public void onLoadCleared(@Nullable Drawable placeholder) {
247 Log.d(TAG, "AvatarTarget: onLoadCleared");
248 bitmap.set(null);
249 countDownLatch.countDown();
250 }
251 }
252}