If a MUC address is entered as a contact, join it

Stephen Paul Weber created

Change summary

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 12 
src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java           |  2 
src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java       |  2 
src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java              | 20 
src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java   | 33 
src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java          |  6 
src/main/java/eu/siacs/conversations/utils/Consumer.java                 |  6 
7 files changed, 54 insertions(+), 27 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java 🔗

@@ -134,6 +134,7 @@ import eu.siacs.conversations.ui.interfaces.OnMediaLoaded;
 import eu.siacs.conversations.ui.interfaces.OnSearchResultsAvailable;
 import eu.siacs.conversations.utils.AccountUtils;
 import eu.siacs.conversations.utils.Compatibility;
+import eu.siacs.conversations.utils.Consumer;
 import eu.siacs.conversations.utils.ConversationsFileObserver;
 import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.utils.EasyOnboardingInvite;
@@ -3399,6 +3400,17 @@ public class XmppConnectionService extends Service {
         }
     }
 
+    public void checkIfMuc(final Account account, final Jid jid, Consumer<Boolean> cb) {
+        IqPacket request = mIqGenerator.queryDiscoInfo(jid.asBareJid());
+        sendIqPacket(account, request, (acct, reply) -> {
+            ServiceDiscoveryResult result = new ServiceDiscoveryResult(reply);
+            cb.accept(
+                result.getFeatures().contains("http://jabber.org/protocol/muc") &&
+                result.hasIdentity("conference", null)
+            );
+        });
+    }
+
     public void fetchConferenceConfiguration(final Conversation conversation) {
         fetchConferenceConfiguration(conversation, null);
     }

src/main/java/eu/siacs/conversations/ui/BlocklistActivity.java 🔗

@@ -82,7 +82,7 @@ public class BlocklistActivity extends AbstractSearchableListItemActivity implem
 				null,
 				account.getJid().asBareJid().toEscapedString(),
 				true,
-				false
+				EnterJidDialog.SanityCheck.NO
 		);
 
 		dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {

src/main/java/eu/siacs/conversations/ui/ChooseContactActivity.java 🔗

@@ -329,7 +329,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
                 jid == null ? null : jid.asBareJid().toString(),
                 getIntent().getStringExtra(EXTRA_ACCOUNT),
                 true,
-                false
+                EnterJidDialog.SanityCheck.NO
         );
 
         dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {

src/main/java/eu/siacs/conversations/ui/EnterJidDialog.java 🔗

@@ -68,11 +68,17 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
 
     private EnterJidDialogBinding binding;
     private AlertDialog dialog;
-    private boolean sanityCheckJid = false;
+    private SanityCheck sanityCheckJid = SanityCheck.NO;
 
     private boolean issuedWarning = false;
     private GatewayListAdapter gatewayListAdapter = new GatewayListAdapter();
 
+    public static enum SanityCheck {
+        NO,
+        YES,
+        ALLOW_MUC
+    }
+
     public static EnterJidDialog newInstance(
             final List<String> activatedAccounts,
             final String title,
@@ -80,7 +86,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
             final String prefilledJid,
             final String account,
             boolean allowEditJid,
-            final boolean sanity_check_jid) {
+            final SanityCheck sanity_check_jid) {
         EnterJidDialog dialog = new EnterJidDialog();
         Bundle bundle = new Bundle();
         bundle.putString(TITLE_KEY, title);
@@ -89,7 +95,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
         bundle.putString(ACCOUNT_KEY, account);
         bundle.putBoolean(ALLOW_EDIT_JID_KEY, allowEditJid);
         bundle.putStringArrayList(ACCOUNTS_LIST_KEY, (ArrayList<String>) activatedAccounts);
-        bundle.putBoolean(SANITY_CHECK_JID, sanity_check_jid);
+        bundle.putInt(SANITY_CHECK_JID, sanity_check_jid.ordinal());
         dialog.setArguments(bundle);
         return dialog;
     }
@@ -131,7 +137,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                 binding.jid.setCursorVisible(false);
             }
         }
-        sanityCheckJid = getArguments().getBoolean(SANITY_CHECK_JID, false);
+        sanityCheckJid = SanityCheck.values()[getArguments().getInt(SANITY_CHECK_JID, SanityCheck.NO.ordinal())];
 
         DelayedHintHelper.setHint(R.string.account_settings_example_jabber_id, binding.jid);
 
