move server command storage into manager

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 14 
src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java      |  3 
src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java     | 52 
src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java            | 53 
src/main/java/eu/siacs/conversations/xmpp/manager/DiscoManager.java      | 39 
5 files changed, 76 insertions(+), 85 deletions(-)

Detailed changes

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

@@ -1889,18 +1889,16 @@ public class XmppConnectionService extends Service {
 
     public void requestEasyOnboardingInvite(
             final Account account, final EasyOnboardingInvite.OnInviteRequested callback) {
-        final XmppConnection connection = account.getXmppConnection();
-        final Jid jid =
-                connection == null
-                        ? null
-                        : connection.getJidForCommand(Namespace.EASY_ONBOARDING_INVITE);
-        if (jid == null) {
+        final var connection = account.getXmppConnection();
+        final var discoManager = connection.getManager(DiscoManager.class);
+        final var address = discoManager.getAddressForCommand(Namespace.EASY_ONBOARDING_INVITE);
+        if (address == null) {
             callback.inviteRequestFailed(
                     getString(R.string.server_does_not_support_easy_onboarding_invites));
             return;
         }
         final Iq request = new Iq(Iq.Type.SET);
-        request.setTo(jid);
+        request.setTo(address);
         final Element command = request.addChild("command", Namespace.COMMANDS);
         command.setAttribute("node", Namespace.EASY_ONBOARDING_INVITE);
         command.setAttribute("action", "execute");
@@ -1922,7 +1920,7 @@ public class XmppConnectionService extends Service {
                             if (uri != null) {
                                 final EasyOnboardingInvite invite =
                                         new EasyOnboardingInvite(
-                                                jid.getDomain().toString(), uri, landingUrl);
+                                                address.getDomain().toString(), uri, landingUrl);
                                 callback.inviteRequested(invite);
                                 return;
                             }

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

@@ -523,8 +523,7 @@ public class ContactDetailsActivity extends OmemoActivity
         final String account = contact.getAccount().getJid().asBareJid().toString();
         binding.detailsAccount.setOnClickListener(this::onDetailsAccountClicked);
         binding.detailsAccount.setText(getString(R.string.using_account, account));
-        AvatarWorkerTask.loadAvatar(
-                contact, binding.detailsAvatar, R.dimen.publish_avatar_size);
+        AvatarWorkerTask.loadAvatar(contact, binding.detailsAvatar, R.dimen.publish_avatar_size);
         binding.detailsAvatar.setOnClickListener(this::onAvatarClicked);
         if (QuickConversationsService.isContactListIntegration(this)) {
             if (contact.getSystemAccount() == null) {

src/main/java/eu/siacs/conversations/utils/EasyOnboardingInvite.java 🔗

@@ -2,17 +2,16 @@ package eu.siacs.conversations.utils;
 
 import android.os.Parcel;
 import android.os.Parcelable;
-
 import com.google.common.base.Strings;
 import com.google.common.collect.ImmutableList;
-
-import java.util.Collections;
-import java.util.List;
-
 import eu.siacs.conversations.entities.Account;
 import eu.siacs.conversations.services.QuickConversationsService;
 import eu.siacs.conversations.services.XmppConnectionService;
-import eu.siacs.conversations.xmpp.XmppConnection;
+import eu.siacs.conversations.xml.Namespace;
+import eu.siacs.conversations.xmpp.manager.DiscoManager;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
 
 public class EasyOnboardingInvite implements Parcelable {
 
@@ -44,50 +43,53 @@ public class EasyOnboardingInvite implements Parcelable {
         return 0;
     }
 
-    public static final Creator<EasyOnboardingInvite> CREATOR = new Creator<EasyOnboardingInvite>() {
-        @Override
-        public EasyOnboardingInvite createFromParcel(Parcel in) {
-            return new EasyOnboardingInvite(in);
-        }
+    public static final Creator<EasyOnboardingInvite> CREATOR =
+            new Creator<EasyOnboardingInvite>() {
+                @Override
+                public EasyOnboardingInvite createFromParcel(Parcel in) {
+                    return new EasyOnboardingInvite(in);
+                }
 
-        @Override
-        public EasyOnboardingInvite[] newArray(int size) {
-            return new EasyOnboardingInvite[size];
-        }
-    };
+                @Override
+                public EasyOnboardingInvite[] newArray(int size) {
+                    return new EasyOnboardingInvite[size];
+                }
+            };
 
     public static boolean anyHasSupport(final XmppConnectionService service) {
         if (QuickConversationsService.isQuicksy()) {
             return false;
         }
-        return getSupportingAccounts(service).size() > 0;
-
+        return !getSupportingAccounts(service).isEmpty();
     }
 
     public static List<Account> getSupportingAccounts(final XmppConnectionService service) {
-        final ImmutableList.Builder<Account> supportingAccountsBuilder = new ImmutableList.Builder<>();
-        final List<Account> accounts = service == null ? Collections.emptyList() : service.getAccounts();
-        for(Account account : accounts) {
-            final XmppConnection xmppConnection = account.getXmppConnection();
-            if (xmppConnection != null && xmppConnection.getFeatures().easyOnboardingInvites()) {
+        final ImmutableList.Builder<Account> supportingAccountsBuilder =
+                new ImmutableList.Builder<>();
+        final List<Account> accounts =
+                service == null ? Collections.emptyList() : service.getAccounts();
+        for (final var account : accounts) {
+            final var connection = account.getXmppConnection();
+            final var discoManager = connection.getManager(DiscoManager.class);
+            if (Objects.nonNull(
+                    discoManager.getAddressForCommand(Namespace.EASY_ONBOARDING_INVITE))) {
                 supportingAccountsBuilder.add(account);
             }
         }
         return supportingAccountsBuilder.build();
     }
 
-
     public String getShareableLink() {
         return Strings.isNullOrEmpty(landingUrl) ? uri : landingUrl;
     }
 
-
     public String getDomain() {
         return domain;
     }
 
     public interface OnInviteRequested {
         void inviteRequested(EasyOnboardingInvite invite);
+
         void inviteRequestFailed(String message);
     }
 }

src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java 🔗

@@ -131,12 +131,10 @@ import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -161,7 +159,6 @@ public class XmppConnection implements Runnable {
 
     protected final Account account;
     private final Features features = new Features(this);
-    private final HashMap<String, Jid> commands = new HashMap<>();
     private final SparseArray<Stanza> mStanzaQueue = new SparseArray<>();
     private final Hashtable<String, Pair<Iq, Consumer<Iq>>> packetCallbacks = new Hashtable<>();
     private final Set<OnAdvancedStreamFeaturesLoaded> advancedStreamFeaturesLoadedListeners =
@@ -295,12 +292,6 @@ public class XmppConnection implements Runnable {
         this.changeState(state, false);
     }
 
-    public Jid getJidForCommand(final String node) {
-        synchronized (this.commands) {
-            return this.commands.get(node);
-        }
-    }
-
     public void prepareNewConnection() {
         this.lastConnectionStarted = SystemClock.elapsedRealtime();
         this.lastPingSent = SystemClock.elapsedRealtime();
@@ -1957,9 +1948,6 @@ public class XmppConnection implements Runnable {
         }
         this.redirectionUrl = null;
         getManager(DiscoManager.class).clear();
-        synchronized (this.commands) {
-            this.commands.clear();
-        }
         this.loginInfo = null;
     }
 
@@ -2255,32 +2243,6 @@ public class XmppConnection implements Runnable {
                 });
     }
 
-    private void discoverCommands() {
-        // TODO move result handling into DiscoManager too
-        final var future =
-                getManager(DiscoManager.class).commands(Entity.discoItem(account.getDomain()));
-        Futures.addCallback(
-                future,
-                new FutureCallback<>() {
-                    @Override
-                    public void onSuccess(Map<String, Jid> result) {
-                        synchronized (XmppConnection.this.commands) {
-                            XmppConnection.this.commands.clear();
-                            XmppConnection.this.commands.putAll(result);
-                        }
-                    }
-
-                    @Override
-                    public void onFailure(@NonNull Throwable throwable) {
-                        Log.d(
-                                Config.LOGTAG,
-                                account.getJid().asBareJid() + ": could not fetch commands",
-                                throwable);
-                    }
-                },
-                MoreExecutors.directExecutor());
-    }
-
     public boolean isMamPreferenceAlways() {
         return isMamPreferenceAlways;
     }
@@ -2312,8 +2274,9 @@ public class XmppConnection implements Runnable {
         if (carbonsManager.hasFeature() && !carbonsManager.isEnabled()) {
             carbonsManager.enable();
         }
-        if (getFeatures().commands()) {
-            discoverCommands();
+        final var discoManager = getManager(DiscoManager.class);
+        if (discoManager.hasServerCommands()) {
+            discoManager.fetchServerCommands();
         }
     }
 
@@ -2998,16 +2961,6 @@ public class XmppConnection implements Runnable {
             return infoQuery != null && infoQuery.getFeatureStrings().contains(feature);
         }
 
-        public boolean commands() {
-            return hasDiscoFeature(account.getDomain(), Namespace.COMMANDS);
-        }
-
-        public boolean easyOnboardingInvites() {
-            synchronized (commands) {
-                return commands.containsKey(Namespace.EASY_ONBOARDING_INVITE);
-            }
-        }
-
         public boolean blocking() {
             return connection.getManager(BlockingManager.class).hasFeature();
         }

src/main/java/eu/siacs/conversations/xmpp/manager/DiscoManager.java 🔗

@@ -10,6 +10,7 @@ import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Maps;
 import com.google.common.io.BaseEncoding;
+import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
 import com.google.common.util.concurrent.MoreExecutors;
@@ -91,6 +92,7 @@ public class DiscoManager extends AbstractManager {
 
     private final Map<Jid, InfoQuery> entityInformation = new HashMap<>();
     private final Map<Jid, ImmutableSet<Jid>> discoItems = new HashMap<>();
+    private final Map<String, Jid> commands = new HashMap<>();
 
     public DiscoManager(Context context, XmppConnection connection) {
         super(context, connection);
@@ -447,10 +449,19 @@ public class DiscoManager extends AbstractManager {
         }
     }
 
+    public Jid getAddressForCommand(final String node) {
+        synchronized (this.commands) {
+            return this.commands.get(node);
+        }
+    }
+
     public void clear() {
         synchronized (this.entityInformation) {
             this.entityInformation.clear();
         }
+        synchronized (this.commands) {
+            this.commands.clear();
+        }
     }
 
     public void clear(final Jid address) {
@@ -478,6 +489,34 @@ public class DiscoManager extends AbstractManager {
         return Iterables.getFirst(items.entrySet(), null);
     }
 
+    public boolean hasServerCommands() {
+        return hasServerFeature(Namespace.COMMANDS);
+    }
+
+    public void fetchServerCommands() {
+        final var future = commands(Entity.discoItem(getAccount().getDomain()));
+        Futures.addCallback(
+                future,
+                new FutureCallback<>() {
+                    @Override
+                    public void onSuccess(Map<String, Jid> result) {
+                        synchronized (commands) {
+                            commands.clear();
+                            commands.putAll(result);
+                        }
+                    }
+
+                    @Override
+                    public void onFailure(@androidx.annotation.NonNull Throwable throwable) {
+                        Log.d(
+                                Config.LOGTAG,
+                                getAccount().getJid().asBareJid() + ": could not fetch commands",
+                                throwable);
+                    }
+                },
+                MoreExecutors.directExecutor());
+    }
+
     public static final class CapsHashMismatchException extends IllegalStateException {
         public CapsHashMismatchException(final String message) {
             super(message);