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                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                    final var displayName = UIHelper.getMessageDisplayName(message);
187                    final var displayNameParts = displayName.split("\\s+");
188                    // Skip when nickname only consists of blank chars
189                    if (displayNameParts.length == 0) {
190                        viewHolder.binding.senderName.setText(String.format("%s:", displayName));
191                    } else {
192                        viewHolder.binding.senderName.setText(
193                                String.format("%s:", displayNameParts[0]));
194                    }
195                } else {
196                    viewHolder.binding.senderName.setVisibility(View.GONE);
197                }
198            } else if (message.getType() != Message.TYPE_STATUS) {
199                viewHolder.binding.senderName.setVisibility(View.VISIBLE);
200                viewHolder.binding.senderName.setText(
201                        String.format("%s:", activity.getString(R.string.me)));
202            } else {
203                viewHolder.binding.senderName.setVisibility(View.GONE);
204            }
205        }
206
207        final Optional<OngoingRtpSession> ongoingCall;
208        if (conversation.getMode() == Conversational.MODE_MULTI) {
209            ongoingCall = Optional.absent();
210        } else {
211            ongoingCall =
212                    activity.xmppConnectionService
213                            .getJingleConnectionManager()
214                            .getOngoingRtpConnection(conversation.getContact());
215        }
216
217        if (ongoingCall.isPresent()) {
218            viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
219            viewHolder.binding.notificationStatus.setImageResource(
220                    R.drawable.ic_phone_in_talk_24dp);
221        } else {
222            final long muted_till =
223                    conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
224            if (muted_till == Long.MAX_VALUE) {
225                viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
226                viewHolder.binding.notificationStatus.setImageResource(
227                        R.drawable.ic_notifications_off_24dp);
228            } else if (muted_till >= System.currentTimeMillis()) {
229                viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
230                viewHolder.binding.notificationStatus.setImageResource(
231                        R.drawable.ic_notifications_paused_24dp);
232            } else if (conversation.alwaysNotify()) {
233                viewHolder.binding.notificationStatus.setVisibility(View.GONE);
234            } else {
235                viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
236                viewHolder.binding.notificationStatus.setImageResource(
237                        R.drawable.ic_notifications_none_24dp);
238            }
239        }
240
241        long timestamp;
242        if (draft != null) {
243            timestamp = draft.getTimestamp();
244        } else {
245            timestamp = conversation.getLatestMessage().getTimeSent();
246        }
247        viewHolder.binding.pinnedOnTop.setVisibility(
248                conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)
249                        ? View.VISIBLE
250                        : View.GONE);
251        viewHolder.binding.conversationLastupdate.setText(
252                UIHelper.readableTimeDifference(activity, timestamp));
253        AvatarWorkerTask.loadAvatar(
254                conversation,
255                viewHolder.binding.conversationImage,
256                R.dimen.avatar_on_conversation_overview);
257        viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
258    }
259
260    @Override
261    public int getItemCount() {
262        return conversations.size();
263    }
264
265    public void setConversationClickListener(OnConversationClickListener listener) {
266        this.listener = listener;
267    }
268
269    public void insert(Conversation c, int position) {
270        conversations.add(position, c);
271        notifyDataSetChanged();
272    }
273
274    public void remove(Conversation conversation, int position) {
275        conversations.remove(conversation);
276        notifyItemRemoved(position);
277    }
278
279    public interface OnConversationClickListener {
280        void onConversationClick(View view, Conversation conversation);
281    }
282
283    public static class ConversationViewHolder extends RecyclerView.ViewHolder {
284        public final ItemConversationBinding binding;
285
286        private ConversationViewHolder(final ItemConversationBinding binding) {
287            super(binding.getRoot());
288            this.binding = binding;
289        }
290    }
291}