ConversationAdapter.java

  1package eu.siacs.conversations.ui.adapter;
  2
  3import android.content.Context;
  4import android.content.res.Resources;
  5import android.graphics.Bitmap;
  6import android.graphics.Typeface;
  7import android.graphics.drawable.BitmapDrawable;
  8import android.graphics.drawable.Drawable;
  9import android.os.AsyncTask;
 10import android.util.Log;
 11import android.util.Pair;
 12import android.view.LayoutInflater;
 13import android.view.View;
 14import android.view.ViewGroup;
 15import android.widget.ArrayAdapter;
 16import android.widget.ImageView;
 17import android.widget.TextView;
 18
 19import java.lang.ref.WeakReference;
 20import java.util.List;
 21import java.util.concurrent.RejectedExecutionException;
 22
 23import eu.siacs.conversations.Config;
 24import eu.siacs.conversations.R;
 25import eu.siacs.conversations.entities.Conversation;
 26import eu.siacs.conversations.entities.Message;
 27import eu.siacs.conversations.entities.Transferable;
 28import eu.siacs.conversations.ui.ConversationActivity;
 29import eu.siacs.conversations.ui.ConversationFragment;
 30import eu.siacs.conversations.ui.XmppActivity;
 31import eu.siacs.conversations.ui.widget.UnreadCountCustomView;
 32import eu.siacs.conversations.utils.EmojiWrapper;
 33import eu.siacs.conversations.utils.UIHelper;
 34
 35public class ConversationAdapter extends ArrayAdapter<Conversation> {
 36
 37	private XmppActivity activity;
 38	private Conversation selectedConversation = null;
 39
 40	public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
 41		super(activity, 0, conversations);
 42		this.activity = activity;
 43	}
 44
 45	@Override
 46	public View getView(int position, View view, ViewGroup parent) {
 47		if (view == null) {
 48			LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 49			view = inflater.inflate(R.layout.conversation_list_row,parent, false);
 50		}
 51		Conversation conversation = getItem(position);
 52		if (this.activity instanceof XmppActivity) {
 53			View swipeableItem = view.findViewById(R.id.swipeable_item);
 54			int c = conversation == selectedConversation ? this.activity.getSecondaryBackgroundColor() : this.activity.getPrimaryBackgroundColor();
 55			swipeableItem.setBackgroundColor(c);
 56		}
 57		ViewHolder viewHolder = ViewHolder.get(view);
 58		if (conversation.getMode() == Conversation.MODE_SINGLE || activity.useSubjectToIdentifyConference()) {
 59			viewHolder.name.setText(EmojiWrapper.transform(conversation.getName()));
 60		} else {
 61			viewHolder.name.setText(conversation.getJid().toBareJid().toString());
 62		}
 63
 64		Message message = conversation.getLatestMessage();
 65		int unreadCount = conversation.unreadCount();
 66		if (unreadCount > 0) {
 67			viewHolder.unreadCount.setVisibility(View.VISIBLE);
 68			viewHolder.unreadCount.setUnreadCount(unreadCount);
 69		} else {
 70			viewHolder.unreadCount.setVisibility(View.GONE);
 71		}
 72
 73		if (!conversation.isRead()) {
 74			viewHolder.name.setTypeface(null, Typeface.BOLD);
 75		} else {
 76			viewHolder.name.setTypeface(null, Typeface.NORMAL);
 77		}
 78
 79		final boolean fileAvailable = message.getTransferable() == null || message.getTransferable().getStatus() != Transferable.STATUS_DELETED;
 80		final boolean showPreviewText;
 81		if (fileAvailable && (message.isFileOrImage() || message.treatAsDownloadable() || message.isGeoUri())) {
 82			final int imageResource;
 83			if (message.isGeoUri()) {
 84				imageResource = activity.getThemeResource(R.attr.ic_attach_location, R.drawable.ic_attach_location);
 85				showPreviewText = false;
 86			} else {
 87				final String mime = message.getMimeType();
 88				switch (mime == null ? "" : mime.split("/")[0]) {
 89					case "image":
 90						imageResource = activity.getThemeResource(R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
 91						showPreviewText = false;
 92						break;
 93					case "video":
 94						imageResource = activity.getThemeResource(R.attr.ic_attach_videocam, R.drawable.ic_attach_videocam);
 95						showPreviewText = false;
 96						break;
 97					case "audio":
 98						imageResource = activity.getThemeResource(R.attr.ic_attach_record, R.drawable.ic_attach_record);
 99						showPreviewText = false;
100						break;
101					default:
102						imageResource = activity.getThemeResource(R.attr.ic_attach_document, R.drawable.ic_attach_document);
103						showPreviewText = true;
104						break;
105				}
106			}
107			viewHolder.lastMessageIcon.setImageResource(imageResource);
108			viewHolder.lastMessageIcon.setVisibility(View.VISIBLE);
109		} else {
110			viewHolder.lastMessageIcon.setVisibility(View.GONE);
111			showPreviewText = true;
112		}
113		final Pair<String,Boolean> preview = UIHelper.getMessagePreview(activity,message);
114		if (showPreviewText) {
115			viewHolder.lastMessage.setText(EmojiWrapper.transform(preview.first));
116		} else {
117			viewHolder.lastMessageIcon.setContentDescription(preview.first);
118		}
119		viewHolder.lastMessage.setVisibility(showPreviewText ? View.VISIBLE : View.GONE);
120		if (preview.second) {
121			if (conversation.isRead()) {
122				viewHolder.lastMessage.setTypeface(null, Typeface.ITALIC);
123				viewHolder.sender.setTypeface(null, Typeface.NORMAL);
124			} else {
125				viewHolder.lastMessage.setTypeface(null,Typeface.BOLD_ITALIC);
126				viewHolder.sender.setTypeface(null,Typeface.BOLD);
127			}
128		} else {
129			if (conversation.isRead()) {
130				viewHolder.lastMessage.setTypeface(null,Typeface.NORMAL);
131				viewHolder.sender.setTypeface(null,Typeface.NORMAL);
132			} else {
133				viewHolder.lastMessage.setTypeface(null,Typeface.BOLD);
134				viewHolder.sender.setTypeface(null,Typeface.BOLD);
135			}
136		}
137		if (message.getStatus() == Message.STATUS_RECEIVED) {
138			if (conversation.getMode() == Conversation.MODE_MULTI) {
139				viewHolder.sender.setVisibility(View.VISIBLE);
140				viewHolder.sender.setText(UIHelper.getMessageDisplayName(message).split("\\s+")[0]+':');
141			} else {
142				viewHolder.sender.setVisibility(View.GONE);
143			}
144		} else if (message.getType() != Message.TYPE_STATUS) {
145			viewHolder.sender.setVisibility(View.VISIBLE);
146			viewHolder.sender.setText(activity.getString(R.string.me)+':');
147		} else {
148			viewHolder.sender.setVisibility(View.GONE);
149		}
150
151		long muted_till = conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL,0);
152		if (muted_till == Long.MAX_VALUE) {
153			viewHolder.notificationIcon.setVisibility(View.VISIBLE);
154			int ic_notifications_off = 	  activity.getThemeResource(R.attr.icon_notifications_off, R.drawable.ic_notifications_off_black_24dp);
155			viewHolder.notificationIcon.setImageResource(ic_notifications_off);
156		} else if (muted_till >= System.currentTimeMillis()) {
157			viewHolder.notificationIcon.setVisibility(View.VISIBLE);
158			int ic_notifications_paused = activity.getThemeResource(R.attr.icon_notifications_paused, R.drawable.ic_notifications_paused_black_24dp);
159			viewHolder.notificationIcon.setImageResource(ic_notifications_paused);
160		} else if (conversation.alwaysNotify()) {
161			viewHolder.notificationIcon.setVisibility(View.GONE);
162		} else {
163			viewHolder.notificationIcon.setVisibility(View.VISIBLE);
164			int ic_notifications_none =	  activity.getThemeResource(R.attr.icon_notifications_none, R.drawable.ic_notifications_none_black_24dp);
165			viewHolder.notificationIcon.setImageResource(ic_notifications_none);
166		}
167
168		viewHolder.timestamp.setText(UIHelper.readableTimeDifference(activity,conversation.getLatestMessage().getTimeSent()));
169		loadAvatar(conversation, viewHolder.avatar);
170
171		return view;
172	}
173
174	@Override
175	public void notifyDataSetChanged() {
176		this.selectedConversation = ConversationFragment.getConversation(activity);
177		Log.d(Config.LOGTAG,"notify data set changed");
178		if (this.selectedConversation == null) {
179			Log.d(Config.LOGTAG,"selected conversation is null");
180		}
181		super.notifyDataSetChanged();
182	}
183
184	public static class ViewHolder {
185		private TextView name;
186		private TextView lastMessage;
187		private ImageView lastMessageIcon;
188		private TextView sender;
189		private TextView timestamp;
190		private ImageView notificationIcon;
191		private UnreadCountCustomView unreadCount;
192		private ImageView avatar;
193
194		private ViewHolder() {
195
196		}
197
198		public static ViewHolder get(View layout) {
199			ViewHolder viewHolder = (ViewHolder) layout.getTag();
200			if (viewHolder == null) {
201				viewHolder = new ViewHolder();
202				viewHolder.name = layout.findViewById(R.id.conversation_name);
203				viewHolder.lastMessage = layout.findViewById(R.id.conversation_lastmsg);
204				viewHolder.lastMessageIcon = layout.findViewById(R.id.conversation_lastmsg_img);
205				viewHolder.timestamp = layout.findViewById(R.id.conversation_lastupdate);
206				viewHolder.sender = layout.findViewById(R.id.sender_name);
207				viewHolder.notificationIcon = layout.findViewById(R.id.notification_status);
208				viewHolder.unreadCount = layout.findViewById(R.id.unread_count);
209				viewHolder.avatar = layout.findViewById(R.id.conversation_image);
210				layout.setTag(viewHolder);
211			}
212			return viewHolder;
213		}
214	}
215
216	class BitmapWorkerTask extends AsyncTask<Conversation, Void, Bitmap> {
217		private final WeakReference<ImageView> imageViewReference;
218		private Conversation conversation = null;
219
220		public BitmapWorkerTask(ImageView imageView) {
221			imageViewReference = new WeakReference<>(imageView);
222		}
223
224		@Override
225		protected Bitmap doInBackground(Conversation... params) {
226			this.conversation = params[0];
227			return activity.avatarService().get(this.conversation, activity.getPixel(56), isCancelled());
228		}
229
230		@Override
231		protected void onPostExecute(Bitmap bitmap) {
232			if (bitmap != null && !isCancelled()) {
233				final ImageView imageView = imageViewReference.get();
234				if (imageView != null) {
235					imageView.setImageBitmap(bitmap);
236					imageView.setBackgroundColor(0x00000000);
237				}
238			}
239		}
240	}
241
242	public void loadAvatar(Conversation conversation, ImageView imageView) {
243		if (cancelPotentialWork(conversation, imageView)) {
244			final Bitmap bm = activity.avatarService().get(conversation, activity.getPixel(56), true);
245			if (bm != null) {
246				cancelPotentialWork(conversation, imageView);
247				imageView.setImageBitmap(bm);
248				imageView.setBackgroundColor(0x00000000);
249			} else {
250				imageView.setBackgroundColor(UIHelper.getColorForName(conversation.getName()));
251				imageView.setImageDrawable(null);
252				final BitmapWorkerTask task = new BitmapWorkerTask(imageView);
253				final AsyncDrawable asyncDrawable = new AsyncDrawable(activity.getResources(), null, task);
254				imageView.setImageDrawable(asyncDrawable);
255				try {
256					task.execute(conversation);
257				} catch (final RejectedExecutionException ignored) {
258				}
259			}
260		}
261	}
262
263	public static boolean cancelPotentialWork(Conversation conversation, ImageView imageView) {
264		final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView);
265
266		if (bitmapWorkerTask != null) {
267			final Conversation oldConversation = bitmapWorkerTask.conversation;
268			if (oldConversation == null || conversation != oldConversation) {
269				bitmapWorkerTask.cancel(true);
270			} else {
271				return false;
272			}
273		}
274		return true;
275	}
276
277	private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) {
278		if (imageView != null) {
279			final Drawable drawable = imageView.getDrawable();
280			if (drawable instanceof AsyncDrawable) {
281				final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable;
282				return asyncDrawable.getBitmapWorkerTask();
283			}
284		}
285		return null;
286	}
287
288	static class AsyncDrawable extends BitmapDrawable {
289		private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference;
290
291		public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) {
292			super(res, bitmap);
293			bitmapWorkerTaskReference = new WeakReference<>(bitmapWorkerTask);
294		}
295
296		public BitmapWorkerTask getBitmapWorkerTask() {
297			return bitmapWorkerTaskReference.get();
298		}
299	}
300}