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