Menu item to refresh features for a contact

Stephen Paul Weber created

Shouldn't be needed in general, so hidden under a sub-menu, but for advanced use
cases and especially services without caps, can enable features that normally
would not be detected.

Change summary

src/main/java/eu/siacs/conversations/entities/Conversation.java          | 17 
src/main/java/eu/siacs/conversations/services/XmppConnectionService.java | 27 
src/main/java/eu/siacs/conversations/ui/ConversationFragment.java        | 60 
src/main/res/menu/fragment_conversation.xml                              |  8 
4 files changed, 81 insertions(+), 31 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/entities/Conversation.java 🔗

@@ -1180,6 +1180,10 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
         pagerAdapter.setupViewPager(pager, tabs);
     }
 
+    public void showViewPager() {
+        pagerAdapter.show();
+    }
+
     public void hideViewPager() {
         pagerAdapter.hide();
     }
@@ -1214,10 +1218,7 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
         public void setupViewPager(ViewPager pager, TabLayout tabs) {
             mPager = pager;
             mTabs = tabs;
-            if (sessions == null) {
-                sessions = new ArrayList<>();
-                notifyDataSetChanged();
-            }
+            show();
             pager.setAdapter(this);
             tabs.setupWithViewPager(mPager);
             pager.setCurrentItem(getCurrentTab());
@@ -1232,6 +1233,14 @@ public class Conversation extends AbstractEntity implements Blockable, Comparabl
             });
         }
 
