~singpolyma/cheogram-android

57f0594e480c9442dd218973dec7aa6be65afb37 — Stephen Paul Weber 4 months ago f88d778
Affiliation and role and hats as tags

Do not show "none" affiliation since it doesn't really mean much and is
the default, same for "participant".  Show "visitor" role as "muted".

Hats are coloured by the URI not their name.
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() {