From 1221cff561eb2ca321f96c7510e01f9218634bd6 Mon Sep 17 00:00:00 2001
From: Daniel Gultsch <daniel@gultsch.de>
Date: Thu, 29 Oct 2015 12:08:15 +0100
Subject: [PATCH] load avatars in message adapter in background task

---
 .../conversations/services/AvatarService.java |  21 +++-
 .../ui/adapter/MessageAdapter.java            | 103 ++++++++++++++++--
 src/main/res/layout/message_received.xml      |   4 +-
 src/main/res/layout/message_sent.xml          |   4 +-
 4 files changed, 115 insertions(+), 17 deletions(-)

diff --git a/src/main/java/eu/siacs/conversations/services/AvatarService.java b/src/main/java/eu/siacs/conversations/services/AvatarService.java
index 7412eb93..3cd32d79 100644
--- a/src/main/java/eu/siacs/conversations/services/AvatarService.java
+++ b/src/main/java/eu/siacs/conversations/services/AvatarService.java
@@ -16,6 +16,7 @@ import eu.siacs.conversations.entities.Bookmark;
 import eu.siacs.conversations.entities.Contact;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.ListItem;
+import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.MucOptions;
 import eu.siacs.conversations.utils.UIHelper;
 
@@ -179,9 +180,13 @@ public class AvatarService {
 	}
 
 	public Bitmap get(Account account, int size) {
+		return get(account, size, false);
+	}
+
+	public Bitmap get(Account account, int size, boolean cachedOnly) {
 		final String KEY = key(account, size);
 		Bitmap avatar = mXmppConnectionService.getBitmapCache().get(KEY);
-		if (avatar != null) {
+		if (avatar != null || cachedOnly) {
 			return avatar;
 		}
 		avatar = mXmppConnectionService.getFileBackend().getAvatar(
@@ -193,6 +198,19 @@ public class AvatarService {
 		return avatar;
 	}
 
+	public Bitmap get(Message message, int size, boolean cachedOnly) {
+		if (message.getStatus() == Message.STATUS_RECEIVED) {
+			Contact contact = message.getContact();
+			if (contact != null) {
+				return get(contact, size, cachedOnly);
+			} else {
+				return get(UIHelper.getMessageDisplayName(message), size, cachedOnly);
+			}
+		} else  {
+			return get(message.getConversation().getAccount(), size, cachedOnly);
+		}
+	}
+
 	public void clear(Account account) {
 		synchronized (this.sizes) {
 			for (Integer size : sizes) {
@@ -291,5 +309,4 @@ public class AvatarService {
 		Rect dst = new Rect(dstleft, dsttop, dstright, dstbottom);
 		canvas.drawBitmap(bm, null, dst, null);
 	}
-
 }
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
index 42c68eb8..2befd65c 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java
@@ -4,8 +4,13 @@ import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.Spanned;
@@ -24,7 +29,9 @@ import android.widget.LinearLayout;
 import android.widget.TextView;
 import android.widget.Toast;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
 
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
@@ -493,17 +500,8 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 				viewHolder.status_message.setText(message.getBody());
 			}
 			return view;
-		} else if (type == RECEIVED) {
-			Contact contact = message.getContact();
-			if (contact != null) {
-				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(contact, activity.getPixel(48)));
-			} else if (conversation.getMode() == Conversation.MODE_MULTI) {
-				viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(
-						UIHelper.getMessageDisplayName(message),
-						activity.getPixel(48)));
-			}
-		} else if (type == SENT) {
-			viewHolder.contact_picture.setImageBitmap(activity.avatarService().get(account, activity.getPixel(48)));
+		} else {
+			loadAvatar(message,viewHolder.contact_picture);
 		}
 
 		viewHolder.contact_picture
@@ -674,4 +672,87 @@ public class MessageAdapter extends ArrayAdapter<Message> {
 		protected TextView status_message;
 		protected TextView encryption;
 	}
+
+	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
+		private final WeakReference<ImageView> imageViewReference;
+		private Message message = null;
+
+		public BitmapWorkerTask(ImageView imageView) {
+			imageViewReference = new WeakReference<>(imageView);
+		}
+
+		@Override
+		protected Bitmap doInBackground(Message... params) {
+			return activity.avatarService().get(params[0], activity.getPixel(48), isCancelled());
+		}
+
+		@Override
+		protected void onPostExecute(Bitmap bitmap) {
+			if (bitmap != null) {
+				final ImageView imageView = imageViewReference.get();
+				if (imageView != null) {
+					imageView.setImageBitmap(bitmap);
+					imageView.setBackgroundColor(0x00000000);
+				}
+			}
+		}
+	}
+
+	public void loadAvatar(Message message, ImageView imageView) {
+		if (cancelPotentialWork(message, imageView)) {
+			final Bitmap bm = activity.avatarService().get(message, activity.getPixel(48), true);
+			if (bm != null) {
+				imageView.setImageBitmap(bm);
+				imageView.setBackgroundColor(0x00000000);
+			} else {
+				imageView.setBackgroundColor(UIHelper.getColorForName(UIHelper.getMessageDisplayName(message)));
+				imageView.setImageDrawable(null);
+				final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+				final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+				imageView.setImageDrawable(asyncDrawable);
+				try {
+					task.execute(message);
+				} catch (final RejectedExecutionException ignored) {
+				}
+			}
+		}
+	}
+
+	public static boolean cancelPotentialWork(Message message, ImageView imageView) {
+		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+		if (bitmapWorkerTask != null) {
+			final Message oldMessage = bitmapWorkerTask.message;
+			if (oldMessage == null || message != oldMessage) {
+				bitmapWorkerTask.cancel(true);
+			} else {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
+		if (imageView != null) {
+			final Drawable drawable = imageView.getDrawable();
+			if (drawable instanceof AsyncDrawable) {
+				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
+				return asyncDrawable.getBitmapWorkerTask();
+			}
+		}
+		return null;
+	}
+
+	static class AsyncDrawable extends BitmapDrawable {
+		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+		public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+			super(res, bitmap);
+			bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+		}
+
+		public BitmapWorkerTask getBitmapWorkerTask() {
+			return bitmapWorkerTaskReference.get();
+		}
+	}
 }
diff --git a/src/main/res/layout/message_received.xml b/src/main/res/layout/message_received.xml
index b6a29e86..a998bf37 100644
--- a/src/main/res/layout/message_received.xml
+++ b/src/main/res/layout/message_received.xml
@@ -11,8 +11,8 @@
 
     <com.makeramen.roundedimageview.RoundedImageView
         android:id="@+id/message_photo"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         android:layout_alignParentLeft="true"
         android:layout_alignParentTop="true"
         android:scaleType="fitXY"
diff --git a/src/main/res/layout/message_sent.xml b/src/main/res/layout/message_sent.xml
index e3b38cd9..55f874e6 100644
--- a/src/main/res/layout/message_sent.xml
+++ b/src/main/res/layout/message_sent.xml
@@ -11,8 +11,8 @@
 
     <com.makeramen.roundedimageview.RoundedImageView
         android:id="@+id/message_photo"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
         android:scaleType="fitXY"
         android:paddingBottom="3dp"
         android:src="@drawable/ic_profile"