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