1package eu.siacs.conversations.ui.adapter;
2
3import android.content.res.ColorStateList;
4import android.graphics.Typeface;
5import android.util.Pair;
6import android.view.LayoutInflater;
7import android.view.View;
8import android.view.ViewGroup;
9
10import androidx.annotation.DrawableRes;
11import androidx.annotation.NonNull;
12import androidx.core.widget.ImageViewCompat;
13import androidx.databinding.DataBindingUtil;
14import androidx.recyclerview.widget.RecyclerView;
15
16import com.google.android.material.color.MaterialColors;
17import com.google.common.base.Optional;
18
19import eu.siacs.conversations.R;
20import eu.siacs.conversations.databinding.ItemConversationBinding;
21import eu.siacs.conversations.entities.Conversation;
22import eu.siacs.conversations.entities.Conversational;
23import eu.siacs.conversations.entities.Message;
24import eu.siacs.conversations.ui.ConversationFragment;
25import eu.siacs.conversations.ui.XmppActivity;
26import eu.siacs.conversations.ui.util.Attachment;
27import eu.siacs.conversations.ui.util.AvatarWorkerTask;
28import eu.siacs.conversations.utils.IrregularUnicodeDetector;
29import eu.siacs.conversations.utils.UIHelper;
30import eu.siacs.conversations.xmpp.Jid;
31import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
32
33import java.util.List;
34
35public class ConversationAdapter
36 extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {
37
38 private final XmppActivity activity;
39 private final List<Conversation> conversations;
40 private OnConversationClickListener listener;
41
42 public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
43 this.activity = activity;
44 this.conversations = conversations;
45 }
46
47 @NonNull
48 @Override
49 public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
50 return new ConversationViewHolder(
51 DataBindingUtil.inflate(
52 LayoutInflater.from(parent.getContext()),
53 R.layout.item_conversation,
54 parent,
55 false));
56 }
57
58 @Override
59 public void onBindViewHolder(@NonNull ConversationViewHolder viewHolder, int position) {
60 Conversation conversation = conversations.get(position);
61 if (conversation == null) {
62 return;
63 }
64 CharSequence name = conversation.getName();
65 if (name instanceof Jid) {
66 viewHolder.binding.conversationName.setText(
67 IrregularUnicodeDetector.style(activity, (Jid) name));
68 } else {
69 viewHolder.binding.conversationName.setText(name);
70 }
71
72 if (conversation == ConversationFragment.getConversation(activity)) {
73 viewHolder.binding.frame.setBackgroundResource(
74 R.drawable.background_selected_item_conversation);
75 // viewHolder.binding.frame.setBackgroundColor(MaterialColors.getColor(viewHolder.binding.frame, com.google.android.material.R.attr.colorSurfaceDim));
76 } else {
77 viewHolder.binding.frame.setBackgroundColor(
78 MaterialColors.getColor(
79 viewHolder.binding.frame,
80 com.google.android.material.R.attr.colorSurface));
81 }
82
83 final Message message = conversation.getLatestMessage();
84 final int status = message.getStatus();
85 final int unreadCount = conversation.unreadCount();
86 final boolean isRead = conversation.isRead();
87 final @DrawableRes Integer messageStatusDrawable =
88 MessageAdapter.getMessageStatusAsDrawable(message, status);
89 if (message.getType() == Message.TYPE_RTP_SESSION) {
90 viewHolder.binding.messageStatus.setVisibility(View.GONE);
91 } else if (messageStatusDrawable == null) {
92 if (status <= Message.STATUS_RECEIVED) {
93 viewHolder.binding.messageStatus.setVisibility(View.GONE);
94 } else {
95 viewHolder.binding.messageStatus.setVisibility(View.INVISIBLE);
96 }
97 } else {
98 viewHolder.binding.messageStatus.setImageResource(messageStatusDrawable);
99 if (status == Message.STATUS_SEND_DISPLAYED) {
100 ImageViewCompat.setImageTintList(
101 viewHolder.binding.messageStatus,
102 ColorStateList.valueOf(
103 MaterialColors.getColor(
104 viewHolder.binding.messageStatus,
105 com.google.android.material.R.attr.colorPrimary)));
106 } else {
107 ImageViewCompat.setImageTintList(
108 viewHolder.binding.messageStatus,
109 ColorStateList.valueOf(
110 MaterialColors.getColor(
111 viewHolder.binding.messageStatus,
112 com.google.android.material.R.attr.colorControlNormal)));
113 }
114 viewHolder.binding.messageStatus.setVisibility(View.VISIBLE);
115 }
116 final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
117 if (unreadCount > 0) {
118 viewHolder.binding.unreadCount.setVisibility(View.VISIBLE);
119 viewHolder.binding.unreadCount.setUnreadCount(unreadCount);
120 } else {
121 viewHolder.binding.unreadCount.setVisibility(View.GONE);
122 }
123
124 if (isRead) {
125 viewHolder.binding.conversationName.setTypeface(null, Typeface.NORMAL);
126 } else {
127 viewHolder.binding.conversationName.setTypeface(null, Typeface.BOLD);
128 }
129
130 if (draft != null) {
131 viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
132 viewHolder.binding.conversationLastmsg.setText(draft.getMessage());
133 viewHolder.binding.senderName.setText(R.string.draft);
134 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
135 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
136 viewHolder.binding.senderName.setTypeface(null, Typeface.ITALIC);
137 } else {
138 final boolean fileAvailable = !message.isDeleted();
139 final boolean showPreviewText;
140 if (fileAvailable
141 && (message.isFileOrImage()
142 || message.treatAsDownloadable()
143 || message.isGeoUri())) {
144 final var attachment = Attachment.of(message);
145 final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment);
146 showPreviewText = false;
147 viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource);
148 viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE);
149 } else {
150 viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
151 showPreviewText = true;
152 }
153 final Pair<CharSequence, Boolean> preview =
154 UIHelper.getMessagePreview(
155 activity,
156 message,
157 viewHolder.binding.conversationLastmsg.getCurrentTextColor());
158 if (showPreviewText) {
159 viewHolder.binding.conversationLastmsg.setText(UIHelper.shorten(preview.first));
160 } else {
161 viewHolder.binding.conversationLastmsgImg.setContentDescription(preview.first);
162 }
163 viewHolder.binding.conversationLastmsg.setVisibility(
164 showPreviewText ? View.VISIBLE : View.GONE);
165 if (preview.second) {
166 if (isRead) {
167 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.ITALIC);
168 viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
169 } else {
170 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC);
171 viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
172 }
173 } else {
174 if (isRead) {
175 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
176 viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
177 } else {
178 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD);
179 viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
180 }
181 }
182 if (status == Message.STATUS_RECEIVED) {
183 if (conversation.getMode() == Conversation.MODE_MULTI) {
184 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
185 viewHolder.binding.senderName.setText(
186 UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
187 } else {
188 viewHolder.binding.senderName.setVisibility(View.GONE);
189 }
190 } else if (message.getType() != Message.TYPE_STATUS) {
191 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
192 viewHolder.binding.senderName.setText(activity.getString(R.string.me) + ':');
193 } else {
194 viewHolder.binding.senderName.setVisibility(View.GONE);
195 }
196 }
197
198 final Optional<OngoingRtpSession> ongoingCall;
199 if (conversation.getMode() == Conversational.MODE_MULTI) {
200 ongoingCall = Optional.absent();
201 } else {
202 ongoingCall =
203 activity.xmppConnectionService
204 .getJingleConnectionManager()
205 .getOngoingRtpConnection(conversation.getContact());
206 }
207
208 if (ongoingCall.isPresent()) {
209 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
210 viewHolder.binding.notificationStatus.setImageResource(
211 R.drawable.ic_phone_in_talk_24dp);
212 } else {
213 final long muted_till =
214 conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
215 if (muted_till == Long.MAX_VALUE) {
216 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
217 viewHolder.binding.notificationStatus.setImageResource(
218 R.drawable.ic_notifications_off_24dp);
219 } else if (muted_till >= System.currentTimeMillis()) {
220 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
221 viewHolder.binding.notificationStatus.setImageResource(
222 R.drawable.ic_notifications_paused_24dp);
223 } else if (conversation.alwaysNotify()) {
224 viewHolder.binding.notificationStatus.setVisibility(View.GONE);
225 } else {
226 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
227 viewHolder.binding.notificationStatus.setImageResource(
228 R.drawable.ic_notifications_none_24dp);
229 }
230 }
231
232 long timestamp;
233 if (draft != null) {
234 timestamp = draft.getTimestamp();
235 } else {
236 timestamp = conversation.getLatestMessage().getTimeSent();
237 }
238 viewHolder.binding.pinnedOnTop.setVisibility(
239 conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)
240 ? View.VISIBLE
241 : View.GONE);
242 viewHolder.binding.conversationLastupdate.setText(
243 UIHelper.readableTimeDifference(activity, timestamp));
244 AvatarWorkerTask.loadAvatar(
245 conversation,
246 viewHolder.binding.conversationImage,
247 R.dimen.avatar_on_conversation_overview);
248 viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
249 }
250
251 @Override
252 public int getItemCount() {
253 return conversations.size();
254 }
255
256 public void setConversationClickListener(OnConversationClickListener listener) {
257 this.listener = listener;
258 }
259
260 public void insert(Conversation c, int position) {
261 conversations.add(position, c);
262 notifyDataSetChanged();
263 }
264
265 public void remove(Conversation conversation, int position) {
266 conversations.remove(conversation);
267 notifyItemRemoved(position);
268 }
269
270 public interface OnConversationClickListener {
271 void onConversationClick(View view, Conversation conversation);
272 }
273
274 static class ConversationViewHolder extends RecyclerView.ViewHolder {
275 private final ItemConversationBinding binding;
276
277 private ConversationViewHolder(final ItemConversationBinding binding) {
278 super(binding.getRoot());
279 this.binding = binding;
280 }
281 }
282}