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 if (attachment.getMime() != null && attachment.getMime().startsWith("audio/")) {
66 attr = R.attr.media_preview_audio;
67 } else {
68 attr = R.attr.media_preview_file;
69 }
70 }
71 holder.binding.mediaPreview.setImageDrawable(StyledAttributes.getDrawable(context, attr));
72 }
73 holder.binding.deleteButton.setOnClickListener(v -> {
74 int pos = mediaPreviews.indexOf(attachment);
75 mediaPreviews.remove(pos);
76 notifyItemRemoved(pos);
77 conversationFragment.toggleInputMethod();
78 });
79 }
80
81 public void addMediaPreviews(List<Attachment> attachments) {
82 this.mediaPreviews.addAll(attachments);
83 notifyDataSetChanged();
84 }
85
86 private void loadPreview(Attachment attachment, ImageView imageView) {
87 if (cancelPotentialWork(attachment, imageView)) {
88 XmppActivity activity = (XmppActivity) conversationFragment.getActivity();
89 final Bitmap bm = activity.xmppConnectionService.getFileBackend().getPreviewForUri(attachment,Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)),true);
90 if (bm != null) {
91 cancelPotentialWork(attachment, imageView);
92 imageView.setImageBitmap(bm);
93 imageView.setBackgroundColor(0x00000000);
94 } else {
95 imageView.setBackgroundColor(0xff333333);
96 imageView.setImageDrawable(null);
97 final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
98 final AsyncDrawable asyncDrawable = new AsyncDrawable(conversationFragment.getActivity().getResources(), null, task);
99 imageView.setImageDrawable(asyncDrawable);
100 try {
101 task.execute(attachment);
102 } catch (final RejectedExecutionException ignored) {
103 }
104 }
105 }
106 }
107
108 private static boolean cancelPotentialWork(Attachment attachment, ImageView imageView) {
109 final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
110
111 if (bitmapWorkerTask != null) {
112 final Attachment oldAttachment = bitmapWorkerTask.attachment;
113 if (oldAttachment == null || !oldAttachment.equals(attachment)) {
114 bitmapWorkerTask.cancel(true);
115 } else {
116 return false;
117 }
118 }
119 return true;
120 }
121
122 private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
123 if (imageView != null) {
124 final Drawable drawable = imageView.getDrawable();
125 if (drawable instanceof AsyncDrawable) {
126 final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
127 return asyncDrawable.getBitmapWorkerTask();
128 }
129 }
130 return null;
131 }
132
133 @Override
134 public int getItemCount() {
135 return mediaPreviews.size();
136 }
137
138 public boolean hasAttachments() {
139 return mediaPreviews.size() > 0;
140 }
141
142 public List<Attachment> getAttachments() {
143 return mediaPreviews;
144 }
145
146 class MediaPreviewViewHolder extends RecyclerView.ViewHolder {
147
148 private final MediaPreviewBinding binding;
149
150 MediaPreviewViewHolder(MediaPreviewBinding binding) {
151 super(binding.getRoot());
152 this.binding = binding;
153 }
154 }
155
156 static class AsyncDrawable extends BitmapDrawable {
157 private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
158
159 AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
160 super(res, bitmap);
161 bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
162 }
163
164 BitmapWorkerTask getBitmapWorkerTask() {
165 return bitmapWorkerTaskReference.get();
166 }
167 }
168
169 class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
170 private final WeakReference<ImageView> imageViewReference;
171 private Attachment attachment = null;
172
173 BitmapWorkerTask(ImageView imageView) {
174 imageViewReference = new WeakReference<>(imageView);
175 }
176
177 @Override
178 protected Bitmap doInBackground(Attachment... params) {
179 Activity activity = conversationFragment.getActivity();
180 if (activity instanceof XmppActivity) {
181 final XmppActivity xmppActivity = (XmppActivity) activity;
182 this.attachment = params[0];
183 return xmppActivity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(xmppActivity.getResources().getDimension(R.dimen.media_preview_size)), false);
184 } else {
185 return null;
186 }
187 }
188
189 @Override
190 protected void onPostExecute(Bitmap bitmap) {
191 if (bitmap != null && !isCancelled()) {
192 final ImageView imageView = imageViewReference.get();
193 if (imageView != null) {
194 imageView.setImageBitmap(bitmap);
195 imageView.setBackgroundColor(0x00000000);
196 }
197 }
198 }
199 }
200}