1package eu.siacs.conversations.ui.adapter;
2
3import android.content.res.Resources;
4import android.graphics.Bitmap;
5import android.graphics.drawable.BitmapDrawable;
6import android.graphics.drawable.Drawable;
7import android.os.AsyncTask;
8import android.text.format.DateUtils;
9import android.util.DisplayMetrics;
10import android.view.LayoutInflater;
11import android.view.View;
12import android.view.ViewGroup;
13import android.widget.ImageView;
14import androidx.annotation.NonNull;
15import androidx.databinding.DataBindingUtil;
16import androidx.recyclerview.widget.RecyclerView;
17import eu.siacs.conversations.R;
18import eu.siacs.conversations.databinding.ItemAccountBinding;
19import eu.siacs.conversations.services.AvatarService;
20import eu.siacs.conversations.utils.BackupFile;
21import eu.siacs.conversations.utils.BackupFileHeader;
22import eu.siacs.conversations.utils.UIHelper;
23import eu.siacs.conversations.xmpp.Jid;
24import java.lang.ref.WeakReference;
25import java.util.ArrayList;
26import java.util.List;
27import java.util.concurrent.RejectedExecutionException;
28
29public class BackupFileAdapter
30 extends RecyclerView.Adapter<BackupFileAdapter.BackupFileViewHolder> {
31
32 private OnItemClickedListener listener;
33
34 private final List<BackupFile> files = new ArrayList<>();
35
36 @NonNull
37 @Override
38 public BackupFileViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
39 return new BackupFileViewHolder(
40 DataBindingUtil.inflate(
41 LayoutInflater.from(viewGroup.getContext()),
42 R.layout.item_account,
43 viewGroup,
44 false));
45 }
46
47 @Override
48 public void onBindViewHolder(@NonNull BackupFileViewHolder backupFileViewHolder, int position) {
49 final BackupFile backupFile = files.get(position);
50 final BackupFileHeader header = backupFile.getHeader();
51 backupFileViewHolder.binding.accountJid.setText(header.getJid().asBareJid().toString());
52 backupFileViewHolder.binding.accountStatus.setText(
53 String.format(
54 "%s · %s",
55 header.getApp(),
56 DateUtils.formatDateTime(
57 backupFileViewHolder.binding.getRoot().getContext(),
58 header.getTimestamp(),
59 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR)));
60 backupFileViewHolder.binding.tglAccountStatus.setVisibility(View.GONE);
61 backupFileViewHolder
62 .binding
63 .getRoot()
64 .setOnClickListener(
65 v -> {
66 if (listener != null) {
67 listener.onClick(backupFile);
68 }
69 });
70 loadAvatar(header.getJid(), backupFileViewHolder.binding.accountImage);
71 }
72
73 @Override
74 public int getItemCount() {
75 return files.size();
76 }
77
78 public void setFiles(List<BackupFile> files) {
79 this.files.clear();
80 this.files.addAll(files);
81 notifyDataSetChanged();
82 }
83
84 public void setOnItemClickedListener(OnItemClickedListener listener) {
85 this.listener = listener;
86 }
87
88 static class BackupFileViewHolder extends RecyclerView.ViewHolder {
89 private final ItemAccountBinding binding;
90
91 BackupFileViewHolder(ItemAccountBinding binding) {
92 super(binding.getRoot());
93 this.binding = binding;
94 }
95 }
96
97 public interface OnItemClickedListener {
98 void onClick(BackupFile backupFile);
99 }
100
101 static class BitmapWorkerTask extends AsyncTask<Jid, Void, Bitmap> {
102 private final WeakReference<ImageView> imageViewReference;
103 private Jid jid = null;
104 private final int size;
105
106 BitmapWorkerTask(final ImageView imageView) {
107 imageViewReference = new WeakReference<>(imageView);
108 DisplayMetrics metrics = imageView.getContext().getResources().getDisplayMetrics();
109 this.size = ((int) (48 * metrics.density));
110 }
111
112 @Override
113 protected Bitmap doInBackground(Jid... params) {
114 this.jid = params[0];
115 return AvatarService.get(this.jid, size);
116 }
117
118 @Override
119 protected void onPostExecute(Bitmap bitmap) {
120 if (bitmap != null && !isCancelled()) {
121 final ImageView imageView = imageViewReference.get();
122 if (imageView != null) {
123 imageView.setImageBitmap(bitmap);
124 imageView.setBackgroundColor(0x00000000);
125 }
126 }
127 }
128 }
129
130 private void loadAvatar(Jid jid, ImageView imageView) {
131 if (cancelPotentialWork(jid, imageView)) {
132 imageView.setBackgroundColor(UIHelper.getColorForName(jid.asBareJid().toString()));
133 imageView.setImageDrawable(null);
134 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
135 final AsyncDrawable asyncDrawable =
136 new AsyncDrawable(imageView.getContext().getResources(), null, task);
137 imageView.setImageDrawable(asyncDrawable);
138 try {
139 task.execute(jid);
140 } catch (final RejectedExecutionException ignored) {
141 }
142 }
143 }
144
145 private static boolean cancelPotentialWork(Jid jid, ImageView imageView) {
146 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
147
148 if (bitmapWorkerTask != null) {
149 final Jid oldJid = bitmapWorkerTask.jid;
150 if (oldJid == null || jid != oldJid) {
151 bitmapWorkerTask.cancel(true);
152 } else {
153 return false;
154 }
155 }
156 return true;
157 }
158
159 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
160 if (imageView != null) {
161 final Drawable drawable = imageView.getDrawable();
162 if (drawable instanceof AsyncDrawable asyncDrawable) {
163 return asyncDrawable.getBitmapWorkerTask();
164 }
165 }
166 return null;
167 }
168
169 static class AsyncDrawable extends BitmapDrawable {
170 private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
171
172 AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
173 super(res, bitmap);
174 bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
175 }
176
177 BitmapWorkerTask getBitmapWorkerTask() {
178 return bitmapWorkerTaskReference.get();
179 }
180 }
181}