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