ConversationAdapter.java

  1package eu.siacs.conversations.ui.adapter;
  2
  3import android.graphics.Typeface;
  4import android.util.Pair;
  5import android.view.MenuItem;
  6import android.view.LayoutInflater;
  7import android.view.View;
  8import android.view.ViewGroup;
  9
 10import androidx.annotation.NonNull;
 11import androidx.core.graphics.ColorUtils;
 12import androidx.databinding.DataBindingUtil;
 13import androidx.recyclerview.widget.RecyclerView;
 14
 15import com.google.common.base.Optional;
 16import com.google.common.base.Strings;
 17
 18import java.util.List;
 19
 20import eu.siacs.conversations.R;
 21import eu.siacs.conversations.databinding.ConversationListRowBinding;
 22import eu.siacs.conversations.entities.Conversation;
 23import eu.siacs.conversations.entities.Conversational;
 24import eu.siacs.conversations.entities.Message;
 25import eu.siacs.conversations.ui.ConversationFragment;
 26import eu.siacs.conversations.ui.XmppActivity;
 27import eu.siacs.conversations.ui.util.AvatarWorkerTask;
 28import eu.siacs.conversations.ui.util.StyledAttributes;
 29import eu.siacs.conversations.utils.IrregularUnicodeDetector;
 30import eu.siacs.conversations.utils.MimeUtils;
 31import eu.siacs.conversations.utils.UIHelper;
 32import eu.siacs.conversations.xmpp.Jid;
 33import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
 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.conversation_list_row,
 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 (activity.xmppConnectionService != null && activity.xmppConnectionService.getAccounts().size() > 1) {
 73            viewHolder.binding.frame.setBackgroundColor(ColorUtils.setAlphaComponent(
 74                UIHelper.getColorForName(conversation.getAccount().getJid().asBareJid().toString()),
 75                activity.isDarkTheme() ? 20 : 10
 76            ));
 77        }
 78
 79        Message message = conversation.getLatestMessage();
 80        final int unreadCount = conversation.unreadCount();
 81        final boolean isRead = conversation.isRead();
 82        final Conversation.Draft draft = isRead ? conversation.getDraft() : null;
 83        if (unreadCount > 0) {
 84            viewHolder.binding.unreadCount.setVisibility(View.VISIBLE);
 85            viewHolder.binding.unreadCount.setUnreadCount(unreadCount);
 86        } else {
 87            viewHolder.binding.unreadCount.setVisibility(View.GONE);
 88        }
 89
 90        if (isRead) {
 91            viewHolder.binding.conversationName.setTypeface(null, Typeface.NORMAL);
 92        } else {
 93            viewHolder.binding.conversationName.setTypeface(null, Typeface.BOLD);
 94        }
 95
 96        if (draft != null) {
 97            viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
 98            viewHolder.binding.conversationLastmsg.setText(draft.getMessage());
 99            viewHolder.binding.senderName.setText(R.string.draft);
100            viewHolder.binding.senderName.setVisibility(View.VISIBLE);
101            viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
102            viewHolder.binding.senderName.setTypeface(null, Typeface.ITALIC);
103        } else {
104            final boolean fileAvailable = !message.isDeleted();
105            final boolean showPreviewText;
106            if (fileAvailable
107                    && (message.isFileOrImage()
108                            || message.treatAsDownloadable()
109                            || message.isGeoUri())) {
110                final int imageResource;
111                if (message.isGeoUri()) {
112                    imageResource =
113                            activity.getThemeResource(
114                                    R.attr.ic_attach_location, R.drawable.ic_attach_location);
115                    showPreviewText = false;
116                } else {
117                    // TODO move this into static MediaPreview method and use same icons as in
118                    // MediaAdapter
119                    final String mime = message.getMimeType();
120                    if (MimeUtils.AMBIGUOUS_CONTAINER_FORMATS.contains(mime)) {
121                        final Message.FileParams fileParams = message.getFileParams();
122                        if (fileParams.width > 0 && fileParams.height > 0) {
123                            imageResource =
124                                    activity.getThemeResource(
125                                            R.attr.ic_attach_videocam,
126                                            R.drawable.ic_attach_videocam);
127                            showPreviewText = false;
128                        } else if (fileParams.runtime > 0) {
129                            imageResource =
130                                    activity.getThemeResource(
131                                            R.attr.ic_attach_record, R.drawable.ic_attach_record);
132                            showPreviewText = false;
133                        } else {
134                            imageResource =
135                                    activity.getThemeResource(
136                                            R.attr.ic_attach_document,
137                                            R.drawable.ic_attach_document);
138                            showPreviewText = true;
139                        }
140                    } else {
141                        switch (Strings.nullToEmpty(mime).split("/")[0]) {
142                            case "image":
143                                imageResource =
144                                        activity.getThemeResource(
145                                                R.attr.ic_attach_photo, R.drawable.ic_attach_photo);
146                                showPreviewText = false;
147                                break;
148                            case "video":
149                                imageResource =
150                                        activity.getThemeResource(
151                                                R.attr.ic_attach_videocam,
152                                                R.drawable.ic_attach_videocam);
153                                showPreviewText = false;
154                                break;
155                            case "audio":
156                                imageResource =
157                                        activity.getThemeResource(
158                                                R.attr.ic_attach_record,
159                                                R.drawable.ic_attach_record);
160                                showPreviewText = false;
161                                break;
162                            default:
163                                imageResource =
164                                        activity.getThemeResource(
165                                                R.attr.ic_attach_document,
166                                                R.drawable.ic_attach_document);
167                                showPreviewText = true;
168                                break;
169                        }
170                    }
171                }
172                viewHolder.binding.conversationLastmsgImg.setImageResource(imageResource);
173                viewHolder.binding.conversationLastmsgImg.setVisibility(View.VISIBLE);
174            } else {
175                viewHolder.binding.conversationLastmsgImg.setVisibility(View.GONE);
176                showPreviewText = true;
177            }
178            final Pair<CharSequence, Boolean> preview =
179                    UIHelper.getMessagePreview(
180                            activity,
181                            message,
182                            viewHolder.binding.conversationLastmsg.getCurrentTextColor());
183            if (showPreviewText) {
184                viewHolder.binding.conversationLastmsg.setText(UIHelper.shorten(preview.first));
185            } else {
186                viewHolder.binding.conversationLastmsgImg.setContentDescription(preview.first);
187            }
188            viewHolder.binding.conversationLastmsg.setVisibility(
189                    showPreviewText ? View.VISIBLE : View.GONE);
190            if (preview.second) {
191                if (isRead) {
192                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.ITALIC);
193                    viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
194                } else {
195                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD_ITALIC);
196                    viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
197                }
198            } else {
199                if (isRead) {
200                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.NORMAL);
201                    viewHolder.binding.senderName.setTypeface(null, Typeface.NORMAL);
202                } else {
203                    viewHolder.binding.conversationLastmsg.setTypeface(null, Typeface.BOLD);
204                    viewHolder.binding.senderName.setTypeface(null, Typeface.BOLD);
205                }
206            }
207            if (message.getStatus() == Message.STATUS_RECEIVED) {
208                if (conversation.getMode() == Conversation.MODE_MULTI) {
209                    viewHolder.binding.senderName.setVisibility(View.VISIBLE);
210                    viewHolder.binding.senderName.setText(
211                            UIHelper.getMessageDisplayName(message).split("\\s+")[0] + ':');
212                } else {
213                    viewHolder.binding.senderName.setVisibility(View.GONE);
214                }
215            } else if (message.getType() != Message.TYPE_STATUS) {
216                viewHolder.binding.senderName.setVisibility(View.VISIBLE);
217                viewHolder.binding.senderName.setText(activity.getString(R.string.me) + ':');
218            } else {
219                viewHolder.binding.senderName.setVisibility(View.GONE);
220            }
221        }
222
223        final Optional<OngoingRtpSession> ongoingCall;
224        if (conversation.getMode() == Conversational.MODE_MULTI) {
225            ongoingCall = Optional.absent();
226        } else {
227            ongoingCall =
228                    activity.xmppConnectionService
229                            .getJingleConnectionManager()
230                            .getOngoingRtpConnection(conversation.getContact());
231        }
232
233        if (ongoingCall.isPresent()) {
234            viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
235            final int ic_ongoing_call =
236                    activity.getThemeResource(
237                            R.attr.ic_ongoing_call_hint, R.drawable.ic_phone_in_talk_black_18dp);
238            viewHolder.binding.notificationStatus.setImageResource(ic_ongoing_call);
239        } else {
240            final long muted_till =
241                    conversation.getLongAttribute(Conversation.ATTRIBUTE_MUTED_TILL, 0);
242            if (muted_till == Long.MAX_VALUE) {
243                viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
244                int ic_notifications_off =
245                        activity.getThemeResource(
246                                R.attr.icon_notifications_off,
247                                R.drawable.ic_notifications_off_black_24dp);
248                viewHolder.binding.notificationStatus.setImageResource(ic_notifications_off);
249            } else if (muted_till >= System.currentTimeMillis()) {
250                viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
251                int ic_notifications_paused =
252                        activity.getThemeResource(
253                                R.attr.icon_notifications_paused,
254                                R.drawable.ic_notifications_paused_black_24dp);
255                viewHolder.binding.notificationStatus.setImageResource(ic_notifications_paused);
256            } else if (conversation.alwaysNotify()) {
257                viewHolder.binding.notificationStatus.setVisibility(View.GONE);
258            } else {
259                viewHolder.binding.notificationStatus.setVisibility(View.VISIBLE);
260                int ic_notifications_none =
261                        activity.getThemeResource(
262                                R.attr.icon_notifications_none,
263                                R.drawable.ic_notifications_none_black_24dp);
264                viewHolder.binding.notificationStatus.setImageResource(ic_notifications_none);
265            }
266        }
267
268        long timestamp;
269        if (draft != null) {
270            timestamp = draft.getTimestamp();
271        } else {
272            timestamp = conversation.getLatestMessage().getTimeSent();
273        }
274        viewHolder.binding.pinnedOnTop.setVisibility(
275                conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false)
276                        ? View.VISIBLE
277                        : View.GONE);
278        viewHolder.binding.conversationLastupdate.setText(
279                UIHelper.readableTimeDifference(activity, timestamp));
280        AvatarWorkerTask.loadAvatar(
281                conversation,
282                viewHolder.binding.conversationImage,
283                R.dimen.avatar_on_conversation_overview);
284        viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
285    }
286
287    @Override
288    public int getItemCount() {
289        return conversations.size();
290    }
291
292    public void setConversationClickListener(OnConversationClickListener listener) {
293        this.listener = listener;
294    }
295
296    public void insert(Conversation c, int position) {
297        conversations.add(position, c);
298        notifyDataSetChanged();
299    }
300
301    public void remove(Conversation conversation, int position) {
302        conversations.remove(conversation);
303        notifyItemRemoved(position);
304    }
305
306    public interface OnConversationClickListener {
307        void onConversationClick(View view, Conversation conversation);
308    }
309
310    static class ConversationViewHolder extends RecyclerView.ViewHolder {
311        private final ConversationListRowBinding binding;
312
313        private ConversationViewHolder(ConversationListRowBinding binding) {
314            super(binding.getRoot());
315            this.binding = binding;
316            binding.getRoot().setLongClickable(true);
317        }
318    }
319}