1package eu.siacs.conversations.ui.adapter;
2
3import android.graphics.Typeface;
4import android.util.Pair;
5import android.view.LayoutInflater;
6import android.view.View;
7import android.view.ViewGroup;
8
9import androidx.annotation.NonNull;
10import androidx.databinding.DataBindingUtil;
11import androidx.recyclerview.widget.RecyclerView;
12
13import com.google.common.base.Optional;
14import com.google.common.base.Strings;
15
16import java.util.List;
17
18import eu.siacs.conversations.R;
19import eu.siacs.conversations.databinding.ConversationListRowBinding;
20import eu.siacs.conversations.entities.Conversation;
21import eu.siacs.conversations.entities.Conversational;
22import eu.siacs.conversations.entities.Message;
23import eu.siacs.conversations.ui.ConversationFragment;
24import eu.siacs.conversations.ui.XmppActivity;
25import eu.siacs.conversations.ui.util.AvatarWorkerTask;
26import eu.siacs.conversations.ui.util.StyledAttributes;
27import eu.siacs.conversations.utils.IrregularUnicodeDetector;
28import eu.siacs.conversations.utils.MimeUtils;
29import eu.siacs.conversations.utils.UIHelper;
30import eu.siacs.conversations.xmpp.Jid;
31import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
32
33public class ConversationAdapter
34 extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {
35
36 private final XmppActivity activity;
37 private final List<Conversation> conversations;
38 private OnConversationClickListener listener;
39
40 public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
41 this.activity = activity;
42 this.conversations = conversations;
43 }
44
45 @NonNull
46 @Override
47 public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
48 return new ConversationViewHolder(
49 DataBindingUtil.inflate(
50 LayoutInflater.from(parent.getContext()),
51 R.layout.conversation_list_row,
52 parent,
53 false));
54 }
55
56 @Override
57 public void onBindViewHolder(@NonNull ConversationViewHolder viewHolder, int position) {
58 Conversation conversation = conversations.get(position);
59 if (conversation == null) {
60 return;
61 }
62 CharSequence name = conversation.getName();
63 if (name instanceof Jid) {
64 viewHolder.binding.conversationName.setText(
65 IrregularUnicodeDetector.style(activity, (Jid) name));
66 } else {
67 viewHolder.binding.conversationName.setText(name);
68 }
69
70 if (conversation == ConversationFragment.getConversation(activity)) {
71 viewHolder.binding.frame.setBackgroundColor(
72 StyledAttributes.getColor(activity, R.attr.color_background_tertiary));
73 } else {
74 viewHolder.binding.frame.setBackgroundColor(
75 StyledAttributes.getColor(activity, R.attr.color_background_primary));
76 }
77
78 Message message = conversation.getLatestMessage();
79 final int unreadCount = conversation.unreadCount();
80 final boolean isRead = conversation.isRead();
81 final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
82 if (unreadCount > 0) {
83 viewHolder.binding.unreadCount.setVisibility(View.VISIBLE);
84 viewHolder.binding.unreadCount.setUnreadCount(unreadCount);
85 } else {
86 viewHolder.binding.unreadCount.setVisibility(View.GONE);
87 }
88
89 if (isRead) {
90 viewHolder.binding.conversationName.setTypeface(null, Typeface.NORMAL);
91 } else {
92 viewHolder.binding.conversationName.setTypeface(null, Typeface.BOLD);
93 }
94
95 if (draft != null) {
96 viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
97 viewHolder.binding.conversationLastmsg.setText(draft.getMessage());
98 viewHolder.binding.senderName.setText(R.string.draft);
99 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
100 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
101 viewHolder.binding.senderName.setTypeface(null, Typeface.ITALIC);
102 } else {
103 final boolean fileAvailable = !message.isDeleted();
104 final boolean showPreviewText;
105 if (fileAvailable
106 && (message.isFileOrImage()
107 || message.treatAsDownloadable()
108 || message.isGeoUri())) {
109 final int imageResource;
110 if (message.isGeoUri()) {
111 imageResource =
112 activity.getThemeResource(
113 R.attr.ic_attach_location, R.drawable.ic_attach_location);
114 showPreviewText = false;
115 } else {
116 // TODO move this into static MediaPreview method and use same icons as in
117 // MediaAdapter
118 final String mime = message.getMimeType();
119 if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
120 final Message.FileParams fileParams = message.getFileParams();
121 if (fileParams.width > 0 && fileParams.height > 0) {
122 imageResource =
123 activity.getThemeResource(
124 R.attr.ic_attach_videocam,
125 R.drawable.ic_attach_videocam);
126 showPreviewText = false;
127 } else if (fileParams.runtime > 0) {
128 imageResource =
129 activity.getThemeResource(
130 R.attr.ic_attach_record, R.drawable.ic_attach_record);
131 showPreviewText = false;
132 } else {
133 imageResource =
134 activity.getThemeResource(
135 R.attr.ic_attach_document,
136 R.drawable.ic_attach_document);
137 showPreviewText = true;
138 }
139 } else {
140 switch (Strings.nullToEmpty(mime).split("/")[0]) {
141 case "image":
142 imageResource =
143 activity.getThemeResource(
144 R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
145 showPreviewText = false;
146 break;
147 case "video":
148 imageResource =
149 activity.getThemeResource(
150 R.attr.ic_attach_videocam,
151 R.drawable.ic_attach_videocam);
152 showPreviewText = false;
153 break;
154 case "audio":
155 imageResource =
156 activity.getThemeResource(
157 R.attr.ic_attach_record,
158 R.drawable.ic_attach_record);
159 showPreviewText = false;
160 break;
161 default:
162 imageResource =
163 activity.getThemeResource(
164 R.attr.ic_attach_document,
165 R.drawable.ic_attach_document);
166 showPreviewText = true;
167 break;
168 }
169 }
170 }
171 viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource);
172 viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE);
173 } else {
174 viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
175 showPreviewText = true;
176 }
177 final Pair<CharSequence, Boolean> preview =
178 UIHelper.getMessagePreview(
179 activity,
180 message,
181 viewHolder.binding.conversationLastmsg.getCurrentTextColor());
182 if (showPreviewText) {
183 viewHolder.binding.conversationLastmsg.setText(UIHelper.shorten(preview.first));
184 } else {
185 viewHolder.binding.conversationLastmsgImg.setContentDescription(preview.first);
186 }
187 viewHolder.binding.conversationLastmsg.setVisibility(
188 showPreviewText ? View.VISIBLE : View.GONE);
189 if (preview.second) {
190 if (isRead) {
191 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.ITALIC);
192 viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
193 } else {
194 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC);
195 viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
196 }
197 } else {
198 if (isRead) {
199 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
200 viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
201 } else {
202 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD);
203 viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
204 }
205 }
206 if (message.getStatus() == Message.STATUS_RECEIVED) {
207 if (conversation.getMode() == Conversation.MODE_MULTI) {
208 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
209 viewHolder.binding.senderName.setText(
210 UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
211 } else {
212 viewHolder.binding.senderName.setVisibility(View.GONE);
213 }
214 } else if (message.getType() != Message.TYPE_STATUS) {
215 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
216 viewHolder.binding.senderName.setText(activity.getString(R.string.me) + ':');
217 } else {
218 viewHolder.binding.senderName.setVisibility(View.GONE);
219 }
220 }
221
222 final Optional<OngoingRtpSession> ongoingCall;
223 if (conversation.getMode() == Conversational.MODE_MULTI) {
224 ongoingCall = Optional.absent();
225 } else {
226 ongoingCall =
227 activity.xmppConnectionService
228 .getJingleConnectionManager()
229 .getOngoingRtpConnection(conversation.getContact());
230 }
231
232 if (ongoingCall.isPresent()) {
233 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
234 final int ic_ongoing_call =
235 activity.getThemeResource(
236 R.attr.ic_ongoing_call_hint, R.drawable.ic_phone_in_talk_black_18dp);
237 viewHolder.binding.notificationStatus.setImageResource(ic_ongoing_call);
238 } else {
239 final long muted_till =
240 conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
241 if (muted_till == Long.MAX_VALUE) {
242 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
243 int ic_notifications_off =
244 activity.getThemeResource(
245 R.attr.icon_notifications_off,
246 R.drawable.ic_notifications_off_black_24dp);
247 viewHolder.binding.notificationStatus.setImageResource(ic_notifications_off);
248 } else if (muted_till >= System.currentTimeMillis()) {
249 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
250 int ic_notifications_paused =
251 activity.getThemeResource(
252 R.attr.icon_notifications_paused,
253 R.drawable.ic_notifications_paused_black_24dp);
254 viewHolder.binding.notificationStatus.setImageResource(ic_notifications_paused);
255 } else if (conversation.alwaysNotify()) {
256 viewHolder.binding.notificationStatus.setVisibility(View.GONE);
257 } else {
258 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
259 int ic_notifications_none =
260 activity.getThemeResource(
261 R.attr.icon_notifications_none,
262 R.drawable.ic_notifications_none_black_24dp);
263 viewHolder.binding.notificationStatus.setImageResource(ic_notifications_none);
264 }
265 }
266
267 long timestamp;
268 if (draft != null) {
269 timestamp = draft.getTimestamp();
270 } else {
271 timestamp = conversation.getLatestMessage().getTimeSent();
272 }
273 viewHolder.binding.pinnedOnTop.setVisibility(
274 conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)
275 ? View.VISIBLE
276 : View.GONE);
277 viewHolder.binding.conversationLastupdate.setText(
278 UIHelper.readableTimeDifference(activity, timestamp));
279 AvatarWorkerTask.loadAvatar(
280 conversation,
281 viewHolder.binding.conversationImage,
282 R.dimen.avatar_on_conversation_overview);
283 viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
284 }
285
286 @Override
287 public int getItemCount() {
288 return conversations.size();
289 }
290
291 public void setConversationClickListener(OnConversationClickListener listener) {
292 this.listener = listener;
293 }
294
295 public void insert(Conversation c, int position) {
296 conversations.add(position, c);
297 notifyDataSetChanged();
298 }
299
300 public void remove(Conversation conversation, int position) {
301 conversations.remove(conversation);
302 notifyItemRemoved(position);
303 }
304
305 public interface OnConversationClickListener {
306 void onConversationClick(View view, Conversation conversation);
307 }
308
309 static class ConversationViewHolder extends RecyclerView.ViewHolder {
310 private final ConversationListRowBinding binding;
311
312 private ConversationViewHolder(ConversationListRowBinding binding) {
313 super(binding.getRoot());
314 this.binding = binding;
315 }
316 }
317}