tags for muc

This commit is contained in:
kosyak 2023-09-06 02:49:41 +02:00
parent 9aac6d99e7
commit c62cc344b5
9 changed files with 168 additions and 18 deletions

View file

@ -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,15 +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<>();
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)));
}
}
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;
}
@ -203,15 +252,11 @@ public class Bookmark extends Element implements ListItem {
}
}
return true;
} else if (parts.length > 0) {
} else {
final Jid jid = getJid();
return (jid != null && jid.toString().contains(parts[0])) ||
getDisplayName().toLowerCase(Locale.US).contains(parts[0]) ||
matchInTag(context, parts[0]);
} else {
final Jid jid = getJid();
return (jid != null && jid.toString().contains(needle)) ||
getDisplayName().toLowerCase(Locale.US).contains(needle);
}
}

View file

@ -247,6 +247,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) {
@ -255,7 +256,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;
}

View file

@ -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));
if (mConversation.isPrivateAndNonAnonymous()) {
this.binding.jid.setText(getString(R.string.hosted_on, mConversation.getJid().getDomain()));
@ -571,6 +622,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) {
@ -645,7 +715,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));

View file

@ -206,7 +206,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
int pos = binding.startConversationViewPager.getCurrentItem();
if (pos == 0) {
if (contacts.size() == 1) {
openConversationForContact((Contact) contacts.get(0));
openConversation((Contact) contacts.get(0));
return true;
} else if (contacts.size() == 0 && conferences.size() == 1) {
openConversationsForBookmark((Bookmark) conferences.get(0));
@ -217,7 +217,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
openConversationsForBookmark((Bookmark) conferences.get(0));
return true;
} else if (conferences.size() == 0 && contacts.size() == 1) {
openConversationForContact((Contact) contacts.get(0));
openConversation((Contact) contacts.get(0));
return true;
}
}
@ -418,8 +418,15 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
}
protected void openConversationForContact(int position) {
Contact contact = (Contact) contacts.get(position);
openConversationForContact(contact);
openConversation(contacts.get(position));
}
protected void openConversation(ListItem item) {
if (item instanceof Contact) {
openConversationForContact((Contact) item);
} else {
openConversationsForBookmark((Bookmark) item);
}
}
protected void openConversationForContact(Contact contact) {

View file

@ -50,6 +50,12 @@ public class Element {
return child;
}
public void removeChild(Element element) {
if (name == null) return;
this.children.remove(element);
}
public Element setContent(String content) {
this.content = content;
this.children.clear();

View file

@ -59,7 +59,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";

View file

@ -2795,8 +2795,7 @@ public class XmppConnection implements Runnable {
}
public boolean bookmarks2() {
return Config
.USE_BOOKMARKS2 /* || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT)*/;
return Config.USE_BOOKMARKS2 || hasDiscoFeature(account.getJid().asBareJid(), Namespace.BOOKMARKS2_COMPAT);
}
public boolean externalServiceDiscovery() {

View file

@ -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"); //YOLO!
options.putString("pubsub#notify_delete", "true");
options.putString("pubsub#notify_retract", "true"); //one could also set notify=true on the retract

View file

@ -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" />
</LinearLayout>
@ -128,6 +138,13 @@
android:textAppearance="@style/Widget.Conversations.EditText"/>
</com.google.android.material.textfield.TextInputLayout>
<eu.siacs.conversations.ui.widget.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