~singpolyma/cheogram-android

cf32eb3d11a2caa1c282c3a921278c12076f1e0a — Stephen Paul Weber 24 days ago 3f75960 + 4fc9b10
Merge branch 'muc-tags'

* muc-tags:
  Show and edit tags on MUC bookmarks
  Fix and updates Bookmarks2 support
M src/main/java/eu/siacs/conversations/entities/Bookmark.java => src/main/java/eu/siacs/conversations/entities/Bookmark.java +52 -4
@@ 25,6 25,7 @@ public class Bookmark extends Element implements ListItem {
	private final Account account;
	private WeakReference<Conversation> conversation;
	private Jid jid;
	protected Element extensions = new Element("extensions", Namespace.BOOKMARKS2);

	public Bookmark(final Account account, final Jid jid) {
		super("conference");


@@ 101,9 102,48 @@ public class Bookmark extends Element implements ListItem {
		bookmark.setBookmarkName(conference.getAttribute("name"));
		bookmark.setAutojoin(conference.getAttributeAsBoolean("autojoin"));
		bookmark.setNick(conference.findChildContent("nick"));
		bookmark.setPassword(conference.findChildContent("password"));
		final Element extensions = conference.findChild("extensions", Namespace.BOOKMARKS2);
		if (extensions != null) {
			for (final Element ext : extensions.getChildren()) {
				if (ext.getName().equals("group") && ext.getNamespace().equals("jabber:iq:roster")) {
					bookmark.addGroup(ext.getContent());
				}
			}
			bookmark.extensions = extensions;
		}
		return bookmark;
	}

	public Element getExtensions() {
		return extensions;
	}

	public void addGroup(final String group) {
		addChild("group", "jabber:iq:roster").setContent(group);
		extensions.addChild("group", "jabber:iq:roster").setContent(group);
	}

	public void setGroups(List<String> groups) {
		final List<Element> children = new ArrayList<>(getChildren());
		for (final Element el : children) {
			if (el.getName().equals("group")) {
				removeChild(el);
			}
		}

		final List<Element> extChildren = new ArrayList<>(extensions.getChildren());
		for (final Element el : extChildren) {
			if (el.getName().equals("group")) {
				extensions.removeChild(el);
			}
		}

		for (final String group : groups) {
			addGroup(group);
		}
	}

	public void setAutojoin(boolean autojoin) {
		if (autojoin) {
			this.setAttribute("autojoin", "true");


@@ 150,16 190,24 @@ public class Bookmark extends Element implements ListItem {
		return jid == null || nick == null || nick.trim().isEmpty() ? jid : jid.withResource(nick);
	}

	@Override
	public List<Tag> getTags(Context context) {
	public List<Tag> getGroupTags() {
		ArrayList<Tag> tags = new ArrayList<>();
		tags.add(new Tag("Channel", UIHelper.getColorForName("Channel",true)));

		for (Element element : getChildren()) {
			if (element.getName().equals("group") && element.getContent() != null) {
				String group = element.getContent();
				tags.add(new Tag(group, UIHelper.getColorForName(group,true)));
				tags.add(new Tag(group, UIHelper.getColorForName(group, true)));
			}
		}

		return tags;
	}

	@Override
	public List<Tag> getTags(Context context) {
		ArrayList<Tag> tags = new ArrayList<>();
		tags.add(new Tag("Channel", UIHelper.getColorForName("Channel",true)));
		tags.addAll(getGroupTags());
		return tags;
	}


M src/main/java/eu/siacs/conversations/generator/IqGenerator.java => src/main/java/eu/siacs/conversations/generator/IqGenerator.java +5 -0
@@ 249,6 249,7 @@ public class IqGenerator extends AbstractGenerator {
    public Element publishBookmarkItem(final Bookmark bookmark) {
        final String name = bookmark.getBookmarkName();
        final String nick = bookmark.getNick();
        final String password = bookmark.getPassword();
        final boolean autojoin = bookmark.autojoin();
        final Element conference = new Element("conference", Namespace.BOOKMARKS2);
        if (name != null) {


@@ 257,7 258,11 @@ public class IqGenerator extends AbstractGenerator {
        if (nick != null) {
            conference.addChild("nick").setContent(nick);
        }
        if (password != null) {
            conference.addChild("password").setContent(password);
        }
        conference.setAttribute("autojoin",String.valueOf(autojoin));
        conference.addChild(bookmark.getExtensions());
        return conference;
    }


M src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java => src/main/java/eu/siacs/conversations/ui/ConferenceDetailsActivity.java +73 -2
@@ 9,25 9,34 @@ import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AlertDialog;
import androidx.databinding.DataBindingUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ActivityMucDetailsBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.services.XmppConnectionService;


@@ 200,6 209,7 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
        this.binding.mucEditTitle.addTextChangedListener(this);
        this.binding.mucEditSubject.addTextChangedListener(this);
        this.binding.mucEditSubject.addTextChangedListener(new StylingHelper.MessageEditorStyler(this.binding.mucEditSubject));
        this.binding.editTags.addTextChangedListener(this);
        this.mMediaAdapter = new MediaAdapter(this, R.dimen.media_size);
        this.mUserPreviewAdapter = new UserPreviewAdapter();
        this.binding.media.setAdapter(mMediaAdapter);


@@ 301,12 311,52 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
            if (!owner) {
                this.binding.mucEditSubject.requestFocus();
            }

            final Bookmark bookmark = mConversation.getBookmark();
            if (bookmark != null && mConversation.getAccount().getXmppConnection().getFeatures().bookmarks2()) {
                for (final ListItem.Tag group : bookmark.getGroupTags()) {
                    binding.editTags.addObjectSync(group);
                }
                ArrayList<ListItem.Tag> tags = new ArrayList<>();
                for (final Account account : xmppConnectionService.getAccounts()) {
                    for (Contact contact : account.getRoster().getContacts()) {
                        tags.addAll(contact.getTags(this));
                    }
                    for (Bookmark bmark : account.getBookmarks()) {
                        tags.addAll(bmark.getTags(this));
                    }
                }
                Comparator<Map.Entry<ListItem.Tag,Integer>> sortTagsBy = Map.Entry.comparingByValue(Comparator.reverseOrder());
                sortTagsBy = sortTagsBy.thenComparing(entry -> entry.getKey().getName());

                ArrayAdapter<ListItem.Tag> adapter = new ArrayAdapter<>(
                    this,
                    android.R.layout.simple_list_item_1,
                    tags.stream()
                    .collect(Collectors.toMap((x) -> x, (t) -> 1, (c1, c2) -> c1 + c2))
                    .entrySet().stream()
                    .sorted(sortTagsBy)
                    .map(e -> e.getKey()).collect(Collectors.toList())
                );
                binding.editTags.setAdapter(adapter);
                this.binding.editTags.setVisibility(View.VISIBLE);
            } else {
                this.binding.editTags.setVisibility(View.GONE);
            }
        } else {
            String subject = this.binding.mucEditSubject.isEnabled() ? this.binding.mucEditSubject.getEditableText().toString().trim() : null;
            String name = this.binding.mucEditTitle.isEnabled() ? this.binding.mucEditTitle.getEditableText().toString().trim() : null;
            onMucInfoUpdated(subject, name);

            final Bookmark bookmark = mConversation.getBookmark();
            if (bookmark != null && mConversation.getAccount().getXmppConnection().getFeatures().bookmarks2()) {
                bookmark.setGroups(binding.editTags.getObjects().stream().map(tag -> tag.getName()).collect(Collectors.toList()));
                xmppConnectionService.createBookmark(bookmark.getAccount(), bookmark);
            }

            SoftKeyboardUtils.hideSoftKeyboard(this);
            hideEditor();
            updateView();
        }
    }



@@ 458,7 508,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
            account = mConversation.getAccount().getJid().asBareJid().toEscapedString();
        }
        setTitle(mucOptions.isPrivateAndNonAnonymous() ? R.string.action_muc_details : R.string.channel_details);
        this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject()) ? View.VISIBLE : View.GONE);
        final Bookmark bookmark = mConversation.getBookmark();
        this.binding.editMucNameButton.setVisibility((self.getAffiliation().ranks(MucOptions.Affiliation.OWNER) || mucOptions.canChangeSubject() || (bookmark != null && mConversation.getAccount().getXmppConnection().getFeatures().bookmarks2())) ? View.VISIBLE : View.GONE);
        this.binding.detailsAccount.setText(getString(R.string.using_account, account));
        this.binding.truejid.setVisibility(View.GONE);
        if (mConversation.isPrivateAndNonAnonymous()) {


@@ 574,6 625,25 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
            this.binding.noUsersHints.setVisibility(View.GONE);
        }

        if (bookmark == null) {
            binding.tags.setVisibility(View.GONE);
            return;
        }

        List<ListItem.Tag> tagList = bookmark.getTags(this);
        if (tagList.size() == 0) {
            binding.tags.setVisibility(View.GONE);
        } else {
            final LayoutInflater inflater = getLayoutInflater();
            binding.tags.setVisibility(View.VISIBLE);
            binding.tags.removeAllViewsInLayout();
            for (final ListItem.Tag tag : tagList) {
                final TextView tv = (TextView) inflater.inflate(R.layout.list_item_tag, binding.tags, false);
                tv.setText(tag.getName());
                tv.setBackgroundColor(tag.getColor());
                binding.tags.addView(tv);
            }
        }
    }

    public static String getStatus(Context context, User user, final boolean advanced) {


@@ 648,7 718,8 @@ public class ConferenceDetailsActivity extends XmppActivity implements OnConvers
        if (this.binding.mucEditor.getVisibility() == View.VISIBLE) {
            boolean subjectChanged = changed(binding.mucEditSubject.getEditableText().toString(), mucOptions.getSubject());
            boolean nameChanged = changed(binding.mucEditTitle.getEditableText().toString(), mucOptions.getName());
            if (subjectChanged || nameChanged) {
            final Bookmark bookmark = mConversation.getBookmark();
            if (subjectChanged || nameChanged || (bookmark != null && mConversation.getAccount().getXmppConnection().getFeatures().bookmarks2())) {
                this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_save, R.drawable.ic_save_black_24dp));
            } else {
                this.binding.editMucNameButton.setImageResource(getThemeResource(R.attr.icon_cancel, R.drawable.ic_cancel_black_24dp));

M src/main/java/eu/siacs/conversations/xml/Namespace.java => src/main/java/eu/siacs/conversations/xml/Namespace.java +1 -1
@@ 48,7 48,7 @@ public final class Namespace {
    public static final String PUSH = "urn:xmpp:push:0";
    public static final String COMMANDS = "http://jabber.org/protocol/commands";
    public static final String MUC_USER = "http://jabber.org/protocol/muc#user";
    public static final String BOOKMARKS2 = "urn:xmpp:bookmarks:0";
    public static final String BOOKMARKS2 = "urn:xmpp:bookmarks:1";
    public static final String BOOKMARKS2_COMPAT = BOOKMARKS2 + "#compat";
    public static final String INVITE = "urn:xmpp:invite";
    public static final String PARS = "urn:xmpp:pars:0";

M src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java => src/main/java/eu/siacs/conversations/xmpp/pep/PublishOptions.java +1 -1
@@ 30,7 30,7 @@ public class PublishOptions {
        options.putString("pubsub#persist_items", "true");
        options.putString("pubsub#access_model", "whitelist");
        options.putString("pubsub#send_last_published_item", "never");
        options.putString("pubsub#max_items", "128"); //YOLO!
        options.putString("pubsub#max_items", "max");

        options.putString("pubsub#notify_delete", "true");
        options.putString("pubsub#notify_retract", "true"); //one could also set notify=true on the retract

M src/main/res/layout/activity_muc_details.xml => src/main/res/layout/activity_muc_details.xml +17 -0
@@ 86,6 86,16 @@
                                            android:layout_height="wrap_content"
                                            android:autoLink="web"
                                            android:textAppearance="@style/TextAppearance.Conversations.Subhead"/>

                                        <com.wefika.flowlayout.FlowLayout
                                            android:id="@+id/tags"
                                            android:layout_width="wrap_content"
                                            android:layout_height="wrap_content"
                                            android:layout_marginBottom="4dp"
                                            android:layout_marginLeft="-2dp"
                                            android:layout_marginTop="4dp"
                                            android:orientation="horizontal"></com.wefika.flowlayout.FlowLayout>

                                    </LinearLayout>




@@ 128,6 138,13 @@
                                                android:textAppearance="@style/Widget.Conversations.EditText"/>
                                        </com.google.android.material.textfield.TextInputLayout>

                                        <com.cheogram.android.TagEditorView
                                            android:id="@+id/edit_tags"
                                            android:layout_width="match_parent"
                                            android:layout_height="wrap_content"
                                            android:hint="Tags"
                                            android:layout_marginBottom="4dp" />

                                    </LinearLayout>

                                    <ImageButton