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