From d2f3ed7e43754e947b0fcdb9dc967c18a16dcdf0 Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Wed, 25 Oct 2017 16:33:10 +0200 Subject: [PATCH] Added back bitmap handling classes & methods --- .../org/linphone/chat/ChatEventsAdapter.java | 201 +++++++++++++++++- .../org/linphone/chat/GroupChatFragment.java | 8 +- 2 files changed, 204 insertions(+), 5 deletions(-) diff --git a/src/android/org/linphone/chat/ChatEventsAdapter.java b/src/android/org/linphone/chat/ChatEventsAdapter.java index 498fb0082..83a38e36b 100644 --- a/src/android/org/linphone/chat/ChatEventsAdapter.java +++ b/src/android/org/linphone/chat/ChatEventsAdapter.java @@ -1,5 +1,5 @@ /* -GroupChatFragment.java +ChatEventsAdapter.java Copyright (C) 2017 Belledonne Communications, Grenoble, France This program is free software; you can redistribute it and/or @@ -20,12 +20,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. package org.linphone.chat; import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.media.ExifInterface; +import android.media.ThumbnailUtils; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.MediaStore; +import android.support.v4.content.FileProvider; import android.text.Spanned; import android.text.method.LinkMovementMethod; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.webkit.MimeTypeMap; import android.widget.BaseAdapter; +import android.widget.ImageView; import android.widget.RelativeLayout; import org.linphone.LinphoneManager; @@ -39,21 +54,31 @@ import org.linphone.core.Address; import org.linphone.core.ChatMessage; import org.linphone.core.Core; import org.linphone.core.EventLog; +import org.linphone.mediastream.Log; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.lang.ref.WeakReference; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Calendar; import java.util.List; import java.util.regex.Pattern; +import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION; + public class ChatEventsAdapter extends BaseAdapter { private Context mContext; + GroupChatFragment mFragment; private List mHistory; private List mParticipants; private LayoutInflater mLayoutInflater; + private Bitmap mDefaultBitmap; - public ChatEventsAdapter(Context context, LayoutInflater inflater, EventLog[] history, List participants) { + public ChatEventsAdapter(Context context, GroupChatFragment fragment, LayoutInflater inflater, EventLog[] history, List participants) { mContext = context; + mFragment = fragment; mLayoutInflater = inflater; mHistory = Arrays.asList(history); mParticipants = participants; @@ -288,4 +313,176 @@ public class ChatEventsAdapter extends BaseAdapter { return Compatibility.fromHtml(text); } + + public void loadBitmap(String path, ImageView imageView) { + if (cancelPotentialWork(path, imageView)) { + if (LinphoneUtils.isExtensionImage(path)) { + mDefaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_attachment_over); + } else { + mDefaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_attachment); + } + + BitmapWorkerTask task = new BitmapWorkerTask(imageView); + final AsyncBitmap asyncBitmap = new AsyncBitmap(mContext.getResources(), mDefaultBitmap, task); + imageView.setImageDrawable(asyncBitmap); + task.execute(path); + } + } + + private class BitmapWorkerTask extends AsyncTask { + private static final int SIZE_SMALL = 500; + private final WeakReference imageViewReference; + public String path; + + public BitmapWorkerTask(ImageView imageView) { + path = null; + // Use a WeakReference to ensure the ImageView can be garbage collected + imageViewReference = new WeakReference(imageView); + } + + // Decode image in background. + @Override + protected Bitmap doInBackground(String... params) { + path = params[0]; + Bitmap bm = null; + Bitmap thumbnail = null; + if(LinphoneUtils.isExtensionImage(path)) { + if (path.startsWith("content")) { + try { + bm = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), Uri.parse(path)); + } catch (FileNotFoundException e) { + Log.e(e); + } catch (IOException e) { + Log.e(e); + } + } else { + bm = BitmapFactory.decodeFile(path); + } + + // Rotate the bitmap if possible/needed, using EXIF data + try { + Bitmap bm_tmp; + ExifInterface exif = new ExifInterface(path); + int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0); + Matrix matrix = new Matrix(); + if (pictureOrientation == 6) { + matrix.postRotate(90); + } else if (pictureOrientation == 3) { + matrix.postRotate(180); + } else if (pictureOrientation == 8) { + matrix.postRotate(270); + } + bm_tmp = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true); + if (bm_tmp != bm) { + bm.recycle(); + bm = bm_tmp; + } else { + bm_tmp = null; + } + } catch (Exception e) { + Log.e(e); + } + + if (bm != null) { + thumbnail = ThumbnailUtils.extractThumbnail(bm, SIZE_SMALL, SIZE_SMALL); + bm.recycle(); + } + return thumbnail; + }else + return mDefaultBitmap; + } + + // Once complete, see if ImageView is still around and set bitmap. + @Override + protected void onPostExecute(Bitmap bitmap) { + if (isCancelled()) { + bitmap = null; + } + if (imageViewReference != null && bitmap != null) { + final ImageView imageView = imageViewReference.get(); + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + if (this == bitmapWorkerTask && imageView != null) { + imageView.setImageBitmap(bitmap); + + //Force scroll too bottom with setSelection() after image loaded and last messages + mFragment.scrollToBottom(); + + imageView.setTag(path); + imageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(Intent.ACTION_VIEW); + File file = null; + Uri contentUri = null; + String imageUri = (String)v.getTag(); + if (imageUri.startsWith("file://")) { + imageUri = imageUri.substring("file://".length()); + file = new File(imageUri); + contentUri = FileProvider.getUriForFile(mContext, "org.linphone.provider", file); + } else if (imageUri.startsWith("content://")) { + contentUri = Uri.parse(imageUri); + } else { + file = new File(imageUri); + contentUri = FileProvider.getUriForFile(mContext, "org.linphone.provider", file); + } + String type = null; + String extension = MimeTypeMap.getFileExtensionFromUrl(contentUri.toString()); + if (extension != null) { + type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); + } + if(type != null) { + intent.setDataAndType(contentUri, type); + }else { + intent.setDataAndType(contentUri, "*/*"); + } + intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); + mContext.startActivity(intent); + } + }); + } + } + } + } + + class AsyncBitmap extends BitmapDrawable { + private final WeakReference bitmapWorkerTaskReference; + + public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { + super(res, bitmap); + bitmapWorkerTaskReference = new WeakReference(bitmapWorkerTask); + } + + public BitmapWorkerTask getBitmapWorkerTask() { + return bitmapWorkerTaskReference.get(); + } + } + + private boolean cancelPotentialWork(String path, ImageView imageView) { + final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); + + if (bitmapWorkerTask != null) { + final String bitmapData = bitmapWorkerTask.path; + // If bitmapData is not yet set or it differs from the new data + if (bitmapData == null || bitmapData != path) { + // Cancel previous task + bitmapWorkerTask.cancel(true); + } else { + // The same work is already in progress + return false; + } + } + // No task associated with the ImageView, or an existing task was cancelled + return true; + } + + private BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { + if (imageView != null) { + final Drawable drawable = imageView.getDrawable(); + if (drawable instanceof AsyncBitmap) { + final AsyncBitmap asyncDrawable = (AsyncBitmap) drawable; + return asyncDrawable.getBitmapWorkerTask(); + } + } + return null; + } } diff --git a/src/android/org/linphone/chat/GroupChatFragment.java b/src/android/org/linphone/chat/GroupChatFragment.java index b14cdbd85..2b5cb8225 100644 --- a/src/android/org/linphone/chat/GroupChatFragment.java +++ b/src/android/org/linphone/chat/GroupChatFragment.java @@ -456,7 +456,7 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con private void displayChatRoomHistory() { if (mChatRoom == null) return; - mMessagesAdapter = new ChatEventsAdapter(getActivity(), mInflater, mChatRoom.getHistoryEvents(0), mParticipants); + mMessagesAdapter = new ChatEventsAdapter(getActivity(), this, mInflater, mChatRoom.getHistoryEvents(0), mParticipants); mChatEventsList.setAdapter(mMessagesAdapter); } @@ -574,8 +574,10 @@ public class GroupChatFragment extends Fragment implements ChatRoomListener, Con mMessageTextToSend.setText(""); } - private void scrollToBottom() { - mChatEventsList.setSelection(mMessagesAdapter.getCount() - 1); + public void scrollToBottom() { + if (((mChatEventsList.getLastVisiblePosition() >= (mMessagesAdapter.getCount() - 1)) && (mChatEventsList.getFirstVisiblePosition() <= (mMessagesAdapter.getCount() - 1)))) { + mChatEventsList.setSelection(mMessagesAdapter.getCount() - 1); + } } @Override