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 (messageStatusDrawable == null) {
90 if (status <= Message.STATUS_RECEIVED) {
91 viewHolder.binding.messageStatus.setVisibility(View.GONE);
92 } else {
93 viewHolder.binding.messageStatus.setVisibility(View.INVISIBLE);
94 }
95 } else {
96 viewHolder.binding.messageStatus.setImageResource(messageStatusDrawable);
97 if (status == Message.STATUS_SEND_DISPLAYED) {
98 ImageViewCompat.setImageTintList(
99 viewHolder.binding.messageStatus,
100 ColorStateList.valueOf(
101 MaterialColors.getColor(
102 viewHolder.binding.messageStatus,
103 com.google.android.material.R.attr.colorPrimary)));
104 } else {
105 ImageViewCompat.setImageTintList(
106 viewHolder.binding.messageStatus,
107 ColorStateList.valueOf(
108 MaterialColors.getColor(
109 viewHolder.binding.messageStatus,
110 com.google.android.material.R.attr.colorControlNormal)));
111 }
112 viewHolder.binding.messageStatus.setVisibility(View.VISIBLE);
113 }
114 final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
115 if (unreadCount > 0) {
116 viewHolder.binding.unreadCount.setVisibility(View.VISIBLE);
117 viewHolder.binding.unreadCount.setUnreadCount(unreadCount);
118 } else {
119 viewHolder.binding.unreadCount.setVisibility(View.GONE);
120 }
121
122 if (isRead) {
123 viewHolder.binding.conversationName.setTypeface(null, Typeface.NORMAL);
124 } else {
125 viewHolder.binding.conversationName.setTypeface(null, Typeface.BOLD);
126 }
127
128 if (draft != null) {
129 viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
130 viewHolder.binding.conversationLastmsg.setText(draft.getMessage());
131 viewHolder.binding.senderName.setText(R.string.draft);
132 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
133 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
134 viewHolder.binding.senderName.setTypeface(null, Typeface.ITALIC);
135 } else {
136 final boolean fileAvailable = !message.isDeleted();
137 final boolean showPreviewText;
138 if (fileAvailable
139 && (message.isFileOrImage()
140 || message.treatAsDownloadable()
141 || message.isGeoUri())) {
142 final var attachment = Attachment.of(message);
143 final @DrawableRes int imageResource = MediaAdapter.getImageDrawable(attachment);
144 showPreviewText = false;
145 viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource);
146 viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE);
147 } else {
148 viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
149 showPreviewText = true;
150 }
151 final Pair<CharSequence, Boolean> preview =
152 UIHelper.getMessagePreview(
153 activity,
154 message,
155 viewHolder.binding.conversationLastmsg.getCurrentTextColor());
156 if (showPreviewText) {
157 viewHolder.binding.conversationLastmsg.setText(UIHelper.shorten(preview.first));
158 } else {
159 viewHolder.binding.conversationLastmsgImg.setContentDescription(preview.first);
160 }
161 viewHolder.binding.conversationLastmsg.setVisibility(
162 showPreviewText ? View.VISIBLE : View.GONE);
163 if (preview.second) {
164 if (isRead) {
165 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.ITALIC);
166 viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
167 } else {
168 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC);
169 viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
170 }
171 } else {
172 if (isRead) {
173 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
174 viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
175 } else {
176 viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD);
177 viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
178 }
179 }
180 if (status == Message.STATUS_RECEIVED) {
181 if (conversation.getMode() == Conversation.MODE_MULTI) {
182 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
183 viewHolder.binding.senderName.setText(
184 UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
185 } else {
186 viewHolder.binding.senderName.setVisibility(View.GONE);
187 }
188 } else if (message.getType() != Message.TYPE_STATUS) {
189 viewHolder.binding.senderName.setVisibility(View.VISIBLE);
190 viewHolder.binding.senderName.setText(activity.getString(R.string.me) + ':');
191 } else {
192 viewHolder.binding.senderName.setVisibility(View.GONE);
193 }
194 }
195
196 final Optional<OngoingRtpSession> ongoingCall;
197 if (conversation.getMode() == Conversational.MODE_MULTI) {
198 ongoingCall = Optional.absent();
199 } else {
200 ongoingCall =
201 activity.xmppConnectionService
202 .getJingleConnectionManager()
203 .getOngoingRtpConnection(conversation.getContact());
204 }
205
206 if (ongoingCall.isPresent()) {
207 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
208 viewHolder.binding.notificationStatus.setImageResource(
209 R.drawable.ic_phone_in_talk_24dp);
210 } else {
211 final long muted_till =
212 conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
213 if (muted_till == Long.MAX_VALUE) {
214 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
215 viewHolder.binding.notificationStatus.setImageResource(
216 R.drawable.ic_notifications_off_24dp);
217 } else if (muted_till >= System.currentTimeMillis()) {
218 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
219 viewHolder.binding.notificationStatus.setImageResource(
220 R.drawable.ic_notifications_paused_24dp);
221 } else if (conversation.alwaysNotify()) {
222 viewHolder.binding.notificationStatus.setVisibility(View.GONE);
223 } else {
224 viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
225 viewHolder.binding.notificationStatus.setImageResource(
226 R.drawable.ic_notifications_none_24dp);
227 }
228 }
229
230 long timestamp;
231 if (draft != null) {
232 timestamp = draft.getTimestamp();
233 } else {
234 timestamp = conversation.getLatestMessage().getTimeSent();
235 }
236 viewHolder.binding.pinnedOnTop.setVisibility(
237 conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)
238 ? View.VISIBLE
239 : View.GONE);
240 viewHolder.binding.conversationLastupdate.setText(
241 UIHelper.readableTimeDifference(activity, timestamp));
242 AvatarWorkerTask.loadAvatar(
243 conversation,
244 viewHolder.binding.conversationImage,
245 R.dimen.avatar_on_conversation_overview);
246 viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
247 }
248
249 @Override
250 public int getItemCount() {
251 return conversations.size();
252 }
253
254 public void setConversationClickListener(OnConversationClickListener listener) {
255 this.listener = listener;
256 }
257
258 public void insert(Conversation c, int position) {
259 conversations.add(position, c);
260 notifyDataSetChanged();
261 }
262
263 public void remove(Conversation conversation, int position) {
264 conversations.remove(conversation);
265 notifyItemRemoved(position);
266 }
267
268 public interface OnConversationClickListener {
269 void onConversationClick(View view, Conversation conversation);
270 }
271
272 static class ConversationViewHolder extends RecyclerView.ViewHolder {
273 private final ItemConversationBinding binding;
274
275 private ConversationViewHolder(final ItemConversationBinding binding) {
276 super(binding.getRoot());
277 this.binding = binding;
278 }
279 }
280}