1package eu.siacs.conversations.ui.adapter;
2
3import android.app.PendingIntent;
4import android.content.Context;
5import android.content.IntentSender;
6import android.content.res.ColorStateList;
7import android.view.ContextMenu;
8import android.view.LayoutInflater;
9import android.widget.TextView;
10import android.view.View;
11import android.view.ViewGroup;
12import android.widget.Toast;
13
14import androidx.annotation.NonNull;
15import androidx.core.view.ViewCompat;
16import androidx.databinding.DataBindingUtil;
17import androidx.recyclerview.widget.DiffUtil;
18import androidx.recyclerview.widget.ListAdapter;
19import androidx.recyclerview.widget.RecyclerView;
20
21import com.google.android.material.color.MaterialColors;
22import com.google.common.base.Strings;
23import com.google.common.collect.ImmutableList;
24import com.google.common.primitives.Ints;
25
26import java.util.ArrayList;
27import java.util.List;
28
29import org.openintents.openpgp.util.OpenPgpUtils;
30
31import eu.siacs.conversations.R;
32import eu.siacs.conversations.crypto.PgpEngine;
33import eu.siacs.conversations.databinding.ItemContactBinding;
34import eu.siacs.conversations.entities.Contact;
35import eu.siacs.conversations.entities.MucOptions;
36import eu.siacs.conversations.services.XmppConnectionService;
37import eu.siacs.conversations.ui.ConferenceDetailsActivity;
38import eu.siacs.conversations.ui.XmppActivity;
39import eu.siacs.conversations.ui.util.AvatarWorkerTask;
40import eu.siacs.conversations.ui.util.MucDetailsContextMenuHelper;
41import eu.siacs.conversations.utils.Compatibility;
42import eu.siacs.conversations.xmpp.Jid;
43
44public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHolder> implements View.OnCreateContextMenuListener {
45
46 static final DiffUtil.ItemCallback<MucOptions.User> DIFF = new DiffUtil.ItemCallback<MucOptions.User>() {
47 @Override
48 public boolean areItemsTheSame(@NonNull MucOptions.User a, @NonNull MucOptions.User b) {
49 final Jid fullA = a.getFullJid();
50 final Jid fullB = b.getFullJid();
51 final Jid realA = a.getRealJid();
52 final Jid realB = b.getRealJid();
53 if (fullA != null && fullB != null) {
54 return fullA.equals(fullB);
55 } else if (realA != null && realB != null) {
56 return realA.equals(realB);
57 } else {
58 return false;
59 }
60 }
61
62 @Override
63 public boolean areContentsTheSame(@NonNull MucOptions.User a, @NonNull MucOptions.User b) {
64 return a.equals(b);
65 }
66 };
67 private final boolean advancedMode;
68 private MucOptions.User selectedUser = null;
69
70 public UserAdapter(final boolean advancedMode) {
71 super(DIFF);
72 this.advancedMode = advancedMode;
73 }
74
75 @NonNull
76 @Override
77 public ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
78 return new ViewHolder(DataBindingUtil.inflate(LayoutInflater.from(viewGroup.getContext()), R.layout.item_contact, viewGroup, false));
79 }
80
81 @Override
82 public void onBindViewHolder(@NonNull ViewHolder viewHolder, int position) {
83 final MucOptions.User user = getItem(position);
84 AvatarWorkerTask.loadAvatar(user, viewHolder.binding.contactPhoto, R.dimen.avatar);
85 viewHolder.binding.getRoot().setOnClickListener(v -> {
86 final XmppActivity activity = XmppActivity.find(v);
87 if (activity == null) {
88 return;
89 }
90 final var contact = user.getContact();
91 if (user.getRole() == MucOptions.Role.NONE && contact != null) {
92 Toast.makeText(
93 activity,
94 activity.getString(
95 R.string.user_has_left_conference,
96 contact.getDisplayName()),
97 Toast.LENGTH_SHORT)
98 .show();
99 }
100 activity.highlightInMuc(user.getConversation(), user.getName());
101 });
102 viewHolder.binding.getRoot().setTag(user);
103 viewHolder.binding.getRoot().setOnCreateContextMenuListener(this);
104 viewHolder.binding.getRoot().setOnLongClickListener(v -> {
105 selectedUser = user;
106 return false;
107 });
108 final String name = user.getNick();
109 final Contact contact = user.getContact();
110 viewHolder.binding.contactJid.setVisibility(View.GONE);
111 viewHolder.binding.contactJid.setText("");
112 if (contact != null) {
113 final String displayName = contact.getDisplayName();
114 viewHolder.binding.contactDisplayName.setText(displayName);
115 if (name != null && !name.equals(displayName)) {
116 viewHolder.binding.contactJid.setVisibility(View.VISIBLE);
117 viewHolder.binding.contactJid.setText(name);
118 }
119 } else {
120 viewHolder.binding.contactDisplayName.setText(Strings.nullToEmpty(name));
121 viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
122 }
123 if (advancedMode && user.getPgpKeyId() != 0) {
124 viewHolder.binding.key.setVisibility(View.VISIBLE);
125 viewHolder.binding.key.setOnClickListener(v -> {
126 final XmppActivity activity = XmppActivity.find(v);
127 final XmppConnectionService service = activity == null ? null : activity.xmppConnectionService;
128 final PgpEngine pgpEngine = service == null ? null : service.getPgpEngine();
129 if (pgpEngine != null) {
130 PendingIntent intent = pgpEngine.getIntentForKey(user.getPgpKeyId());
131 if (intent != null) {
132 try {
133 activity.startIntentSenderForResult(intent.getIntentSender(), 0, null, 0, 0, 0, Compatibility.pgpStartIntentSenderOptions());
134 } catch (IntentSender.SendIntentException ignored) {
135
136 }
137 }
138 }
139 });
140 viewHolder.binding.key.setText(OpenPgpUtils.convertKeyIdToHex(user.getPgpKeyId()));
141 } else {
142 viewHolder.binding.key.setVisibility(View.GONE);
143 }
144
145 final Context context = viewHolder.binding.getRoot().getContext();
146 final LayoutInflater inflater = LayoutInflater.from(context);
147 viewHolder.binding.tags.setVisibility(View.VISIBLE);
148 viewHolder.binding.tags.removeViews(1, viewHolder.binding.tags.getChildCount() - 1);
149 final ImmutableList.Builder<Integer> viewIdBuilder = new ImmutableList.Builder<>();
150 for (MucOptions.Hat hat : user.getPseudoHats(viewHolder.binding.getRoot().getContext())) {
151 final String tag = hat.toString();
152 final TextView tv = (TextView) inflater.inflate(R.layout.item_tag, viewHolder.binding.tags, false);
153 tv.setText(tag);
154 tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(context,hat.getColor())));
155 final int id = ViewCompat.generateViewId();
156 tv.setId(id);
157 viewIdBuilder.add(id);
158 viewHolder.binding.tags.addView(tv);
159 }
160 for (MucOptions.Hat hat : user.getHats()) {
161 final String tag = hat.toString();
162 final TextView tv = (TextView) inflater.inflate(R.layout.item_tag, viewHolder.binding.tags, false);
163 tv.setText(tag);
164 tv.setBackgroundTintList(ColorStateList.valueOf(MaterialColors.harmonizeWithPrimary(context,hat.getColor())));
165 final int id = ViewCompat.generateViewId();
166 tv.setId(id);
167 viewIdBuilder.add(id);
168 viewHolder.binding.tags.addView(tv);
169 }
170 viewHolder.binding.flowWidget.setReferencedIds(Ints.toArray(viewIdBuilder.build()));
171
172 if (viewHolder.binding.tags.getChildCount() < 1) {
173 viewHolder.binding.contactJid.setVisibility(View.VISIBLE);
174 viewHolder.binding.tags.setVisibility(View.GONE);
175 }
176 }
177
178 public MucOptions.User getSelectedUser() {
179 return selectedUser;
180 }
181
182 @Override
183 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
184 MucDetailsContextMenuHelper.onCreateContextMenu(menu,v);
185 }
186
187 public static class ViewHolder extends RecyclerView.ViewHolder {
188
189 public final ItemContactBinding binding;
190
191 private ViewHolder(ItemContactBinding binding) {
192 super(binding.getRoot());
193 this.binding = binding;
194 }
195 }
196}