1package eu.siacs.conversations.ui.adapter;
2
3import android.app.Activity;
4import android.content.Context;
5import android.content.res.Resources;
6import android.databinding.DataBindingUtil;
7import android.graphics.Bitmap;
8import android.graphics.drawable.BitmapDrawable;
9import android.graphics.drawable.Drawable;
10import android.os.AsyncTask;
11import android.support.annotation.AttrRes;
12import android.support.annotation.NonNull;
13import android.support.v7.widget.RecyclerView;
14import android.view.LayoutInflater;
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.ui.ConversationFragment;
26import eu.siacs.conversations.ui.XmppActivity;
27import eu.siacs.conversations.ui.util.Attachment;
28import eu.siacs.conversations.ui.util.StyledAttributes;
29
30public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapter.MediaPreviewViewHolder> {
31
32 private final List<Attachment> mediaPreviews = new ArrayList<>();
33
34 private final ConversationFragment conversationFragment;
35
36 public MediaPreviewAdapter(ConversationFragment fragment) {
37 this.conversationFragment = fragment;
38 }
39
40 @NonNull
41 @Override
42 public MediaPreviewViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
43 LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
44 MediaPreviewBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.media_preview, parent, false);
45 return new MediaPreviewViewHolder(binding);
46 }
47
48 @Override
49 public void onBindViewHolder(@NonNull MediaPreviewViewHolder holder, int position) {
50 final Context context = conversationFragment.getActivity();
51 final Attachment attachment = mediaPreviews.get(position);
52 if (attachment.renderThumbnail()) {
53 holder.binding.mediaPreview.setImageAlpha(255);
54 loadPreview(attachment, holder.binding.mediaPreview);
55 } else {
56 cancelPotentialWork(attachment, holder.binding.mediaPreview);
57 holder.binding.mediaPreview.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary));
58 holder.binding.mediaPreview.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255));
59 final @AttrRes int attr;
60 if (attachment.getType() == Attachment.Type.LOCATION) {
61 attr = R.attr.media_preview_location;
62 } else if (attachment.getType() == Attachment.Type.RECORDING) {
63 attr = R.attr.media_preview_recording;
64 } else {
65 final String mime = attachment.getMime();
66 if (mime == null) {
67 attr = R.attr.media_preview_file;
68 } else if (mime.startsWith("audio/")) {
69 attr = R.attr.media_preview_audio;
70 } else if (mime.equals("text/calendar") || (mime.equals("text/x-vcalendar"))) {
71 attr = R.attr.media_preview_calendar;
72 } else if (mime.equals("text/x-vcard")) {
73 attr = R.attr.media_preview_contact;
74 } else if (mime.equals("application/vnd.android.package-archive")) {
75 attr = R.attr.media_preview_app;
76 } else if (mime.equals("application/zip") || mime.equals("application/rar")) {
77 attr = R.attr.media_preview_archive;
78 } else {
79 attr = R.attr.media_preview_file;
80 }
81 }
82 holder.binding.mediaPreview.setImageDrawable(StyledAttributes.getDrawable(context, attr));
83 }
84 holder.binding.deleteButton.setOnClickListener(v -> {
85 int pos = mediaPreviews.indexOf(attachment);
86 mediaPreviews.remove(pos);
87 notifyItemRemoved(pos);
88 conversationFragment.toggleInputMethod();
89 });
90 }
91
92 public void addMediaPreviews(List<Attachment> attachments) {
93 this.mediaPreviews.addAll(attachments);
94 notifyDataSetChanged();
95 }
96
97 private void loadPreview(Attachment attachment, ImageView imageView) {
98 if (cancelPotentialWork(attachment, imageView)) {
99 XmppActivity activity = (XmppActivity) conversationFragment.getActivity();
100 final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment,Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)),true);
101 if (bm != null) {
102 cancelPotentialWork(attachment, imageView);
103 imageView.setImageBitmap(bm);
104 imageView.setBackgroundColor(0x00000000);
105 } else {
106 imageView.setBackgroundColor(0xff333333);
107 imageView.setImageDrawable(null);
108 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
109 final AsyncDrawable asyncDrawable = new AsyncDrawable(conversationFragment.getActivity().getResources(), null, task);
110 imageView.setImageDrawable(asyncDrawable);
111 try {
112 task.execute(attachment);
113 } catch (final RejectedExecutionException ignored) {
114 }
115 }
116 }
117 }
118
119 private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) {
120 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
121
122 if (bitmapWorkerTask != null) {
123 final Attachment oldAttachment = bitmapWorkerTask.attachment;
124 if (oldAttachment == null || !oldAttachment.equals(attachment)) {
125 bitmapWorkerTask.cancel(true);
126 } else {
127 return false;
128 }
129 }
130 return true;
131 }
132
133 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
134 if (imageView != null) {
135 final Drawable drawable = imageView.getDrawable();
136 if (drawable instanceof AsyncDrawable) {
137 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
138 return asyncDrawable.getBitmapWorkerTask();
139 }
140 }
141 return null;
142 }
143
144 @Override
145 public int getItemCount() {
146 return mediaPreviews.size();
147 }
148
149 public boolean hasAttachments() {
150 return mediaPreviews.size() > 0;
151 }
152
153 public List<Attachment> getAttachments() {
154 return mediaPreviews;
155 }
156
157 class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
158
159 private final MediaPreviewBinding binding;
160
161 MediaPreviewViewHolder(MediaPreviewBinding binding) {
162 super(binding.getRoot());
163 this.binding = binding;
164 }
165 }
166
167 static class AsyncDrawable extends BitmapDrawable {
168 private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
169
170 AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
171 super(res, bitmap);
172 bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
173 }
174
175 BitmapWorkerTask getBitmapWorkerTask() {
176 return bitmapWorkerTaskReference.get();
177 }
178 }
179
180 class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
181 private final WeakReference<ImageView> imageViewReference;
182 private Attachment attachment = null;
183
184 BitmapWorkerTask(ImageView imageView) {
185 imageViewReference = new WeakReference<>(imageView);
186 }
187
188 @Override
189 protected Bitmap doInBackground(Attachment... params) {
190 Activity activity = conversationFragment.getActivity();
191 if (activity instanceof XmppActivity) {
192 final XmppActivity xmppActivity = (XmppActivity) activity;
193 this.attachment = params[0];
194 return xmppActivity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(xmppActivity.getResources().getDimension(R.dimen.media_preview_size)), false);
195 } else {
196 return null;
197 }
198 }
199
200 @Override
201 protected void onPostExecute(Bitmap bitmap) {
202 if (bitmap != null && !isCancelled()) {
203 final ImageView imageView = imageViewReference.get();
204 if (imageView != null) {
205 imageView.setImageBitmap(bitmap);
206 imageView.setBackgroundColor(0x00000000);
207 }
208 }
209 }
210 }
211}