M src/cheogram/res/values/strings.xml => src/cheogram/res/values/strings.xml +1 -0
@@ 37,4 37,5 @@
<string name="block_media">Block Media</string>
<string name="new_contact">New Contact or Channel</string>
<string name="pref_broadcast_last_activity_summary">Allow contacts to see when you were last active in the app</string>
+ <string name="visitor">Muted</string>
</resources>
M src/main/java/eu/siacs/conversations/entities/MucOptions.java => src/main/java/eu/siacs/conversations/entities/MucOptions.java +45 -4
@@ 1,5 1,6 @@
package eu.siacs.conversations.entities;
+import android.net.Uri;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ 25,6 26,7 @@ import eu.siacs.conversations.xmpp.chatstate.ChatState;
import eu.siacs.conversations.xmpp.forms.Data;
import eu.siacs.conversations.xmpp.forms.Field;
import eu.siacs.conversations.xmpp.pep.Avatar;
+import eu.siacs.conversations.xml.Element;
public class MucOptions {
@@ 54,7 56,7 @@ public class MucOptions {
this.account = conversation.getAccount();
this.conversation = conversation;
final String nick = getProposedNick(conversation.getAttribute("mucNick"));
- this.self = new User(this, createJoinJid(nick), nick);
+ this.self = new User(this, createJoinJid(nick), nick, new HashSet<>());
this.self.affiliation = Affiliation.of(conversation.getAttribute("affiliation"));
this.self.role = Role.of(conversation.getAttribute("role"));
}
@@ 338,7 340,7 @@ public class MucOptions {
public User findOrCreateUserByRealJid(Jid jid, Jid fullJid) {
User user = findUserByRealJid(jid);
if (user == null) {
- user = new User(this, fullJid, null);
+ user = new User(this, fullJid, null, new HashSet<>());
user.setRealJid(jid);
}
return user;
@@ 537,7 539,7 @@ public class MucOptions {
private List<User> getFallbackUsersFromCryptoTargets() {
List<User> users = new ArrayList<>();
for (Jid jid : conversation.getAcceptedCryptoTargets()) {
- User user = new User(this, null, null);
+ User user = new User(this, null, null, new HashSet<>());
user.setRealJid(jid);
users.add(user);
}
@@ 781,6 783,39 @@ public class MucOptions {
}
+ public static class Hat implements Comparable<Hat> {
+ private final Uri uri;
+ private final String title;
+
+ public Hat(final Element el) {
+ Uri parseUri = null; // null hat uri is invaild per spec
+ try {
+ parseUri = Uri.parse(el.getAttribute("uri"));
+ } catch (final Exception e) { }
+ uri = parseUri;
+
+ title = el.getAttribute("title");
+ }
+
+ public Hat(final Uri uri, final String title) {
+ this.uri = uri;
+ this.title = title;
+ }
+
+ public String toString() {
+ return title;
+ }
+
+ public int getColor() {
+ return UIHelper.getColorForName(uri == null ? title : uri.toString());
+ }
+
+ @Override
+ public int compareTo(@NonNull Hat another) {
+ return title.compareTo(another.title);
+ }
+ }
+
public static class User implements Comparable<User>, AvatarService.Avatarable {
private Role role = Role.NONE;
private Affiliation affiliation = Affiliation.NONE;
@@ 791,11 826,13 @@ public class MucOptions {
private Avatar avatar;
private final MucOptions options;
private ChatState chatState = Config.DEFAULT_CHAT_STATE;
+ private final Set<Hat> hats;
- public User(MucOptions options, Jid fullJid, final String nick) {
+ public User(MucOptions options, Jid fullJid, final String nick, final Set<Hat> hats) {
this.options = options;
this.fullJid = fullJid;
this.nick = nick == null ? getName() : nick;
+ this.hats = hats;
}
public String getName() {
@@ 822,6 859,10 @@ public class MucOptions {
this.affiliation = Affiliation.of(affiliation);
}
+ public Set<Hat> getHats() {
+ return this.hats;
+ }
+
public long getPgpKeyId() {
if (this.pgpKeyId != 0) {
return this.pgpKeyId;
M src/main/java/eu/siacs/conversations/parser/AbstractParser.java => src/main/java/eu/siacs/conversations/parser/AbstractParser.java +11 -3
@@ 7,6 7,8 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
+import java.util.Set;
+import java.util.TreeSet;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
@@ 132,10 134,10 @@ public abstract class AbstractParser {
}
public static MucOptions.User parseItem(Conversation conference, Element item) {
- return parseItem(conference,item,null,null);
+ return parseItem(conference,item,null,null,new Element("hats", "urn:xmpp:hats:0"));
}
- public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid, final String nickname) {
+ public static MucOptions.User parseItem(Conversation conference, Element item, Jid fullJid, final String nickname, final Element hatsEl) {
final String local = conference.getJid().getLocal();
final String domain = conference.getJid().getDomain().toEscapedString();
String affiliation = item.getAttribute("affiliation");
@@ 155,7 157,13 @@ public abstract class AbstractParser {
nick = nickname;
}
} catch (final gnu.inet.encoding.PunycodeException | ArrayIndexOutOfBoundsException e) { }
- MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid, nick);
+ Set<MucOptions.Hat> hats = new TreeSet<>();
+ for (Element hat : hatsEl.getChildren()) {
+ if ("hat".equals(hat.getName()) && ("urn:xmpp:hats:0".equals(hat.getNamespace()) || "xmpp:prosody.im/protocol/hats:1".equals(hat.getNamespace()))) {
+ hats.add(new MucOptions.Hat(hat));
+ }
+ }
+ MucOptions.User user = new MucOptions.User(conference.getMucOptions(), fullJid, nick, hats);
if (InvalidJid.isValid(realJid)) {
user.setRealJid(realJid);
}
M src/main/java/eu/siacs/conversations/parser/PresenceParser.java => src/main/java/eu/siacs/conversations/parser/PresenceParser.java +7 -2
@@ 64,6 64,11 @@ public class PresenceParser extends AbstractParser implements
final String type = packet.getAttribute("type");
final Element x = packet.findChild("x", Namespace.MUC_USER);
final Element nick = packet.findChild("nick", Namespace.NICK);
+ Element hats = packet.findChild("hats", "urn:xmpp:hats:0");
+ if (hats == null) {
+ hats = packet.findChild("hats", "xmpp:prosody.im/protocol/hats:1");
+ }
+ if (hats == null) hats = new Element("hats", "urn:xmpp:hats:0");
Avatar avatar = Avatar.parsePresence(packet.findChild("x", "vcard-temp:x:update"));
final List<String> codes = getStatusCodes(x);
if (type == null) {
@@ 71,7 76,7 @@ public class PresenceParser extends AbstractParser implements
Element item = x.findChild("item");
if (item != null && !from.isBareJid()) {
mucOptions.setError(MucOptions.Error.NONE);
- MucOptions.User user = parseItem(conversation, item, from, nick == null ? null : nick.getContent());
+ MucOptions.User user = parseItem(conversation, item, from, nick == null ? null : nick.getContent(), hats);
if (codes.contains(MucOptions.STATUS_CODE_SELF_PRESENCE) || (codes.contains(MucOptions.STATUS_CODE_ROOM_CREATED) && jid.equals(InvalidJid.getNullForInvalid(item.getAttributeAsJid("jid"))))) {
if (mucOptions.setOnline()) {
mXmppConnectionService.getAvatarService().clear(mucOptions);
@@ 175,7 180,7 @@ public class PresenceParser extends AbstractParser implements
} else if (!from.isBareJid()){
Element item = x.findChild("item");
if (item != null) {
- mucOptions.updateUser(parseItem(conversation, item, from, nick == null ? null : nick.getContent()));
+ mucOptions.updateUser(parseItem(conversation, item, from, nick == null ? null : nick.getContent(), hats));
}
MucOptions.User user = mucOptions.deleteUser(from);
if (user != null) {
M src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java => src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +0 -1
@@ 671,7 671,6 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
return getStatus(this, user, mAdvancedMode);
}
-
@Override
public void onAffiliationChangedSuccessful(Jid jid) {
refreshUi();
M src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/UserAdapter.java +38 -4
@@ 1,9 1,11 @@
package eu.siacs.conversations.ui.adapter;
import android.app.PendingIntent;
+import android.content.Context;
import android.content.IntentSender;
import android.view.ContextMenu;
import android.view.LayoutInflater;
+import android.widget.TextView;
import android.view.View;
import android.view.ViewGroup;
@@ 13,6 15,9 @@ import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.ListAdapter;
import androidx.recyclerview.widget.RecyclerView;
+import java.util.ArrayList;
+import java.util.List;
+
import org.openintents.openpgp.util.OpenPgpUtils;
import eu.siacs.conversations.R;
@@ 82,17 87,17 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
});
final String name = user.getNick();
final Contact contact = user.getContact();
+ viewHolder.binding.contactJid.setVisibility(View.GONE);
+ viewHolder.binding.contactJid.setText("");
if (contact != null) {
final String displayName = contact.getDisplayName();
viewHolder.binding.contactDisplayName.setText(displayName);
if (name != null && !name.equals(displayName)) {
- viewHolder.binding.contactJid.setText(String.format("%s \u2022 %s", name, ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode)));
- } else {
- viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
+ viewHolder.binding.contactJid.setVisibility(View.VISIBLE);
+ viewHolder.binding.contactJid.setText(name);
}
} else {
viewHolder.binding.contactDisplayName.setText(name == null ? "" : name);
- viewHolder.binding.contactJid.setText(ConferenceDetailsActivity.getStatus(viewHolder.binding.getRoot().getContext(), user, advancedMode));
}
if (advancedMode && user.getPgpKeyId() != 0) {
viewHolder.binding.key.setVisibility(View.VISIBLE);
@@ 116,7 121,36 @@ public class UserAdapter extends ListAdapter<MucOptions.User, UserAdapter.ViewHo
viewHolder.binding.key.setVisibility(View.GONE);
}
+ viewHolder.binding.tags.setVisibility(View.VISIBLE);
+ viewHolder.binding.tags.removeAllViewsInLayout();
+ for (MucOptions.Hat hat : getPseudoHats(viewHolder.binding.getRoot().getContext(), user)) {
+ TextView tv = (TextView) LayoutInflater.from(viewHolder.binding.getRoot().getContext()).inflate(R.layout.list_item_tag, viewHolder.binding.tags, false);
+ tv.setText(hat.toString());
+ tv.setBackgroundColor(hat.getColor());
+ viewHolder.binding.tags.addView(tv);
+ }
+ for (MucOptions.Hat hat : user.getHats()) {
+ TextView tv = (TextView) LayoutInflater.from(viewHolder.binding.getRoot().getContext()).inflate(R.layout.list_item_tag, viewHolder.binding.tags, false);
+ tv.setText(hat.toString());
+ tv.setBackgroundColor(hat.getColor());
+ viewHolder.binding.tags.addView(tv);
+ }
+ if (viewHolder.binding.tags.getChildCount() < 1) {
+ viewHolder.binding.contactJid.setVisibility(View.VISIBLE);
+ viewHolder.binding.tags.setVisibility(View.GONE);
+ }
+ }
+
+ private List<MucOptions.Hat> getPseudoHats(Context context, MucOptions.User user) {
+ List<MucOptions.Hat> hats = new ArrayList<>();
+ if (user.getAffiliation() != MucOptions.Affiliation.NONE) {
+ hats.add(new MucOptions.Hat(null, context.getString(user.getAffiliation().getResId())));
+ }
+ if (user.getRole() != MucOptions.Role.PARTICIPANT) {
+ hats.add(new MucOptions.Hat(null, context.getString(user.getRole().getResId())));
+ }
+ return hats;
}
public MucOptions.User getSelectedUser() {