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