ConversationAdapter.java

  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}