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;
  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}