make bitmap worker task static; migrate conversation list row to binder

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/ui/XmppActivity.java                |  20 
src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java      |  16 
src/main/java/eu/siacs/conversations/ui/adapter/ConversationAdapter.java | 587 
src/main/java/eu/siacs/conversations/ui/adapter/ListItemAdapter.java     |  31 
src/main/java/eu/siacs/conversations/ui/adapter/MediaAdapter.java        |  14 
src/main/java/eu/siacs/conversations/ui/adapter/MediaPreviewAdapter.java |  12 
src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java      |  12 
src/main/res/layout/conversation_list_row.xml                            | 204 
8 files changed, 453 insertions(+), 443 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/ui/XmppActivity.java 🔗

@@ -893,7 +893,7 @@ public abstract class XmppActivity extends ActionBarActivity {
 			if (cancelPotentialWork(message, imageView)) {
 				imageView.setBackgroundColor(0xff333333);
 				imageView.setImageDrawable(null);
-				final BitmapWorkerTask task = new BitmapWorkerTask(this, imageView);
+				final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
 				final AsyncDrawable asyncDrawable = new AsyncDrawable(
 						getResources(), null, task);
 				imageView.setImageDrawable(asyncDrawable);
@@ -944,11 +944,9 @@ public abstract class XmppActivity extends ActionBarActivity {
 
 	static class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
 		private final WeakReference<ImageView> imageViewReference;
-		private final WeakReference<XmppActivity> activity;
 		private Message message = null;
 
-		private BitmapWorkerTask(XmppActivity activity, ImageView imageView) {
-			this.activity = new WeakReference<>(activity);
+		private BitmapWorkerTask(ImageView imageView) {
 			this.imageViewReference = new WeakReference<>(imageView);
 		}
 
@@ -959,7 +957,7 @@ public abstract class XmppActivity extends ActionBarActivity {
 			}
 			message = params[0];
 			try {
-				XmppActivity activity = this.activity.get();
+				final XmppActivity activity = find(imageViewReference);
 				if (activity != null && activity.xmppConnectionService != null) {
 					return activity.xmppConnectionService.getFileBackend().getThumbnail(message, (int) (activity.metrics.density * 288), false);
 				} else {
@@ -994,4 +992,16 @@ public abstract class XmppActivity extends ActionBarActivity {
 			return bitmapWorkerTaskReference.get();
 		}
 	}
+
+	public static XmppActivity find(WeakReference<ImageView> viewWeakReference) {
+		final View view = viewWeakReference.get();
+		if (view == null) {
+			return null;
+		}
+		final Context context = view.getContext();
+		if (context instanceof XmppActivity) {
+			return (XmppActivity) context;
+		}
+		return null;
+	}
 }

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

@@ -97,17 +97,21 @@ public class AccountAdapter extends ArrayAdapter<Account> {
         }
     }
 
-    class BitmapWorkerTask extends AsyncTask<Account, Void, Bitmap> {
+    private static class BitmapWorkerTask extends AsyncTask<Account, Void, Bitmap> {
         private final WeakReference<ImageView> imageViewReference;
         private Account account = null;
 
-        public BitmapWorkerTask(ImageView imageView) {
+        BitmapWorkerTask(ImageView imageView) {
             imageViewReference = new WeakReference<>(imageView);
         }
 
         @Override
         protected Bitmap doInBackground(Account... params) {
             this.account = params[0];
+            final XmppActivity activity = XmppActivity.find(imageViewReference);
+            if (activity == null) {
+                return null;
+            }
             return activity.avatarService().get(this.account, activity.getPixel(48), isCancelled());
         }
 
@@ -123,7 +127,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
         }
     }
 
-    public void loadAvatar(Account account, ImageView imageView) {
+    private void loadAvatar(Account account, ImageView imageView) {
         if (cancelPotentialWork(account, imageView)) {
             final Bitmap bm = activity.avatarService().get(account, activity.getPixel(48), true);
             if (bm != null) {
@@ -149,7 +153,7 @@ public class AccountAdapter extends ArrayAdapter<Account> {
         void onClickTglAccountState(Account account, boolean state);
     }
 
-    public static boolean cancelPotentialWork(Account account, ImageView imageView) {
+    private static boolean cancelPotentialWork(Account account, ImageView imageView) {
         final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 
         if (bitmapWorkerTask != null) {
@@ -177,12 +181,12 @@ public class AccountAdapter extends ArrayAdapter<Account> {
     static class AsyncDrawable extends BitmapDrawable {
         private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
 
-        public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+        AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
             super(res, bitmap);
             bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
         }
 
-        public BitmapWorkerTask getBitmapWorkerTask() {
+        BitmapWorkerTask getBitmapWorkerTask() {
             return bitmapWorkerTaskReference.get();
         }
     }

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

@@ -2,6 +2,7 @@ package eu.siacs.conversations.ui.adapter;
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.databinding.DataBindingUtil;
 import android.graphics.Bitmap;
 import android.graphics.Typeface;
 import android.graphics.drawable.BitmapDrawable;
@@ -22,6 +23,7 @@ import java.util.List;
 import java.util.concurrent.RejectedExecutionException;
 
 import eu.siacs.conversations.R;
+import eu.siacs.conversations.databinding.ConversationListRowBinding;
 import eu.siacs.conversations.entities.Conversation;
 import eu.siacs.conversations.entities.Message;
 import eu.siacs.conversations.entities.Transferable;
@@ -36,307 +38,286 @@ import rocks.xmpp.addr.Jid;
 
 public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {
 
-	private XmppActivity activity;
-	private List<Conversation> conversations;
-	private OnConversationClickListener listener;
-
-	public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
-		this.activity = activity;
-		this.conversations = conversations;
-	}
-
-	private 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;
-	}
-
-	@NonNull
-	@Override
-	public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
-		LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-		View view = inflater.inflate(R.layout.conversation_list_row, parent, false);
-		return ConversationViewHolder.get(view);
-	}
-
-	@Override
-	public void onBindViewHolder(@NonNull ConversationViewHolder viewHolder, int position) {
-		Conversation conversation = conversations.get(position);
-		if (conversation == null) {
-			return;
-		}
-		CharSequence name = conversation.getName();
-		if (name instanceof Jid) {
-			viewHolder.name.setText(IrregularUnicodeDetector.style(activity, (Jid) name));
-		} else {
-			viewHolder.name.setText(EmojiWrapper.transform(name));
-		}
-
-		if (conversation == ConversationFragment.getConversation(activity)) {
-			viewHolder.frame.setBackgroundColor(StyledAttributes.getColor(activity,R.attr.color_background_tertiary));
-		} else {
-			viewHolder.frame.setBackgroundColor(StyledAttributes.getColor(activity,R.attr.color_background_primary));
-		}
-
-		Message message = conversation.getLatestMessage();
-		final int unreadCount = conversation.unreadCount();
-		final boolean isRead = conversation.isRead();
-		final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
-		if (unreadCount > 0) {
-			viewHolder.unreadCount.setVisibility(View.VISIBLE);
-			viewHolder.unreadCount.setUnreadCount(unreadCount);
-		} else {
-			viewHolder.unreadCount.setVisibility(View.GONE);
-		}
-
-		if (isRead) {
-			viewHolder.name.setTypeface(null, Typeface.NORMAL);
-		} else {
-			viewHolder.name.setTypeface(null, Typeface.BOLD);
-		}
-
-		if (draft != null) {
-			viewHolder.lastMessageIcon.setVisibility(View.GONE);
-			viewHolder.lastMessage.setText(EmojiWrapper.transform(draft.getMessage()));
-			viewHolder.sender.setText(R.string.draft);
-			viewHolder.sender.setVisibility(View.VISIBLE);
-			viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL);
-			viewHolder.sender.setTypeface(null, Typeface.ITALIC);
-		} else {
-			final boolean fileAvailable = !message.isDeleted();
-			final boolean showPreviewText;
-			if (fileAvailable && (message.isFileOrImage() || message.treatAsDownloadable() || message.isGeoUri())) {
-				final int imageResource;
-				if (message.isGeoUri()) {
-					imageResource = activity.getThemeResource(R.attr.ic_attach_location, R.drawable.ic_attach_location);
-					showPreviewText = false;
-				} else {
-					final String mime = message.getMimeType();
-					switch (mime == null ? "" : mime.split("/")[0]) {
-						case "image":
-							imageResource = activity.getThemeResource(R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
-							showPreviewText = false;
-							break;
-						case "video":
-							imageResource = activity.getThemeResource(R.attr.ic_attach_videocam, R.drawable.ic_attach_videocam);
-							showPreviewText = false;
-							break;
-						case "audio":
-							imageResource = activity.getThemeResource(R.attr.ic_attach_record, R.drawable.ic_attach_record);
-							showPreviewText = false;
-							break;
-						default:
-							imageResource = activity.getThemeResource(R.attr.ic_attach_document, R.drawable.ic_attach_document);
-							showPreviewText = true;
-							break;
-					}
-				}
-				viewHolder.lastMessageIcon.setImageResource(imageResource);
-				viewHolder.lastMessageIcon.setVisibility(View.VISIBLE);
-			} else {
-				viewHolder.lastMessageIcon.setVisibility(View.GONE);
-				showPreviewText = true;
-			}
-			final Pair<CharSequence, Boolean> preview = UIHelper.getMessagePreview(activity, message, viewHolder.lastMessage.getCurrentTextColor());
-			if (showPreviewText) {
-				viewHolder.lastMessage.setText(EmojiWrapper.transform(UIHelper.shorten(preview.first)));
-			} else {
-				viewHolder.lastMessageIcon.setContentDescription(preview.first);
-			}
-			viewHolder.lastMessage.setVisibility(showPreviewText ? View.VISIBLE : View.GONE);
-			if (preview.second) {
-				if (isRead) {
-					viewHolder.lastMessage.setTypeface(null, Typeface.ITALIC);
-					viewHolder.sender.setTypeface(null, Typeface.NORMAL);
-				} else {
-					viewHolder.lastMessage.setTypeface(null, Typeface.BOLD_ITALIC);
-					viewHolder.sender.setTypeface(null, Typeface.BOLD);
-				}
-			} else {
-				if (isRead) {
-					viewHolder.lastMessage.setTypeface(null, Typeface.NORMAL);
-					viewHolder.sender.setTypeface(null, Typeface.NORMAL);
-				} else {
-					viewHolder.lastMessage.setTypeface(null, Typeface.BOLD);
-					viewHolder.sender.setTypeface(null, Typeface.BOLD);
-				}
-			}
-			if (message.getStatus() == Message.STATUS_RECEIVED) {
-				if (conversation.getMode() == Conversation.MODE_MULTI) {
-					viewHolder.sender.setVisibility(View.VISIBLE);
-					viewHolder.sender.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
-				} else {
-					viewHolder.sender.setVisibility(View.GONE);
-				}
-			} else if (message.getType() != Message.TYPE_STATUS) {
-				viewHolder.sender.setVisibility(View.VISIBLE);
-				viewHolder.sender.setText(activity.getString(R.string.me) + ':');
-			} else {
-				viewHolder.sender.setVisibility(View.GONE);
-			}
-		}
-
-		long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
-		if (muted_till == Long.MAX_VALUE) {
-			viewHolder.notificationIcon.setVisibility(View.VISIBLE);
-			int ic_notifications_off = activity.getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
-			viewHolder.notificationIcon.setImageResource(ic_notifications_off);
-		} else if (muted_till >= System.currentTimeMillis()) {
-			viewHolder.notificationIcon.setVisibility(View.VISIBLE);
-			int ic_notifications_paused = activity.getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp);
-			viewHolder.notificationIcon.setImageResource(ic_notifications_paused);
-		} else if (conversation.alwaysNotify()) {
-			viewHolder.notificationIcon.setVisibility(View.GONE);
-		} else {
-			viewHolder.notificationIcon.setVisibility(View.VISIBLE);
-			int ic_notifications_none = activity.getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
-			viewHolder.notificationIcon.setImageResource(ic_notifications_none);
-		}
-
-		long timestamp;
-		if (draft != null) {
-			timestamp = draft.getTimestamp();
-		} else {
-			timestamp = conversation.getLatestMessage().getTimeSent();
-		}
-		viewHolder.timestamp.setText(UIHelper.readableTimeDifference(activity, timestamp));
-		loadAvatar(conversation, viewHolder.avatar);
-		viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v,conversation));
-	}
-
-	@Override
-	public int getItemCount() {
-		return conversations.size();
-	}
-
-	public void setConversationClickListener(OnConversationClickListener listener) {
-		this.listener = listener;
-	}
-
-	private void loadAvatar(Conversation conversation, ImageView imageView) {
-		if (cancelPotentialWork(conversation, imageView)) {
-			final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
-			if (bm != null) {
-				cancelPotentialWork(conversation, imageView);
-				imageView.setImageBitmap(bm);
-				imageView.setBackgroundColor(0x00000000);
-			} else {
-				imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName().toString()));
-				imageView.setImageDrawable(null);
-				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 void insert(Conversation c, int position) {
-		conversations.add(position,c);
-		notifyDataSetChanged();
-	}
-
-	public void remove(Conversation conversation,int position) {
-		conversations.remove(conversation);
-		notifyItemRemoved(position);
-	}
-
-	public static class ConversationViewHolder extends RecyclerView.ViewHolder {
-		private TextView name;
-		private TextView lastMessage;
-		private ImageView lastMessageIcon;
-		private TextView sender;
-		private TextView timestamp;
-		private ImageView notificationIcon;
-		private UnreadCountCustomView unreadCount;
-		private ImageView avatar;
-		private FrameLayout frame;
-
-		private ConversationViewHolder(View view) {
-			super(view);
-		}
-
-		public static ConversationViewHolder get(View layout) {
-			ConversationViewHolder conversationViewHolder = (ConversationViewHolder) layout.getTag();
-			if (conversationViewHolder == null) {
-				conversationViewHolder = new ConversationViewHolder(layout);
-				conversationViewHolder.frame = layout.findViewById(R.id.frame);
-				conversationViewHolder.name = layout.findViewById(R.id.conversation_name);
-				conversationViewHolder.lastMessage = layout.findViewById(R.id.conversation_lastmsg);
-				conversationViewHolder.lastMessageIcon = layout.findViewById(R.id.conversation_lastmsg_img);
-				conversationViewHolder.timestamp = layout.findViewById(R.id.conversation_lastupdate);
-				conversationViewHolder.sender = layout.findViewById(R.id.sender_name);
-				conversationViewHolder.notificationIcon = layout.findViewById(R.id.notification_status);
-				conversationViewHolder.unreadCount = layout.findViewById(R.id.unread_count);
-				conversationViewHolder.avatar = layout.findViewById(R.id.conversation_image);
-				layout.setTag(conversationViewHolder);
-			}
-			return conversationViewHolder;
-		}
-	}
-
-	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();
-		}
-	}
-
-	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) {
-			this.conversation = params[0];
-			return activity.avatarService().get(this.conversation, activity.getPixel(56), isCancelled());
-		}
-
-		@Override
-		protected void onPostExecute(Bitmap bitmap) {
-			if (bitmap != null && !isCancelled()) {
-				final ImageView imageView = imageViewReference.get();
-				if (imageView != null) {
-					imageView.setImageBitmap(bitmap);
-					imageView.setBackgroundColor(0x00000000);
-				}
-			}
-		}
-	}
-
-	public interface OnConversationClickListener {
-		void onConversationClick(View view, Conversation conversation);
-	}
+    private XmppActivity activity;
+    private List<Conversation> conversations;
+    private OnConversationClickListener listener;
+
+    public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
+        this.activity = activity;
+        this.conversations = conversations;
+    }
+
+    private 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;
+    }
+
+    @NonNull
+    @Override
+    public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new ConversationViewHolder(DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.conversation_list_row, parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ConversationViewHolder viewHolder, int position) {
+        Conversation conversation = conversations.get(position);
+        if (conversation == null) {
+            return;
+        }
+        CharSequence name = conversation.getName();
+        if (name instanceof Jid) {
+            viewHolder.binding.conversationName.setText(IrregularUnicodeDetector.style(activity, (Jid) name));
+        } else {
+            viewHolder.binding.conversationName.setText(EmojiWrapper.transform(name));
+        }
+
+        if (conversation == ConversationFragment.getConversation(activity)) {
+            viewHolder.binding.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_tertiary));
+        } else {
+            viewHolder.binding.frame.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.color_background_primary));
+        }
+
+        Message message = conversation.getLatestMessage();
+        final int unreadCount = conversation.unreadCount();
+        final boolean isRead = conversation.isRead();
+        final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
+        if (unreadCount > 0) {
+            viewHolder.binding.unreadCount.setVisibility(View.VISIBLE);
+            viewHolder.binding.unreadCount.setUnreadCount(unreadCount);
+        } else {
+            viewHolder.binding.unreadCount.setVisibility(View.GONE);
+        }
+
+        if (isRead) {
+            viewHolder.binding.conversationName.setTypeface(null, Typeface.NORMAL);
+        } else {
+            viewHolder.binding.conversationName.setTypeface(null, Typeface.BOLD);
+        }
+
+        if (draft != null) {
+            viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
+            viewHolder.binding.conversationLastmsg.setText(EmojiWrapper.transform(draft.getMessage()));
+            viewHolder.binding.senderName.setText(R.string.draft);
+            viewHolder.binding.senderName.setVisibility(View.VISIBLE);
+            viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
+            viewHolder.binding.senderName.setTypeface(null, Typeface.ITALIC);
+        } else {
+            final boolean fileAvailable = !message.isDeleted();
+            final boolean showPreviewText;
+            if (fileAvailable && (message.isFileOrImage() || message.treatAsDownloadable() || message.isGeoUri())) {
+                final int imageResource;
+                if (message.isGeoUri()) {
+                    imageResource = activity.getThemeResource(R.attr.ic_attach_location, R.drawable.ic_attach_location);
+                    showPreviewText = false;
+                } else {
+                    final String mime = message.getMimeType();
+                    switch (mime == null ? "" : mime.split("/")[0]) {
+                        case "image":
+                            imageResource = activity.getThemeResource(R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
+                            showPreviewText = false;
+                            break;
+                        case "video":
+                            imageResource = activity.getThemeResource(R.attr.ic_attach_videocam, R.drawable.ic_attach_videocam);
+                            showPreviewText = false;
+                            break;
+                        case "audio":
+                            imageResource = activity.getThemeResource(R.attr.ic_attach_record, R.drawable.ic_attach_record);
+                            showPreviewText = false;
+                            break;
+                        default:
+                            imageResource = activity.getThemeResource(R.attr.ic_attach_document, R.drawable.ic_attach_document);
+                            showPreviewText = true;
+                            break;
+                    }
+                }
+                viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource);
+                viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE);
+            } else {
+                viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
+                showPreviewText = true;
+            }
+            final Pair<CharSequence, Boolean> preview = UIHelper.getMessagePreview(activity, message, viewHolder.binding.conversationLastmsg.getCurrentTextColor());
+            if (showPreviewText) {
+                viewHolder.binding.conversationLastmsg.setText(EmojiWrapper.transform(UIHelper.shorten(preview.first)));
+            } else {
+                viewHolder.binding.conversationLastmsgImg.setContentDescription(preview.first);
+            }
+            viewHolder.binding.conversationLastmsg.setVisibility(showPreviewText ? View.VISIBLE : View.GONE);
+            if (preview.second) {
+                if (isRead) {
+                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.ITALIC);
+                    viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
+                } else {
+                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC);
+                    viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
+                }
+            } else {
+                if (isRead) {
+                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
+                    viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
+                } else {
+                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD);
+                    viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
+                }
+            }
+            if (message.getStatus() == Message.STATUS_RECEIVED) {
+                if (conversation.getMode() == Conversation.MODE_MULTI) {
+                    viewHolder.binding.senderName.setVisibility(View.VISIBLE);
+                    viewHolder.binding.senderName.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
+                } else {
+                    viewHolder.binding.senderName.setVisibility(View.GONE);
+                }
+            } else if (message.getType() != Message.TYPE_STATUS) {
+                viewHolder.binding.senderName.setVisibility(View.VISIBLE);
+                viewHolder.binding.senderName.setText(activity.getString(R.string.me) + ':');
+            } else {
+                viewHolder.binding.senderName.setVisibility(View.GONE);
+            }
+        }
+
+        long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
+        if (muted_till == Long.MAX_VALUE) {
+            viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
+            int ic_notifications_off = activity.getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
+            viewHolder.binding.notificationStatus.setImageResource(ic_notifications_off);
+        } else if (muted_till >= System.currentTimeMillis()) {
+            viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
+            int ic_notifications_paused = activity.getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp);
+            viewHolder.binding.notificationStatus.setImageResource(ic_notifications_paused);
+        } else if (conversation.alwaysNotify()) {
+            viewHolder.binding.notificationStatus.setVisibility(View.GONE);
+        } else {
+            viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
+            int ic_notifications_none = activity.getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
+            viewHolder.binding.notificationStatus.setImageResource(ic_notifications_none);
+        }
+
+        long timestamp;
+        if (draft != null) {
+            timestamp = draft.getTimestamp();
+        } else {
+            timestamp = conversation.getLatestMessage().getTimeSent();
+        }
+        viewHolder.binding.conversationLastupdate.setText(UIHelper.readableTimeDifference(activity, timestamp));
+        loadAvatar(conversation, viewHolder.binding.conversationImage);
+        viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
+    }
+
+    @Override
+    public int getItemCount() {
+        return conversations.size();
+    }
+
+    public void setConversationClickListener(OnConversationClickListener listener) {
+        this.listener = listener;
+    }
+
+    private void loadAvatar(Conversation conversation, ImageView imageView) {
+        if (cancelPotentialWork(conversation, imageView)) {
+            final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
+            if (bm != null) {
+                cancelPotentialWork(conversation, imageView);
+                imageView.setImageBitmap(bm);
+                imageView.setBackgroundColor(0x00000000);
+            } else {
+                imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName().toString()));
+                imageView.setImageDrawable(null);
+                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 void insert(Conversation c, int position) {
+        conversations.add(position, c);
+        notifyDataSetChanged();
+    }
+
+    public void remove(Conversation conversation, int position) {
+        conversations.remove(conversation);
+        notifyItemRemoved(position);
+    }
+
+    public interface OnConversationClickListener {
+        void onConversationClick(View view, Conversation conversation);
+    }
+
+    static class ConversationViewHolder extends RecyclerView.ViewHolder {
+        private final ConversationListRowBinding binding;
+
+        private ConversationViewHolder(ConversationListRowBinding binding) {
+            super(binding.getRoot());
+            this.binding = binding;
+        }
+
+    }
+
+    static class AsyncDrawable extends BitmapDrawable {
+        private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
+
+        AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+            super(res, bitmap);
+            bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
+        }
+
+        BitmapWorkerTask getBitmapWorkerTask() {
+            return bitmapWorkerTaskReference.get();
+        }
+    }
+
+    static class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> {
+        private final WeakReference<ImageView> imageViewReference;
+        private Conversation conversation = null;
+
+        BitmapWorkerTask(ImageView imageView) {
+            imageViewReference = new WeakReference<>(imageView);
+        }
+
+
+        @Override
+        protected Bitmap doInBackground(Conversation... params) {
+            this.conversation = params[0];
+            final XmppActivity activity = XmppActivity.find(imageViewReference);
+            if (activity == null) {
+                return null;
+            }
+            return activity.avatarService().get(this.conversation, activity.getPixel(56), isCancelled());
+        }
+
+        @Override
+        protected void onPostExecute(Bitmap bitmap) {
+            if (bitmap != null && !isCancelled()) {
+                final ImageView imageView = imageViewReference.get();
+                if (imageView != null) {
+                    imageView.setImageBitmap(bitmap);
+                    imageView.setBackgroundColor(0x00000000);
+                }
+            }
+        }
+    }
 }

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

