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