From 7926c19e69368fcafebdc2626327381b2bf5b889 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 19 Feb 2024 13:35:34 -0500 Subject: [PATCH] Support full MUC configuration form for owners --- .../conversations/entities/Conversation.java | 128 ++++++++++++++++++ .../ui/ConversationFragment.java | 30 +++- .../ui/adapter/CommandAdapter.java | 40 +++++- .../siacs/conversations/xmpp/forms/Data.java | 2 + 4 files changed, 189 insertions(+), 11 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/entities/Conversation.java b/src/main/java/eu/siacs/conversations/entities/Conversation.java index 676eaf56b85bed033782820f62f865d140f83420..e33f7cee25ac10159c1b00f9321f8b6c1df1900a 100644 --- a/src/main/java/eu/siacs/conversations/entities/Conversation.java +++ b/src/main/java/eu/siacs/conversations/entities/Conversation.java @@ -1406,6 +1406,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl pagerAdapter.startCommand(command, xmppConnectionService); } + public void startMucConfig(XmppConnectionService xmppConnectionService) { + pagerAdapter.startMucConfig(xmppConnectionService); + } + public boolean switchToSession(final String node) { return pagerAdapter.switchToSession(node); } @@ -1574,6 +1578,37 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl if (mPager != null) mPager.setCurrentItem(getCount() - 1); } + public void startMucConfig(XmppConnectionService xmppConnectionService) { + MucConfigSession session = new MucConfigSession(xmppConnectionService); + final IqPacket packet = new IqPacket(IqPacket.TYPE.GET); + packet.setTo(Conversation.this.getJid().asBareJid()); + packet.addChild("query", "http://jabber.org/protocol/muc#owner"); + + final TimerTask task = new TimerTask() { + @Override + public void run() { + if (getAccount().getStatus() != Account.State.ONLINE) { + final TimerTask self = this; + new Timer().schedule(new TimerTask() { + @Override + public void run() { + self.run(); + } + }, 1000); + } else { + xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> { + session.updateWithResponse(iq); + }, 120L); + } + } + }; + task.run(); + + sessions.add(session); + notifyDataSetChanged(); + if (mPager != null) mPager.setCurrentItem(getCount() - 1); + } + public void removeSession(ConversationPage session) { sessions.remove(session); notifyDataSetChanged(); @@ -3434,6 +3469,99 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl return null; } } + + class MucConfigSession extends CommandSession { + MucConfigSession(XmppConnectionService xmppConnectionService) { + super("Configure Channel", null, xmppConnectionService); + } + + @Override + protected void updateWithResponseUiThread(final IqPacket iq) { + Timer oldTimer = this.loadingTimer; + this.loadingTimer = new Timer(); + oldTimer.cancel(); + this.executing = false; + this.loading = false; + this.loadingHasBeenLong = false; + this.responseElement = null; + this.fillableFieldCount = 0; + this.reported = null; + this.response = iq; + this.items.clear(); + this.actionsAdapter.clear(); + layoutManager.setSpanCount(1); + + final Element query = iq.findChild("query", "http://jabber.org/protocol/muc#owner"); + if (iq.getType() == IqPacket.TYPE.RESULT && query != null) { + final Data form = Data.parse(query.findChild("x", "jabber:x:data")); + final String title = form.getTitle(); + if (title != null) { + mTitle = title; + ConversationPagerAdapter.this.notifyDataSetChanged(); + } + + this.responseElement = form; + setupReported(form.findChild("reported", "jabber:x:data")); + if (mBinding != null) mBinding.form.setLayoutManager(setupLayoutManager()); + + if (actionsAdapter.countExceptCancel() < 1) { + actionsAdapter.add(Pair.create("save", "Save")); + } + + if (actionsAdapter.getPosition("cancel") < 0) { + actionsAdapter.insert(Pair.create("cancel", "cancel"), 0); + } + } else if (iq.getType() == IqPacket.TYPE.RESULT) { + expectingRemoval = true; + removeSession(this); + return; + } else { + actionsAdapter.add(Pair.create("close", "close")); + } + + notifyDataSetChanged(); + } + + @Override + public synchronized boolean execute(String action) { + if ("cancel".equals(action)) { + final IqPacket packet = new IqPacket(IqPacket.TYPE.SET); + packet.setTo(response.getFrom()); + final Element form = packet + .addChild("query", "http://jabber.org/protocol/muc#owner") + .addChild("x", "jabber:x:data"); + form.setAttribute("type", "cancel"); + xmppConnectionService.sendIqPacket(getAccount(), packet, null); + return true; + } + + if (!"save".equals(action)) return true; + + final IqPacket packet = new IqPacket(IqPacket.TYPE.SET); + packet.setTo(response.getFrom()); + + String formType = responseElement == null ? null : responseElement.getAttribute("type"); + if (responseElement != null && + responseElement.getName().equals("x") && + responseElement.getNamespace().equals("jabber:x:data") && + formType != null && formType.equals("form")) { + + responseElement.setAttribute("type", "submit"); + packet + .addChild("query", "http://jabber.org/protocol/muc#owner") + .addChild(responseElement); + } + + executing = true; + xmppConnectionService.sendIqPacket(getAccount(), packet, (a, iq) -> { + updateWithResponse(iq); + }, 120L); + + loading(); + + return false; + } + } } public static class Thread { diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index cf7877ca380074fba218b54c533fbed53167141d..64b9709049b3ec718db0831e3ad5e3e9499e51e1 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -3151,8 +3151,7 @@ public class ConversationFragment extends XmppFragment binding.commandsView.setOnItemClickListener((parent, view, position, id) -> { if (activity == null) return; - final Element command = commandAdapter.getItem(position); - activity.startCommand(ConversationFragment.this.conversation.getAccount(), command.getAttributeAsJid("jid"), command.getAttribute("node")); + commandAdapter.getItem(position).start(activity, ConversationFragment.this.conversation); }); refreshCommands(false); } @@ -3169,6 +3168,11 @@ public class ConversationFragment extends XmppFragment protected void refreshCommands(boolean delayShow) { if (commandAdapter == null) return; + final CommandAdapter.MucConfig mucConfig = + conversation.getMucOptions().getSelf().getAffiliation().ranks(MucOptions.Affiliation.OWNER) ? + new CommandAdapter.MucConfig() : + null; + Jid commandJid = conversation.getContact().resourceWhichSupport(Namespace.COMMANDS); if (commandJid == null && conversation.getMode() == Conversation.MODE_MULTI && conversation.getMucOptions().hasFeature(Namespace.COMMANDS)) { commandJid = conversation.getJid().asBareJid(); @@ -3177,7 +3181,14 @@ public class ConversationFragment extends XmppFragment commandJid = conversation.getJid(); } if (commandJid == null) { - conversation.hideViewPager(); + binding.commandsViewProgressbar.setVisibility(View.GONE); + if (mucConfig == null) { + conversation.hideViewPager(); + } else { + commandAdapter.clear(); + commandAdapter.add(mucConfig); + conversation.showViewPager(); + } } else { if (!delayShow) conversation.showViewPager(); binding.commandsViewProgressbar.setVisibility(View.VISIBLE); @@ -3185,15 +3196,17 @@ public class ConversationFragment extends XmppFragment if (activity == null) return; activity.runOnUiThread(() -> { + binding.commandsViewProgressbar.setVisibility(View.GONE); + commandAdapter.clear(); if (iq.getType() == IqPacket.TYPE.RESULT) { - binding.commandsViewProgressbar.setVisibility(View.GONE); - commandAdapter.clear(); for (Element child : iq.query().getChildren()) { if (!"item".equals(child.getName()) || !Namespace.DISCO_ITEMS.equals(child.getNamespace())) continue; - commandAdapter.add(child); + commandAdapter.add(new CommandAdapter.Command0050(child)); } } + if (mucConfig != null) commandAdapter.add(mucConfig); + if (commandAdapter.getCount() < 1) { conversation.hideViewPager(); } else if (delayShow) { @@ -3353,7 +3366,10 @@ public class ConversationFragment extends XmppFragment private Element commandFor(final Jid jid, final String node) { if (commandAdapter != null) { for (int i = 0; i < commandAdapter.getCount(); i++) { - Element command = commandAdapter.getItem(i); + final CommandAdapter.Command c = commandAdapter.getItem(i); + if (!(c instanceof CommandAdapter.Command0050)) continue; + + final Element command = ((CommandAdapter.Command0050) c).el; final String commandNode = command.getAttribute("node"); if (commandNode == null || !commandNode.equals(node)) continue; diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/CommandAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/CommandAdapter.java index 1b134f9f95755b15f9b3d9a58a6c07f53a8cbef6..16282fecbc88aa6d59ee73e7afc93aedeb7b06a7 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/CommandAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/CommandAdapter.java @@ -9,11 +9,13 @@ import androidx.annotation.NonNull; import androidx.databinding.DataBindingUtil; import eu.siacs.conversations.R; -import eu.siacs.conversations.xml.Element; -import eu.siacs.conversations.ui.XmppActivity; import eu.siacs.conversations.databinding.CommandRowBinding; +import eu.siacs.conversations.entities.Conversation; +import eu.siacs.conversations.ui.ConversationsActivity; +import eu.siacs.conversations.ui.XmppActivity; +import eu.siacs.conversations.xml.Element; -public class CommandAdapter extends ArrayAdapter { +public class CommandAdapter extends ArrayAdapter { public CommandAdapter(XmppActivity activity) { super(activity, 0); } @@ -21,7 +23,37 @@ public class CommandAdapter extends ArrayAdapter { @Override public View getView(int position, View view, @NonNull ViewGroup parent) { CommandRowBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.command_row, parent, false); - binding.command.setText(getItem(position).getAttribute("name")); + binding.command.setText(getItem(position).getName()); return binding.getRoot(); } + + public interface Command { + public String getName(); + public void start(final ConversationsActivity activity, final Conversation conversation); + } + + public static class Command0050 implements Command { + public final Element el; + public Command0050(Element el) { this.el = el; } + + public String getName() { + return el.getAttribute("name"); + } + + public void start(final ConversationsActivity activity, final Conversation conversation) { + activity.startCommand(conversation.getAccount(), el.getAttributeAsJid("jid"), el.getAttribute("node")); + } + } + + public static class MucConfig implements Command { + public MucConfig() { } + + public String getName() { + return "⚙️ Configure Channel"; + } + + public void start(final ConversationsActivity activity, final Conversation conversation) { + conversation.startMucConfig(activity.xmppConnectionService); + } + } } diff --git a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java index a90bbd804695c2825ed0207214182caf9338f9d2..9f0bd72aeffe7defb4dcae08b9470bfa9ba05590 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java +++ b/src/main/java/eu/siacs/conversations/xmpp/forms/Data.java @@ -81,6 +81,8 @@ public class Data extends Element { } public static Data parse(Element element) { + if (element == null) return null; + Data data = new Data(); data.bindTo(element); return data;