loading avatars in seperate tasks

iNPUTmice created

Change summary

src/main/java/eu/siacs/conversations/services/AvatarService.java         | 40 
src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java | 90 
src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java     | 91 
3 files changed, 203 insertions(+), 18 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/AvatarService.java 🔗

@@ -38,10 +38,10 @@ public class AvatarService {
 		this.mXmppConnectionService = service;
 	}
 
-	public Bitmap get(final Contact contact, final int size) {
+	private Bitmap get(final Contact contact, final int size, boolean cachedOnly) {
 		final String KEY = key(contact, size);
 		Bitmap avatar = this.mXmppConnectionService.getBitmapCache().get(KEY);
-		if (avatar != null) {
+		if (avatar != null || cachedOnly) {
 			return avatar;
 		}
 		if (contact.getProfilePhoto() != null) {
@@ -51,7 +51,7 @@ public class AvatarService {
 			avatar = mXmppConnectionService.getFileBackend().getAvatar(contact.getAvatar(), size);
 		}
 		if (avatar == null) {
-            avatar = get(contact.getDisplayName(), size);
+            avatar = get(contact.getDisplayName(), size, cachedOnly);
 		}
 		this.mXmppConnectionService.getBitmapCache().put(KEY, avatar);
 		return avatar;
@@ -77,25 +77,33 @@ public class AvatarService {
 	}
 
 	public Bitmap get(ListItem item, int size) {
+		return get(item,size,false);
+	}
+
+	public Bitmap get(ListItem item, int size, boolean cachedOnly) {
 		if (item instanceof Contact) {
-			return get((Contact) item, size);
+			return get((Contact) item, size,cachedOnly);
 		} else if (item instanceof Bookmark) {
 			Bookmark bookmark = (Bookmark) item;
 			if (bookmark.getConversation() != null) {
-				return get(bookmark.getConversation(), size);
+				return get(bookmark.getConversation(), size, cachedOnly);
 			} else {
-				return get(bookmark.getDisplayName(), size);
+				return get(bookmark.getDisplayName(), size, cachedOnly);
 			}
 		} else {
-			return get(item.getDisplayName(), size);
+			return get(item.getDisplayName(), size, cachedOnly);
 		}
 	}
 
 	public Bitmap get(Conversation conversation, int size) {
+		return get(conversation,size,false);
+	}
+
+	public Bitmap get(Conversation conversation, int size, boolean cachedOnly) {
 		if (conversation.getMode() == Conversation.MODE_SINGLE) {
-			return get(conversation.getContact(), size);
+			return get(conversation.getContact(), size, cachedOnly);
 		} else {
-			return get(conversation.getMucOptions(), size);
+			return get(conversation.getMucOptions(), size, cachedOnly);
 		}
 	}
 
@@ -107,10 +115,10 @@ public class AvatarService {
 		}
 	}
 
-	public Bitmap get(MucOptions mucOptions, int size) {
+	private Bitmap get(MucOptions mucOptions, int size,  boolean cachedOnly) {
 		final String KEY = key(mucOptions, size);
 		Bitmap bitmap = this.mXmppConnectionService.getBitmapCache().get(KEY);
-		if (bitmap != null) {
+		if (bitmap != null || cachedOnly) {
 			return bitmap;
 		}
 		final List<MucOptions.User> users = new ArrayList<>(mucOptions.getUsers());
@@ -179,7 +187,7 @@ public class AvatarService {
 		avatar = mXmppConnectionService.getFileBackend().getAvatar(
 				account.getAvatar(), size);
 		if (avatar == null) {
-			avatar = get(account.getJid().toBareJid().toString(), size);
+			avatar = get(account.getJid().toBareJid().toString(), size,false);
 		}
 		mXmppConnectionService.getBitmapCache().put(KEY, avatar);
 		return avatar;
@@ -204,10 +212,14 @@ public class AvatarService {
 				+ String.valueOf(size);
 	}
 
-	public Bitmap get(final String name, final int size) {
+	public Bitmap get(String name, int size) {
+		return get(name,size,false);
+	}
+
+	public Bitmap get(final String name, final int size, boolean cachedOnly) {
 		final String KEY = key(name, size);
 		Bitmap bitmap = mXmppConnectionService.getBitmapCache().get(KEY);
-		if (bitmap != null) {
+		if (bitmap != null || cachedOnly) {
 			return bitmap;
 		}
 		bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);

src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java 🔗

@@ -1,8 +1,13 @@
 package eu.siacs.conversations.ui.adapter;
 
 import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Typeface;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
 import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -11,12 +16,13 @@ import android.widget.ArrayAdapter;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
 
 import eu.siacs.conversations.R;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Downloadable;
-import eu.siacs.conversations.entities.DownloadableFile;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.ui.ConversationActivity;
 import eu.siacs.conversations.ui.XmppActivity;
@@ -98,8 +104,88 @@ public class ConversationAdapter extends ArrayAdapter<Conversation> {
 
 		mTimestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent()));
 		ImageView profilePicture = (ImageView) view.findViewById(R.id.conversation_image);
-		profilePicture.setImageBitmap(activity.avatarService().get(conversation, activity.getPixel(56)));
+		loadAvatar(conversation,profilePicture);
 
 		return view;
 	}
+
+	class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> {
+		private final WeakReference<ImageView> imageViewReference;
+		private Conversation conversation = null;
+
+		public BitmapWorkerTask(ImageView imageView) {
+			imageViewReference = new WeakReference<>(imageView);
+		}
+
+		@Override
+		protected Bitmap doInBackground(Conversation... params) {
+			return activity.avatarService().get(params[0], activity.getPixel(56));
+		}
+
+		@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(Conversation conversation, ImageView imageView) {
+		Bitmap bm = activity.avatarService().get(conversation,activity.getPixel(56),true);
+		if (bm != null) {
+			imageView.setImageBitmap(bm);
+			imageView.setBackgroundColor(0x00000000);
+		} else if (cancelPotentialWork(conversation, imageView)) {
+			imageView.setBackgroundColor(0xff333333);
+			final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+			final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+			imageView.setImageDrawable(asyncDrawable);
+			try {
+				task.execute(conversation);
+			} catch (final RejectedExecutionException ignored) {
+			}
+		}
+	}
+
+	public static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) {
+		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+		if (bitmapWorkerTask != null) {
+			final Conversation oldConversation = bitmapWorkerTask.conversation;
+			if (oldConversation == null || conversation != oldConversation) {
+				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();
+		}
+	}
 }

src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java 🔗

@@ -1,15 +1,23 @@
 package eu.siacs.conversations.ui.adapter;
 
+import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
 
 import eu.siacs.conversations.Config;
 import eu.siacs.conversations.R;
+import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.ListItem;
 import eu.siacs.conversations.ui.XmppActivity;
 import eu.siacs.conversations.xmpp.jid.Jid;
 
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
 import android.preference.PreferenceManager;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -77,8 +85,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 			tvJid.setText("");
 		}
 		tvName.setText(item.getDisplayName());
-		picture.setImageBitmap(activity.avatarService().get(item,
-				activity.getPixel(48)));
+		loadAvatar(item,picture);
 		return view;
 	}
 
@@ -90,4 +97,84 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 		public void onTagClicked(String tag);
 	}
 
+	class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
+		private final WeakReference<ImageView> imageViewReference;
+		private ListItem item = null;
+
+		public BitmapWorkerTask(ImageView imageView) {
+			imageViewReference = new WeakReference<>(imageView);
+		}
+
+		@Override
+		protected Bitmap doInBackground(ListItem... params) {
+			return activity.avatarService().get(params[0], activity.getPixel(48));
+		}
+
+		@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(ListItem item, ImageView imageView) {
+		Bitmap bm = activity.avatarService().get(item,activity.getPixel(56),true);
+		if (bm != null) {
+			imageView.setImageBitmap(bm);
+			imageView.setBackgroundColor(0x00000000);
+		} else if (cancelPotentialWork(item, imageView)) {
+			imageView.setBackgroundColor(0xff333333);
+			final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+			final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
+			imageView.setImageDrawable(asyncDrawable);
+			try {
+				task.execute(item);
+			} catch (final RejectedExecutionException ignored) {
+			}
+		}
+	}
+
+	public static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
+		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
+
+		if (bitmapWorkerTask != null) {
+			final ListItem oldItem = bitmapWorkerTask.item;
+			if (oldItem == null || item != oldItem) {
+				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();
+		}
+	}
+
 }