+        public void show() {
+            if (sessions == null) {
+                sessions = new ArrayList<>();
+                notifyDataSetChanged();
+            }
+            mTabs.setVisibility(View.VISIBLE);
+        }
+
         public void hide() {
             mPager.setCurrentItem(0);
             mTabs.setVisibility(View.GONE);

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

@@ -4673,6 +4673,7 @@ public class XmppConnectionService extends Service {
         if (result != null) {
             return result;
         } else {
+            if (key.first == null || key.second == null) return null;
             result = databaseBackend.findDiscoveryResult(key.first, key.second);
             if (result != null) {
                 discoCache.put(key, result);
@@ -4700,6 +4701,10 @@ public class XmppConnectionService extends Service {
     }
 
     public void fetchCaps(Account account, final Jid jid, final Presence presence) {
+        fetchCaps(account, jid, presence, null);
+    }
+
+    public void fetchCaps(Account account, final Jid jid, final Presence presence, Runnable cb) {
         final Pair<String, String> key = new Pair<>(presence.getHash(), presence.getVer());
         final ServiceDiscoveryResult disco = getCachedServiceDiscoveryResult(key);
 
@@ -4726,14 +4731,15 @@ public class XmppConnectionService extends Service {
             sendIqPacket(account, request, (a, response) -> {
                 if (response.getType() == IqPacket.TYPE.RESULT) {
                     final ServiceDiscoveryResult discoveryResult = new ServiceDiscoveryResult(response);
-                    if (presence.getVer().equals(discoveryResult.getVer())) {
+                    if (presence.getVer() == null || presence.getVer().equals(discoveryResult.getVer())) {
                         databaseBackend.insertDiscoveryResult(discoveryResult);
-                        injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), discoveryResult);
+                        injectServiceDiscoveryResult(a.getRoster(), presence.getHash(), presence.getVer(), jid.getResource(), discoveryResult);
                         if (discoveryResult.hasIdentity("gateway", "pstn")) {
                             final Contact contact = account.getRoster().getContact(jid);
                             contact.registerAsPhoneAccount(this);
                             mQuickConversationsService.considerSyncBackground(false);
                         }
+                        if (cb != null) cb.run();
                     } else {
                         Log.d(Config.LOGTAG, a.getJid().asBareJid() + ": mismatch in caps for contact " + jid + " " + presence.getVer() + " vs " + discoveryResult.getVer());
                     }
@@ -4749,14 +4755,21 @@ public class XmppConnectionService extends Service {
         sendIqPacket(account, request, callback);
     }
 
-    private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
+    private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, String resource, ServiceDiscoveryResult disco) {
         boolean rosterNeedsSync = false;
         for (final Contact contact : roster.getContacts()) {
             boolean serviceDiscoverySet = false;
-            for (final Presence presence : contact.getPresences().getPresences()) {
-                if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
-                    presence.setServiceDiscoveryResult(disco);
-                    serviceDiscoverySet = true;
+            Presence onePresence = contact.getPresences().get(resource == null ? "" : resource);
+            if (onePresence != null) {
+                onePresence.setServiceDiscoveryResult(disco);
+                serviceDiscoverySet = true;
+            }
+            if (hash != null && ver != null) {
+                for (final Presence presence : contact.getPresences().getPresences()) {
+                    if (hash.equals(presence.getHash()) && ver.equals(presence.getVer())) {
+                        presence.setServiceDiscoveryResult(disco);
+                        serviceDiscoverySet = true;
+                    }
                 }
             }
             if (serviceDiscoverySet) {

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

@@ -1527,6 +1527,9 @@ public class ConversationFragment extends XmppFragment
             case R.id.action_toggle_pinned:
                 togglePinned();
                 break;
+            case R.id.action_refresh_feature_discovery:
+                refreshFeatureDiscovery();
+                break;
             default:
                 break;
         }
@@ -1566,6 +1569,17 @@ public class ConversationFragment extends XmppFragment
         }
     }
 
+    private void refreshFeatureDiscovery() {
+        for (Map.Entry<String, Presence> entry : conversation.getContact().getPresences().getPresencesMap().entrySet()) {
+            Jid jid = conversation.getContact().getJid();
+            if (!entry.getKey().equals("")) jid = jid.withResource(entry.getKey());
+            activity.xmppConnectionService.fetchCaps(conversation.getAccount(), jid, entry.getValue(), () -> {
+                if (activity == null) return;
+                activity.runOnUiThread(() -> { refresh(); });
+            });
+        }
+    }
+
     private void togglePinned() {
         final boolean pinned =
                 conversation.getBooleanAttribute(Conversation.ATTRIBUTE_PINNED_ON_TOP, false);
@@ -2510,29 +2524,36 @@ public class ConversationFragment extends XmppFragment
             binding.commandsView.setOnItemClickListener((parent, view, position, id) -> {
                 conversation.startCommand(commandAdapter.getItem(position), activity.xmppConnectionService);
             });
-            Jid commandJid = conversation.getContact().resourceWhichSupport(Namespace.COMMANDS);
-            if (commandJid == null) {
-                conversation.hideViewPager();
-            } else {
-                binding.tabLayout.setVisibility(View.VISIBLE);
-                activity.xmppConnectionService.fetchCommands(conversation.getAccount(), commandJid, (a, iq) -> {
-                    if (activity == null) return;
-
-                    activity.runOnUiThread(() -> {
-                        if (iq.getType() == IqPacket.TYPE.RESULT) {
-                            for (Element child : iq.query().getChildren()) {
-                                if (!"item".equals(child.getName()) || !Namespace.DISCO_ITEMS.equals(child.getNamespace())) continue;
-                                commandAdapter.add(child);
-                            }
+            refreshCommands();
+        }
+
+        return true;
+    }
+
+    protected void refreshCommands() {
+        if (commandAdapter == null) return;
+
+        Jid commandJid = conversation.getContact().resourceWhichSupport(Namespace.COMMANDS);
+        if (commandJid == null) {
+            conversation.hideViewPager();
+        } else {
+            conversation.showViewPager();
+            activity.xmppConnectionService.fetchCommands(conversation.getAccount(), commandJid, (a, iq) -> {
+                if (activity == null) return;
+
+                activity.runOnUiThread(() -> {
+                    if (iq.getType() == IqPacket.TYPE.RESULT) {
+                        commandAdapter.clear();
+                        for (Element child : iq.query().getChildren()) {
+                            if (!"item".equals(child.getName()) || !Namespace.DISCO_ITEMS.equals(child.getNamespace())) continue;
+                            commandAdapter.add(child);
                         }
+                    }
 
-                        if (commandAdapter.getCount() < 1) conversation.hideViewPager();
-                    });
+                    if (commandAdapter.getCount() < 1) conversation.hideViewPager();
                 });
-            }
+            });
         }
-
-        return true;
     }
 
     private void resetUnreadMessagesCount() {
@@ -2833,6 +2854,7 @@ public class ConversationFragment extends XmppFragment
                 }
                 updateSendButton();
                 updateEditablity();
+                refreshCommands();
             }
         }
     }

src/main/res/menu/fragment_conversation.xml 🔗

@@ -135,8 +135,14 @@
                 android:orderInCategory="73"
                 android:title="@string/add_to_favorites"
                 app:showAsAction="never" />
+
+            <item
+                android:id="@+id/action_refresh_feature_discovery"
+                android:orderInCategory="74"
+                android:title="Refresh Feature Discovery"
+                app:showAsAction="never" />
         </menu>
     </item>
 
 
-</menu>
+</menu>