@@ -236,7 +242,7 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                     return;
                 }
 
-                final Jid contactJid;
+                Jid contactJid = null;
                 try {
                     contactJid = Jid.ofEscaped(jidString);
                 } catch (final IllegalArgumentException e) {
@@ -244,14 +250,14 @@ public class EnterJidDialog extends DialogFragment implements OnBackendConnected
                     return;
                 }
 
-                if (!issuedWarning && sanityCheckJid) {
+                if (!issuedWarning && sanityCheckJid != SanityCheck.NO) {
                     if (contactJid.isDomainJid()) {
                         binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_a_domain));
                         dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
                         issuedWarning = true;
                         return;
                     }
-                    if (suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
+                    if (sanityCheckJid != SanityCheck.ALLOW_MUC && suspiciousSubDomain(contactJid.getDomain().toEscapedString())) {
                         binding.jidLayout.setError(getActivity().getString(R.string.this_looks_like_channel));
                         dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.add_anway);
                         issuedWarning = true;

src/main/java/eu/siacs/conversations/ui/StartConversationActivity.java 🔗

@@ -536,7 +536,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
                 prefilledJid,
                 invite == null ? null : invite.account,
                 invite == null || !invite.hasFingerprints(),
-                true
+                EnterJidDialog.SanityCheck.ALLOW_MUC
         );
 
         dialog.setOnEnterJidDialogPositiveListener((accountJid, contactJid) -> {
@@ -548,25 +548,32 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
             if (account == null) {
                 return true;
             }
-
             final Contact contact = account.getRoster().getContact(contactJid);
+
             if (invite != null && invite.getName() != null) {
                 contact.setServerName(invite.getName());
             }
-            if (contact.isSelf()) {
-                switchToConversation(contact);
-                return true;
-            } else if (contact.showInRoster()) {
-                throw new EnterJidDialog.JidError(getString(R.string.contact_already_exists));
-            } else {
-                final String preAuth = invite == null ? null : invite.getParameter(XmppUri.PARAMETER_PRE_AUTH);
-                xmppConnectionService.createContact(contact, true, preAuth);
-                if (invite != null && invite.hasFingerprints()) {
-                    xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
-                }
+
+            if (contact.isSelf() || contact.showInRoster()) {
                 switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody());
                 return true;
             }
+
+            xmppConnectionService.checkIfMuc(account, contactJid, (isMuc) -> {
+                if (isMuc) {
+                    final Conversation conversation = xmppConnectionService.findOrCreateConversation(account, contactJid, true, true, true);
+                    switchToConversationDoNotAppend(conversation, invite == null ? null : invite.getBody());
+                } else {
+                    final String preAuth = invite == null ? null : invite.getParameter(XmppUri.PARAMETER_PRE_AUTH);
+                    xmppConnectionService.createContact(contact, true, preAuth);
+                    if (invite != null && invite.hasFingerprints()) {
+                        xmppConnectionService.verifyFingerprints(contact, invite.getFingerprints());
+                    }
+                    switchToConversationDoNotAppend(contact, invite == null ? null : invite.getBody());
+                }
+            });
+
+            return true;
         });
         dialog.show(ft, FRAGMENT_TAG_DIALOG);
     }

src/main/java/eu/siacs/conversations/ui/widget/DialpadView.java 🔗

@@ -26,6 +26,7 @@ import androidx.constraintlayout.widget.ConstraintLayout;
 import androidx.databinding.DataBindingUtil;
 import eu.siacs.conversations.databinding.DialpadBinding;
 import eu.siacs.conversations.R;
+import eu.siacs.conversations.utils.Consumer;
 
 public class DialpadView extends ConstraintLayout implements View.OnClickListener {
 
@@ -64,9 +65,4 @@ public class DialpadView extends ConstraintLayout implements View.OnClickListene
     public void onClick(View v) {
         clickConsumer.accept(v.getTag().toString());
     }
-
-	// Based on java.util.function.Consumer to avoid Android 24 dependency
-	public interface Consumer<T> {
-		void accept(T t);
-	}
 }