MediaPreviewAdapter.java

  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}