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