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