Added back async image loading in chat bubbles

This commit is contained in:
Sylvain Berfini 2018-11-25 10:01:41 +01:00
parent 6d9b2ab592
commit 404c92da9f
3 changed files with 229 additions and 3 deletions

View file

@ -1,3 +1,5 @@
package org.linphone.chat;
/* /*
ChatMessageViewHolder.java ChatMessageViewHolder.java
Copyright (C) 2017 Belledonne Communications, Grenoble, France Copyright (C) 2017 Belledonne Communications, Grenoble, France
@ -17,8 +19,6 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
package org.linphone.chat;
import android.Manifest; import android.Manifest;
import android.content.Context; import android.content.Context;
@ -27,6 +27,8 @@ import androidx.recyclerview.widget.RecyclerView;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri; import android.net.Uri;
import android.text.Spanned; import android.text.Spanned;
import android.text.method.LinkMovementMethod; import android.text.method.LinkMovementMethod;
@ -54,6 +56,8 @@ import org.linphone.core.Content;
import org.linphone.mediastream.Log; import org.linphone.mediastream.Log;
import org.linphone.utils.FileUtils; import org.linphone.utils.FileUtils;
import org.linphone.utils.LinphoneUtils; import org.linphone.utils.LinphoneUtils;
import org.linphone.views.AsyncBitmap;
import org.linphone.views.BitmapWorkerTask;
import org.linphone.views.ContactAvatar; import org.linphone.views.ContactAvatar;
import java.io.File; import java.io.File;
@ -221,7 +225,7 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
View v; View v;
if (FileUtils.isExtensionImage(filePath)) { if (FileUtils.isExtensionImage(filePath)) {
v = content.findViewById(R.id.image); v = content.findViewById(R.id.image);
((ImageView)v).setImageURI(Uri.parse(c.getFilePath())); loadBitmap(c.getFilePath(), ((ImageView)v));
} else { } else {
v = content.findViewById(R.id.file); v = content.findViewById(R.id.file);
((TextView)v).setText(FileUtils.getNameFromFilePath(filePath)); ((TextView)v).setText(FileUtils.getNameFromFilePath(filePath));
@ -308,4 +312,33 @@ public class ChatMessageViewHolder extends RecyclerView.ViewHolder implements Vi
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION);
mContext.startActivity(intent); mContext.startActivity(intent);
} }
private void loadBitmap(String path, ImageView imageView) {
if (cancelPotentialWork(path, imageView)) {
Bitmap defaultBitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.chat_file);
BitmapWorkerTask task = new BitmapWorkerTask(mContext, imageView, defaultBitmap);
final AsyncBitmap asyncBitmap = new AsyncBitmap(mContext.getResources(), defaultBitmap, task);
imageView.setImageDrawable(asyncBitmap);
task.execute(path);
}
}
private boolean cancelPotentialWork(String path, ImageView imageView) {
final BitmapWorkerTask 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;
}
} }

View file

@ -0,0 +1,39 @@
package org.linphone.views;
/*
AsyncBitmap.java
Copyright (C) 2018 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import java.lang.ref.WeakReference;
public class AsyncBitmap extends BitmapDrawable {
private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
public AsyncBitmap(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
super(res, bitmap);
bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
}
public BitmapWorkerTask getBitmapWorkerTask() {
return bitmapWorkerTaskReference.get();
}
}

View file

@ -0,0 +1,154 @@
package org.linphone.views;
/*
BitmapWorkerTask.java
Copyright (C) 2018 Belledonne Communications, Grenoble, France
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.MediaStore;
import android.widget.ImageView;
import org.linphone.mediastream.Log;
import org.linphone.utils.FileUtils;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
private Context mContext;
private Bitmap mDefaultBitmap;
private final WeakReference<ImageView> imageViewReference;
public String path;
public BitmapWorkerTask(Context context, ImageView imageView, Bitmap defaultBitmap) {
mContext = context;
mDefaultBitmap = defaultBitmap;
path = null;
// Use a WeakReference to ensure the ImageView can be garbage collected
imageViewReference = new WeakReference<>(imageView);
}
private Bitmap scaleToFitHeight(Bitmap b, int height) {
float factor = height / (float) b.getHeight();
int dstWidth = (int) (b.getWidth() * factor);
if (dstWidth > 0 && height > 0) {
return Bitmap.createScaledBitmap(b, dstWidth, height, true);
}
return b;
}
// Decode image in background.
@Override
protected Bitmap doInBackground(String... params) {
path = params[0];
Bitmap bm = null;
Bitmap thumbnail = null;
if (FileUtils.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);
}
ImageView imageView = imageViewReference.get();
try {
// Rotate the bitmap if possible/needed, using EXIF data
Matrix matrix = new Matrix();
ExifInterface exif = new ExifInterface(path);
int pictureOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 0);
if (pictureOrientation == 6 || pictureOrientation == 3 || pictureOrientation == 8) {
if (pictureOrientation == 6) {
matrix.postRotate(90);
} else if (pictureOrientation == 3) {
matrix.postRotate(180);
} else {
matrix.postRotate(270);
}
if (imageView != null) {
if (pictureOrientation == 6 || pictureOrientation == 8) {
matrix.postScale(1, imageView.getMeasuredHeight() / (float) bm.getHeight());
} else {
matrix.postScale(imageView.getMeasuredHeight() / (float) bm.getHeight(), 1);
}
}
thumbnail = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
if (thumbnail != bm) {
bm.recycle();
bm = null;
}
}
} catch (Exception e) {
Log.e(e);
}
if (thumbnail == null && bm != null) {
if (imageView == null) return bm;
thumbnail = scaleToFitHeight(bm, imageView.getMeasuredHeight());
if (thumbnail != bm) {
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.recycle();
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);
}
}
}
public static 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;
}
}