1package eu.siacs.conversations.ui.adapter;
2
3import android.content.ActivityNotFoundException;
4import android.content.Context;
5import android.content.Intent;
6import android.content.res.Resources;
7import androidx.databinding.DataBindingUtil;
8import android.graphics.Bitmap;
9import android.graphics.drawable.BitmapDrawable;
10import android.graphics.drawable.Drawable;
11import android.net.Uri;
12import android.os.AsyncTask;
13import androidx.annotation.NonNull;
14import androidx.recyclerview.widget.RecyclerView;
15import android.view.LayoutInflater;
16import android.view.ViewGroup;
17import android.widget.ImageView;
18import android.widget.Toast;
19
20import java.lang.ref.WeakReference;
21import java.util.ArrayList;
22import java.util.List;
23import java.util.concurrent.RejectedExecutionException;
24
25import eu.siacs.conversations.R;
26import eu.siacs.conversations.databinding.MediaPreviewBinding;
27import eu.siacs.conversations.persistance.FileBackend;
28import eu.siacs.conversations.ui.ConversationFragment;
29import eu.siacs.conversations.ui.XmppActivity;
30import eu.siacs.conversations.ui.util.Attachment;
31
32public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
33
34 private final ArrayList<Attachment> mediaPreviews = new ArrayList<>();
35
36 private final ConversationFragment conversationFragment;
37
38 public MediaPreviewAdapter(ConversationFragment fragment) {
39 this.conversationFragment = fragment;
40 }
41
42 @NonNull
43 @Override
44 public MediaPreviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
45 LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
46 MediaPreviewBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media_preview, parent, false);
47 return new MediaPreviewViewHolder(binding);
48 }
49
50 @Override
51 public void onBindViewHolder(@NonNull MediaPreviewViewHolder holder, int position) {
52 final Context context = conversationFragment.getActivity();
53 final Attachment attachment = mediaPreviews.get(position);
54 if (attachment.renderThumbnail()) {
55 holder.binding.mediaPreview.setImageAlpha(255);
56 loadPreview(attachment, holder.binding.mediaPreview);
57 } else {
58 cancelPotentialWork(attachment, holder.binding.mediaPreview);
59 MediaAdapter.renderPreview(context, attachment, holder.binding.mediaPreview);
60 }
61 holder.binding.deleteButton.setOnClickListener(v -> {
62 final int pos = mediaPreviews.indexOf(attachment);
63 mediaPreviews.remove(pos);
64 notifyItemRemoved(pos);
65 conversationFragment.toggleInputMethod();
66 });
67 holder.binding.mediaPreview.setOnClickListener(v -> view(context, attachment));
68 }
69
70 private static void view(final Context context, Attachment attachment) {
71 final Intent view = new Intent(Intent.ACTION_VIEW);
72 final Uri uri = FileBackend.getUriForUri(context, attachment.getUri());
73 view.setDataAndType(uri, attachment.getMime());
74 view.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
75 try {
76 context.startActivity(view);
77 } catch (ActivityNotFoundException e) {
78 Toast.makeText(context, R.string.no_application_found_to_open_file, Toast.LENGTH_SHORT).show();
79 }
80 }
81
82 public void addMediaPreviews(List<Attachment> attachments) {
83 this.mediaPreviews.addAll(attachments);
84 notifyDataSetChanged();
85 }
86
87 private void loadPreview(Attachment attachment, ImageView imageView) {
88 if (cancelPotentialWork(attachment, imageView)) {
89 XmppActivity activity = (XmppActivity) conversationFragment.getActivity();
90 final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment,Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)),true);
91 if (bm != null) {
92 cancelPotentialWork(attachment, imageView);
93 imageView.setImageBitmap(bm);
94 imageView.setBackgroundColor(0x00000000);
95 } else {
96 imageView.setBackgroundColor(0xff333333);
97 imageView.setImageDrawable(null);
98 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
99 final AsyncDrawable asyncDrawable = new AsyncDrawable(conversationFragment.getActivity().getResources(), null, task);
100 imageView.setImageDrawable(asyncDrawable);
101 try {
102 task.execute(attachment);
103 } catch (final RejectedExecutionException ignored) {
104 }
105 }
106 }
107 }
108
109 private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) {
110 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
111
112 if (bitmapWorkerTask != null) {
113 final Attachment oldAttachment = bitmapWorkerTask.attachment;
114 if (oldAttachment == null || !oldAttachment.equals(attachment)) {
115 bitmapWorkerTask.cancel(true);
116 } else {
117 return false;
118 }
119 }
120 return true;
121 }
122
123 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
124 if (imageView != null) {
125 final Drawable drawable = imageView.getDrawable();
126 if (drawable instanceof AsyncDrawable) {
127 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
128 return asyncDrawable.getBitmapWorkerTask();
129 }
130 }
131 return null;
132 }
133
134 @Override
135 public int getItemCount() {
136 return mediaPreviews.size();
137 }
138
139 public boolean hasAttachments() {
140 return mediaPreviews.size() > 0;
141 }
142
143 public ArrayList<Attachment> getAttachments() {
144 return mediaPreviews;
145 }
146
147 public void clearPreviews() {
148 this.mediaPreviews.clear();
149 }
150
151 class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
152
153 private final MediaPreviewBinding binding;
154
155 MediaPreviewViewHolder(MediaPreviewBinding binding) {
156 super(binding.getRoot());
157 this.binding = binding;
158 }
159 }
160
161 static class AsyncDrawable extends BitmapDrawable {
162 private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
163
164 AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
165 super(res, bitmap);
166 bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
167 }
168
169 BitmapWorkerTask getBitmapWorkerTask() {
170 return bitmapWorkerTaskReference.get();
171 }
172 }
173
174 private static class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
175 private final WeakReference<ImageView> imageViewReference;
176 private Attachment attachment = null;
177
178 BitmapWorkerTask(ImageView imageView) {
179 imageViewReference = new WeakReference<>(imageView);
180 }
181
182 @Override
183 protected Bitmap doInBackground(Attachment... params) {
184 this.attachment = params[0];
185 final XmppActivity activity = XmppActivity.find(imageViewReference);
186 if (activity == null) {
187 return null;
188 }
189 return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)), false);
190 }
191
192 @Override
193 protected void onPostExecute(Bitmap bitmap) {
194 if (bitmap != null && !isCancelled()) {
195 final ImageView imageView = imageViewReference.get();
196 if (imageView != null) {
197 imageView.setImageBitmap(bitmap);
198 imageView.setBackgroundColor(0x00000000);
199 }
200 }
201 }
202 }
203}