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