Detailed changes
@@ -552,7 +552,14 @@ public class IqGenerator extends AbstractGenerator {
public IqPacket queryDiscoItems(Jid jid) {
IqPacket packet = new IqPacket(IqPacket.TYPE.GET);
packet.setTo(jid);
- packet.addChild("query",Namespace.DISCO_ITEMS);
+ packet.query(Namespace.DISCO_ITEMS);
+ return packet;
+ }
+
+ public IqPacket queryDiscoItems(Jid jid, String node) {
+ IqPacket packet = queryDiscoItems(jid);
+ final Element query = packet.query(Namespace.DISCO_ITEMS);
+ query.setAttribute("node", node);
return packet;
}
@@ -4744,6 +4744,11 @@ public class XmppConnectionService extends Service {
}
}
+ public void fetchCommands(Account account, final Jid jid, OnIqPacketReceived callback) {
+ final IqPacket request = mIqGenerator.queryDiscoItems(jid, "http://jabber.org/protocol/commands");
+ sendIqPacket(account, request, callback);
+ }
+
private void injectServiceDiscoveryResult(Roster roster, String hash, String ver, ServiceDiscoveryResult disco) {
boolean rosterNeedsSync = false;
for (final Contact contact : roster.getContacts()) {
@@ -55,11 +55,14 @@ import android.widget.Toast;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.core.view.inputmethod.InputConnectionCompat;
import androidx.core.view.inputmethod.InputContentInfoCompat;
import androidx.databinding.DataBindingUtil;
+import androidx.viewpager.widget.PagerAdapter;
+import androidx.viewpager.widget.ViewPager;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
@@ -73,6 +76,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -92,6 +96,7 @@ import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.entities.MucOptions;
import eu.siacs.conversations.entities.MucOptions.User;
import eu.siacs.conversations.entities.Presence;
+import eu.siacs.conversations.entities.Presences;
import eu.siacs.conversations.entities.ReadByMarker;
import eu.siacs.conversations.entities.Transferable;
import eu.siacs.conversations.entities.TransferablePlaceholder;
@@ -100,6 +105,7 @@ import eu.siacs.conversations.persistance.FileBackend;
import eu.siacs.conversations.services.MessageArchiveService;
import eu.siacs.conversations.services.QuickConversationsService;
import eu.siacs.conversations.services.XmppConnectionService;
+import eu.siacs.conversations.ui.adapter.CommandAdapter;
import eu.siacs.conversations.ui.adapter.MediaPreviewAdapter;
import eu.siacs.conversations.ui.adapter.MessageAdapter;
import eu.siacs.conversations.ui.util.ActivityResult;
@@ -129,6 +135,7 @@ import eu.siacs.conversations.utils.QuickLoader;
import eu.siacs.conversations.utils.StylingHelper;
import eu.siacs.conversations.utils.TimeFrameUtils;
import eu.siacs.conversations.utils.UIHelper;
+import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.Namespace;
import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.XmppConnection;
@@ -139,6 +146,7 @@ import eu.siacs.conversations.xmpp.jingle.JingleFileTransferConnection;
import eu.siacs.conversations.xmpp.jingle.Media;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
import eu.siacs.conversations.xmpp.jingle.RtpCapability;
+import eu.siacs.conversations.xmpp.stanzas.IqPacket;
public class ConversationFragment extends XmppFragment
implements EditMessage.KeyboardListener,
@@ -185,6 +193,7 @@ public class ConversationFragment extends XmppFragment
private final PendingItem<Message> pendingMessage = new PendingItem<>();
public Uri mPendingEditorContent = null;
protected MessageAdapter messageListAdapter;
+ protected CommandAdapter commandAdapter;
private MediaPreviewAdapter mediaPreviewAdapter;
private String lastMessageUuid = null;
private Conversation conversation;
@@ -1234,6 +1243,39 @@ public class ConversationFragment extends XmppFragment
new EditMessageActionModeCallback(this.binding.textinput));
}
+ binding.conversationViewPager.setAdapter(new StaticPagerAdapter(
+ binding.conversationViewPager
+ ));
+ binding.tabLayout.setupWithViewPager(binding.conversationViewPager);
+
+ commandAdapter = new CommandAdapter((XmppActivity) getActivity());
+ binding.commandsView.setAdapter(commandAdapter);
+ Presences presences = conversation.getContact().getPresences();
+ for (Map.Entry<String, Presence> entry : presences.getPresencesMap().entrySet()) {
+ String resource = entry.getKey();
+ Presence presence = entry.getValue();
+ if (presence.getServiceDiscoveryResult().getFeatures().contains("http://jabber.org/protocol/commands")) {
+ binding.tabLayout.setVisibility(View.VISIBLE);
+ binding.conversationViewPager.setCurrentItem(1);
+ Jid jid = conversation.getContact().getJid();
+ if (resource != null && !resource.equals("")) jid = jid.withResource(resource);
+ activity.xmppConnectionService.fetchCommands(conversation.getAccount(), jid, (a, iq) -> {
+ if (iq.getType() == IqPacket.TYPE.RESULT) {
+ activity.runOnUiThread(() -> {
+ for (Element child : iq.query().getChildren()) {
+ if (!"item".equals(child.getName()) || !Namespace.DISCO_ITEMS.equals(child.getNamespace())) continue;
+ commandAdapter.add(child);
+ }
+ });
+ } else {
+ binding.tabLayout.setVisibility(View.GONE);
+ binding.conversationViewPager.setCurrentItem(0);
+ }
+ });
+ break;
+ }
+ }
+
return binding.getRoot();
}
@@ -3604,4 +3646,41 @@ public class ConversationFragment extends XmppFragment
}
return activity;
}
+
+ public class StaticPagerAdapter extends PagerAdapter {
+ ViewPager mPager;
+
+ StaticPagerAdapter(ViewPager pager) {
+ mPager = pager;
+ }
+
+ @NonNull
+ @Override
+ public View instantiateItem(@NonNull ViewGroup container, int position) {
+ return mPager.getChildAt(position);
+ }
+
+ @Override
+ public int getCount() {
+ return 2;
+ }
+
+ @Override
+ public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
+ return view == o;
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getPageTitle(int position) {
+ switch (position) {
+ case 0:
+ return "Conversation";
+ case 1:
+ return "Commands";
+ default:
+ return super.getPageTitle(position);
+ }
+ }
+ }
}
@@ -0,0 +1,27 @@
+package eu.siacs.conversations.ui.adapter;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+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;
+
+public class CommandAdapter extends ArrayAdapter<Element> {
+ public CommandAdapter(XmppActivity activity) {
+ super(activity, 0);
+ }
+
+ @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"));
+ return binding.getRoot();
+ }
+}
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?selectableItemBackground"
+ android:paddingLeft="8dp"
+ android:paddingBottom="8dp"
+ android:paddingTop="8dp">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:orientation="vertical"
+ android:paddingLeft="@dimen/avatar_item_distance">
+
+ <TextView
+ android:id="@+id/command"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:scrollHorizontally="false"
+ android:singleLine="true"
+ android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
+
+ <TextView
+ android:id="@+id/description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.Conversations.Body2" />
+ </LinearLayout>
+
+ </RelativeLayout>
+</layout>
@@ -7,123 +7,162 @@
android:layout_height="match_parent"
android:background="?attr/color_background_secondary">
- <ListView
- android:id="@+id/messages_view"
- android:layout_width="fill_parent"
+ <com.google.android.material.tabs.TabLayout
+ android:visibility="gone"
+ android:id="@+id/tab_layout"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="?attr/colorPrimary"
+ android:elevation="@dimen/toolbar_elevation"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+ app:tabGravity="fill"
+ app:tabIndicatorColor="@color/white87"
+ app:tabMode="fixed"
+ app:tabSelectedTextColor="@color/white"
+ app:tabTextColor="@color/white70" />
+
+ <androidx.viewpager.widget.ViewPager
+ android:id="@+id/conversation_view_pager"
+ android:layout_below="@id/tab_layout"
android:layout_above="@+id/snackbar"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:background="?attr/color_background_secondary"
- android:divider="@null"
- android:dividerHeight="0dp"
- android:listSelector="@android:color/transparent"
- android:stackFromBottom="true"
- android:transcriptMode="normal"
- tools:listitem="@layout/message_sent"></ListView>
-
- <com.google.android.material.floatingactionbutton.FloatingActionButton
- android:id="@+id/scroll_to_bottom_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignBottom="@+id/messages_view"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:alpha="0.85"
- android:src="?attr/icon_scroll_down"
- android:visibility="gone"
- app:backgroundTint="?attr/color_background_primary"
- app:fabSize="mini"
- app:useCompatPadding="true" />
-
- <eu.siacs.conversations.ui.widget.UnreadCountCustomView
- android:id="@+id/unread_count_custom_view"
- android:layout_width="?attr/IconSize"
- android:layout_height="?attr/IconSize"
- android:layout_alignTop="@+id/scroll_to_bottom_button"
- android:layout_alignEnd="@+id/scroll_to_bottom_button"
- android:layout_alignRight="@+id/scroll_to_bottom_button"
- android:layout_marginTop="16dp"
- android:layout_marginEnd="8dp"
- android:layout_marginRight="8dp"
- android:elevation="8dp"
- android:visibility="gone"
- app:backgroundColor="?attr/unread_count"
- tools:ignore="RtlCompat" />
-
- <RelativeLayout
- android:id="@+id/textsend"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
+ android:layout_height="fill_parent"
android:background="?attr/color_background_primary">
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentStart="true"
- android:layout_alignParentLeft="true"
- android:layout_toStartOf="@+id/textSendButton"
- android:layout_toLeftOf="@+id/textSendButton"
- android:orientation="vertical">
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
- <TextView
- android:id="@+id/text_input_hint"
- android:layout_width="wrap_content"
+ <ListView
+ android:id="@+id/messages_view"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_above="@+id/textsend"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="?attr/color_background_secondary"
+ android:divider="@null"
+ android:dividerHeight="0dp"
+ android:listSelector="@android:color/transparent"
+ android:stackFromBottom="true"
+ android:transcriptMode="normal"
+ tools:listitem="@layout/message_sent"></ListView>
+
+ <RelativeLayout
+ android:id="@+id/textsend"
+ android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:maxLines="1"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:textAppearance="@style/TextAppearance.Conversations.Caption.Highlight"
- android:visibility="gone" />
-
- <androidx.recyclerview.widget.RecyclerView
- android:id="@+id/media_preview"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:background="?attr/color_background_primary">
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_toStartOf="@+id/textSendButton"
+ android:layout_toLeftOf="@+id/textSendButton"
+ android:orientation="vertical">
+
+ <TextView
+ android:id="@+id/text_input_hint"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:maxLines="1"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:textAppearance="@style/TextAppearance.Conversations.Caption.Highlight"
+ android:visibility="gone" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/media_preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingTop="8dp"
+ android:requiresFadingEdge="horizontal"
+ android:visibility="gone"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
+ tools:listitem="@layout/media_preview">
+
+ </androidx.recyclerview.widget.RecyclerView>
+
+ <eu.siacs.conversations.ui.widget.EditMessage
+ android:id="@+id/textinput"
+ style="@style/Widget.Conversations.EditText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/color_background_primary"
+ android:ems="10"
+ android:imeOptions="flagNoExtractUi|actionSend"
+ android:inputType="textShortMessage|textMultiLine|textCapSentences"
+ android:maxLines="8"
+ android:minHeight="48dp"
+ android:minLines="1"
+ android:padding="8dp">
+
+ <requestFocus />
+ </eu.siacs.conversations.ui.widget.EditMessage>
+
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/textSendButton"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ android:background="?attr/color_background_primary"
+ android:contentDescription="@string/send_message"
+ android:src="?attr/ic_send_text_offline" />
+ </RelativeLayout>
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/scroll_to_bottom_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:paddingTop="8dp"
- android:requiresFadingEdge="horizontal"
+ android:layout_alignBottom="@+id/messages_view"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:alpha="0.85"
+ android:src="?attr/icon_scroll_down"
android:visibility="gone"
- app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- tools:listitem="@layout/media_preview">
+ app:backgroundTint="?attr/color_background_primary"
+ app:fabSize="mini"
+ app:useCompatPadding="true" />
+
+ <eu.siacs.conversations.ui.widget.UnreadCountCustomView
+ android:id="@+id/unread_count_custom_view"
+ android:layout_width="?attr/IconSize"
+ android:layout_height="?attr/IconSize"
+ android:layout_alignTop="@+id/scroll_to_bottom_button"
+ android:layout_alignEnd="@+id/scroll_to_bottom_button"
+ android:layout_alignRight="@+id/scroll_to_bottom_button"
+ android:layout_marginTop="16dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginRight="8dp"
+ android:elevation="8dp"
+ android:visibility="gone"
+ app:backgroundColor="?attr/unread_count"
+ tools:ignore="RtlCompat" />
+ </RelativeLayout>
- </androidx.recyclerview.widget.RecyclerView>
+ <ListView
+ android:id="@+id/commands_view"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:background="?attr/color_background_secondary"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"></ListView>
- <eu.siacs.conversations.ui.widget.EditMessage
- android:id="@+id/textinput"
- style="@style/Widget.Conversations.EditText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="?attr/color_background_primary"
- android:ems="10"
- android:imeOptions="flagNoExtractUi|actionSend"
- android:inputType="textShortMessage|textMultiLine|textCapSentences"
- android:maxLines="8"
- android:minHeight="48dp"
- android:minLines="1"
- android:padding="8dp">
-
- <requestFocus />
- </eu.siacs.conversations.ui.widget.EditMessage>
-
- </LinearLayout>
-
- <ImageButton
- android:id="@+id/textSendButton"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentRight="true"
- android:layout_centerVertical="true"
- android:background="?attr/color_background_primary"
- android:contentDescription="@string/send_message"
- android:src="?attr/ic_send_text_offline" />
- </RelativeLayout>
+ </androidx.viewpager.widget.ViewPager>
<RelativeLayout
android:id="@+id/snackbar"
@@ -167,4 +206,4 @@
</RelativeLayout>
</RelativeLayout>
-</layout>
+</layout>