Detailed changes
@@ -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;
}
@@ -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;
}
@@ -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));
@@ -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";
@@ -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
@@ -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