@@ -34,16 +34,13 @@ import rocks.xmpp.addr.Jid;
 public class ListItemAdapter extends ArrayAdapter<ListItem> {
 
 	protected XmppActivity activity;
-	protected boolean showDynamicTags = false;
+	private boolean showDynamicTags = false;
 	private OnTagClickedListener mOnTagClickedListener = null;
-	private View.OnClickListener onTagTvClick = new View.OnClickListener() {
-		@Override
-		public void onClick(View view) {
-			if (view instanceof TextView && mOnTagClickedListener != null) {
-				TextView tv = (TextView) view;
-				final String tag = tv.getText().toString();
-				mOnTagClickedListener.onTagClicked(tag);
-			}
+	private View.OnClickListener onTagTvClick = view -> {
+		if (view instanceof TextView && mOnTagClickedListener != null) {
+			TextView tv = (TextView) view;
+			final String tag = tv.getText().toString();
+			mOnTagClickedListener.onTagClicked(tag);
 		}
 	};
 
@@ -52,7 +49,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 		this.activity = activity;
 	}
 
-	public static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
+	private static boolean cancelPotentialWork(ListItem item, ImageView imageView) {
 		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
 
 		if (bitmapWorkerTask != null) {
@@ -125,7 +122,7 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 		this.mOnTagClickedListener = listener;
 	}
 
-	public void loadAvatar(ListItem item, ImageView imageView) {
+	private void loadAvatar(ListItem item, ImageView imageView) {
 		if (cancelPotentialWork(item, imageView)) {
 			final Bitmap bm = activity.avatarService().get(item, activity.getPixel(48), true);
 			if (bm != null) {
@@ -175,27 +172,31 @@ public class ListItemAdapter extends ArrayAdapter<ListItem> {
 	static class AsyncDrawable extends BitmapDrawable {
 		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
 
-		public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
+		AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
 			super(res, bitmap);
 			bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
 		}
 
-		public BitmapWorkerTask getBitmapWorkerTask() {
+		BitmapWorkerTask getBitmapWorkerTask() {
 			return bitmapWorkerTaskReference.get();
 		}
 	}
 
-	class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
+	private static class BitmapWorkerTask extends AsyncTask<ListItem, Void, Bitmap> {
 		private final WeakReference<ImageView> imageViewReference;
 		private ListItem item = null;
 
-		public BitmapWorkerTask(ImageView imageView) {
+		BitmapWorkerTask(ImageView imageView) {
 			imageViewReference = new WeakReference<>(imageView);
 		}
 
 		@Override
 		protected Bitmap doInBackground(ListItem... params) {
 			this.item = params[0];
+			final XmppActivity activity = XmppActivity.find(imageViewReference);
+			if (activity == null) {
+				return null;
+			}
 			return activity.avatarService().get(this.item, activity.getPixel(48), isCancelled());
 		}
 

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

@@ -88,7 +88,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
         return attr;
     }
 
-    public static void renderPreview(Context context, Attachment attachment, ImageView imageView) {
+    static void renderPreview(Context context, Attachment attachment, ImageView imageView) {
         imageView.setBackgroundColor(StyledAttributes.getColor(context, R.attr.color_background_tertiary));
         imageView.setImageAlpha(Math.round(StyledAttributes.getFloat(context, R.attr.icon_alpha) * 255));
         imageView.setImageDrawable(StyledAttributes.getDrawable(context, getImageAttr(attachment)));
@@ -160,7 +160,7 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
             } else {
                 imageView.setBackgroundColor(0xff333333);
                 imageView.setImageDrawable(null);
-                final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
+                final BitmapWorkerTask task = new BitmapWorkerTask(mediaSize, imageView);
                 final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
                 imageView.setImageDrawable(asyncDrawable);
                 try {
@@ -199,17 +199,23 @@ public class MediaAdapter extends RecyclerView.Adapter<MediaAdapter.MediaViewHol
         }
     }
 
-    class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
+    private static class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
         private final WeakReference<ImageView> imageViewReference;
         private Attachment attachment = null;
+        private final int mediaSize;
 
-        BitmapWorkerTask(ImageView imageView) {
+        BitmapWorkerTask(int mediaSize, ImageView imageView) {
+            this.mediaSize = mediaSize;
             imageViewReference = new WeakReference<>(imageView);
         }
 
         @Override
         protected Bitmap doInBackground(Attachment... params) {
             this.attachment = params[0];
+            final XmppActivity activity = XmppActivity.find(imageViewReference);
+            if (activity == null) {
+                return null;
+            }
             return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, mediaSize, false);
         }
 

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

@@ -157,7 +157,7 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
         }
     }
 
-    class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
+    private static class BitmapWorkerTask extends AsyncTask<Attachment, Void, Bitmap> {
         private final WeakReference<ImageView> imageViewReference;
         private Attachment attachment = null;
 
@@ -167,14 +167,12 @@ public class MediaPreviewAdapter extends RecyclerView.Adapter<MediaPreviewAdapte
 
         @Override
         protected Bitmap doInBackground(Attachment... params) {
-            Activity activity = conversationFragment.getActivity();
-            if (activity instanceof XmppActivity) {
-                final XmppActivity xmppActivity = (XmppActivity) activity;
-                this.attachment = params[0];
-                return xmppActivity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(xmppActivity.getResources().getDimension(R.dimen.media_preview_size)), false);
-            } else {
+            this.attachment = params[0];
+            final XmppActivity activity = XmppActivity.find(imageViewReference);
+            if (activity == null) {
                 return null;
             }
+            return activity.xmppConnectionService.getFileBackend().getPreviewForUri(this.attachment, Math.round(activity.getResources().getDimension(R.dimen.media_preview_size)), false);
         }
 
         @Override

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

@@ -902,7 +902,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
 		ViewUtil.view(activity, file);
 	}
 
-	public void showLocation(Message message) {
+	private void showLocation(Message message) {
 		for (Intent intent : GeoHelper.createGeoIntentsFromMessage(activity, message)) {
 			if (intent.resolveActivity(getContext().getPackageManager()) != null) {
 				getContext().startActivity(intent);
@@ -918,7 +918,7 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
 		this.mUseGreenBackground = p.getBoolean("use_green_background", activity.getResources().getBoolean(R.bool.use_green_background));
 	}
 
-	public void loadAvatar(Message message, ImageView imageView, int size) {
+	private void loadAvatar(Message message, ImageView imageView, int size) {
 		if (cancelPotentialWork(message, imageView)) {
 			final Bitmap bm = activity.avatarService().get(message, size, true);
 			if (bm != null) {
@@ -1037,12 +1037,12 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
 		}
 	}
 
-	class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
+	private static class BitmapWorkerTask extends AsyncTask<Message, Void, Bitmap> {
 		private final WeakReference<ImageView> imageViewReference;
 		private final int size;
 		private Message message = null;
 
-		public BitmapWorkerTask(ImageView imageView, int size) {
+		BitmapWorkerTask(ImageView imageView, int size) {
 			imageViewReference = new WeakReference<>(imageView);
 			this.size = size;
 		}
@@ -1050,6 +1050,10 @@ public class MessageAdapter extends ArrayAdapter<Message> implements CopyTextVie
 		@Override
 		protected Bitmap doInBackground(Message... params) {
 			this.message = params[0];
+			final XmppActivity activity = XmppActivity.find(imageViewReference);
+			if (activity == null) {
+				return null;
+			}
 			return activity.avatarService().get(this.message, size, isCancelled());
 		}
 

src/main/res/layout/conversation_list_row.xml 🔗

@@ -1,128 +1,134 @@
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-             xmlns:app="http://schemas.android.com/apk/res-auto"
-             android:layout_width="fill_parent"
-             android:layout_height="wrap_content"
-             android:descendantFocusability="blocksDescendants">
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <FrameLayout
-        android:id="@+id/frame"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
-        android:background="?attr/color_background_primary">
+        android:descendantFocusability="blocksDescendants">
 
-        <RelativeLayout
+        <FrameLayout
+            android:id="@+id/frame"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
-            android:background="?android:selectableItemBackground"
-            android:orientation="horizontal"
-            android:padding="8dp">
-
-            <com.makeramen.roundedimageview.RoundedImageView
-                android:id="@+id/conversation_image"
-                android:layout_width="56dp"
-                android:layout_height="56dp"
-                android:layout_alignParentLeft="true"
-                android:scaleType="centerCrop"
-                app:riv_corner_radius="2dp"/>
+            android:background="?attr/color_background_primary">
 
             <RelativeLayout
                 android:layout_width="fill_parent"
                 android:layout_height="wrap_content"
-                android:layout_centerVertical="true"
-                android:layout_toRightOf="@+id/conversation_image"
-                android:paddingLeft="@dimen/avatar_item_distance">
+                android:background="?android:selectableItemBackground"
+                android:orientation="horizontal"
+                android:padding="8dp">
 
-                <TextView
-                    android:id="@+id/conversation_name"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_alignLeft="@+id/conversation_lastwrapper"
-                    android:layout_toLeftOf="@+id/conversation_lastupdate"
-                    android:paddingRight="4dp"
-                    android:maxLines="1"
-                    android:ellipsize="end"
-                    android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
+                <com.makeramen.roundedimageview.RoundedImageView
+                    android:id="@+id/conversation_image"
+                    android:layout_width="56dp"
+                    android:layout_height="56dp"
+                    android:layout_alignParentLeft="true"
+                    android:scaleType="centerCrop"
+                    app:riv_corner_radius="2dp" />
 
                 <RelativeLayout
-                    android:id="@+id/conversation_lastwrapper"
-                    android:layout_width="match_parent"
+                    android:layout_width="fill_parent"
                     android:layout_height="wrap_content"
-                    android:layout_below="@id/conversation_name"
-                    android:layout_marginTop="4dp">
+                    android:layout_centerVertical="true"
+                    android:layout_toRightOf="@+id/conversation_image"
+                    android:paddingLeft="@dimen/avatar_item_distance">
 
                     <TextView
-                        android:id="@+id/sender_name"
+                        android:id="@+id/conversation_name"
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
-                        android:minHeight="?attr/IconSize"
-                        android:layout_centerVertical="true"
-                        android:gravity="center_vertical"
-                        android:layout_alignParentLeft="true"
-                        android:text="@string/me"
-                        android:layout_marginRight="?attr/TextSeparation"
-                        android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary"
-                        android:visibility="visible"/>
+                        android:layout_alignLeft="@+id/conversation_lastwrapper"
+                        android:layout_toLeftOf="@+id/conversation_lastupdate"
+                        android:paddingRight="4dp"
+                        android:maxLines="1"
+                        android:ellipsize="end"
+                        android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
 
-                    <LinearLayout android:layout_width="match_parent"
-                                  android:layout_height="wrap_content"
-                                  android:layout_centerVertical="true"
-                                  android:orientation="horizontal"
-                                  android:layout_toRightOf="@id/sender_name"
-                                  android:layout_alignWithParentIfMissing="true"
-                                  android:layout_toLeftOf="@+id/notification_status"
-                                  android:id="@+id/txt_img_wrapper">
-                      <ImageView
-                            android:id="@+id/conversation_lastmsg_img"
-                            android:layout_width="?attr/IconSize"
-                            android:layout_height="?attr/IconSize"
-                            android:layout_marginRight="?attr/TextSeparation"/>
+                    <RelativeLayout
+                        android:id="@+id/conversation_lastwrapper"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_below="@id/conversation_name"
+                        android:layout_marginTop="4dp">
 
                         <TextView
-                            android:id="@+id/conversation_lastmsg"
-                            android:layout_width="match_parent"
+                            android:id="@+id/sender_name"
+                            android:layout_width="wrap_content"
                             android:layout_height="wrap_content"
                             android:minHeight="?attr/IconSize"
+                            android:layout_centerVertical="true"
                             android:gravity="center_vertical"
-                            android:scrollHorizontally="false"
-                            android:maxLines="1"
-                            android:ellipsize="end"
-                            android:textAppearance="@style/TextAppearance.Conversations.Body1"/>
-                    </LinearLayout>
-                    <ImageView
-                        android:visibility="visible"
-                        android:id="@+id/notification_status"
-                        android:layout_width="?attr/IconSize"
-                        android:layout_height="?attr/IconSize"
-                        android:layout_toLeftOf="@+id/unread_count"
-                        android:layout_alignWithParentIfMissing="true"
-                        android:layout_centerVertical="true"
-                        android:layout_marginLeft="4dp"
-                        android:alpha="?attr/icon_alpha"
-                        android:src="?attr/icon_notifications"
-                        />
-                    <eu.siacs.conversations.ui.widget.UnreadCountCustomView
-                        android:id="@+id/unread_count"
-                        android:layout_width="?attr/IconSize"
-                        android:layout_height="?attr/IconSize"
-                        android:layout_centerVertical="true"
-                        android:layout_marginLeft="3dp"
-                        android:layout_marginTop="2dp"
-                        android:layout_marginBottom="1dp"
-                        android:visibility="gone"
-                        android:layout_alignParentRight="true"
-                        app:backgroundColor="?attr/unread_count" />
+                            android:layout_alignParentLeft="true"
+                            android:text="@string/me"
+                            android:layout_marginRight="?attr/TextSeparation"
+                            android:textAppearance="@style/TextAppearance.Conversations.Body1.Secondary"
+                            android:visibility="visible" />
 
-                </RelativeLayout>
+                        <LinearLayout
+                            android:layout_width="match_parent"
+                            android:layout_height="wrap_content"
+                            android:layout_centerVertical="true"
+                            android:orientation="horizontal"
+                            android:layout_toRightOf="@id/sender_name"
+                            android:layout_alignWithParentIfMissing="true"
+                            android:layout_toLeftOf="@+id/notification_status"
+                            android:id="@+id/txt_img_wrapper">
 
-                <TextView
-                    android:id="@+id/conversation_lastupdate"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_alignBaseline="@+id/conversation_name"
-                    android:layout_alignParentRight="true"
-                    android:gravity="right"
-                    android:textAppearance="@style/TextAppearance.Conversations.Caption"/>
+                            <ImageView
+                                android:id="@+id/conversation_lastmsg_img"
+                                android:layout_width="?attr/IconSize"
+                                android:layout_height="?attr/IconSize"
+                                android:layout_marginRight="?attr/TextSeparation" />
+
+                            <TextView
+                                android:id="@+id/conversation_lastmsg"
+                                android:layout_width="match_parent"
+                                android:layout_height="wrap_content"
+                                android:minHeight="?attr/IconSize"
+                                android:gravity="center_vertical"
+                                android:scrollHorizontally="false"
+                                android:maxLines="1"
+                                android:ellipsize="end"
+                                android:textAppearance="@style/TextAppearance.Conversations.Body1" />
+                        </LinearLayout>
+
+                        <ImageView
+                            android:visibility="visible"
+                            android:id="@+id/notification_status"
+                            android:layout_width="?attr/IconSize"
+                            android:layout_height="?attr/IconSize"
+                            android:layout_toLeftOf="@+id/unread_count"
+                            android:layout_alignWithParentIfMissing="true"
+                            android:layout_centerVertical="true"
+                            android:layout_marginLeft="4dp"
+                            android:alpha="?attr/icon_alpha"
+                            android:src="?attr/icon_notifications" />
+
+                        <eu.siacs.conversations.ui.widget.UnreadCountCustomView
+                            android:id="@+id/unread_count"
+                            android:layout_width="?attr/IconSize"
+                            android:layout_height="?attr/IconSize"
+                            android:layout_centerVertical="true"
+                            android:layout_marginLeft="3dp"
+                            android:layout_marginTop="2dp"
+                            android:layout_marginBottom="1dp"
+                            android:visibility="gone"
+                            android:layout_alignParentRight="true"
+                            app:backgroundColor="?attr/unread_count" />
+
+                    </RelativeLayout>
+
+                    <TextView
+                        android:id="@+id/conversation_lastupdate"
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_alignBaseline="@+id/conversation_name"
+                        android:layout_alignParentRight="true"
+                        android:gravity="right"
+                        android:textAppearance="@style/TextAppearance.Conversations.Caption" />
+                </RelativeLayout>
             </RelativeLayout>
-        </RelativeLayout>
+        </FrameLayout>
     </FrameLayout>
-</FrameLayout>
+</layout>