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