From eb8b8d63681994e7610a6f6c8502d8be0619164b Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Mon, 1 Aug 2022 15:40:21 -0500 Subject: [PATCH] Show any commands pushed along with a message --- src/cheogram/res/layout/command_button.xml | 8 ++++ .../entities/IndividualMessage.java | 2 +- .../siacs/conversations/entities/Message.java | 47 +++++++++++++++++-- .../conversations/parser/MessageParser.java | 5 ++ .../persistance/DatabaseBackend.java | 8 ++++ .../ui/adapter/CommandButtonAdapter.java | 29 ++++++++++++ .../ui/adapter/MessageAdapter.java | 15 ++++++ src/main/res/layout/message_content.xml | 10 +++- 8 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 src/cheogram/res/layout/command_button.xml create mode 100644 src/main/java/eu/siacs/conversations/ui/adapter/CommandButtonAdapter.java diff --git a/src/cheogram/res/layout/command_button.xml b/src/cheogram/res/layout/command_button.xml new file mode 100644 index 000000000..9f0df64c2 --- /dev/null +++ b/src/cheogram/res/layout/command_button.xml @@ -0,0 +1,8 @@ + + + + diff --git a/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java b/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java index a9a62237a..b16de580d 100644 --- a/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java +++ b/src/main/java/eu/siacs/conversations/entities/IndividualMessage.java @@ -44,7 +44,7 @@ public class IndividualMessage extends Message { } private IndividualMessage(Conversational conversation, String uuid, String conversationUUid, Jid counterpart, Jid trueCounterpart, String body, long timeSent, int encryption, int status, int type, boolean carbon, String remoteMsgId, String relativeFilePath, String serverMsgId, String fingerprint, boolean read, String edited, boolean oob, String errorMessage, Set readByMarkers, boolean markable, boolean deleted, String bodyLanguage) { - super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable, deleted, bodyLanguage, null, null, null); + super(conversation, uuid, conversationUUid, counterpart, trueCounterpart, body, timeSent, encryption, status, type, carbon, remoteMsgId, relativeFilePath, serverMsgId, fingerprint, read, edited, oob, errorMessage, readByMarkers, markable, deleted, bodyLanguage, null, null, null, null); } @Override diff --git a/src/main/java/eu/siacs/conversations/entities/Message.java b/src/main/java/eu/siacs/conversations/entities/Message.java index 739a54ee6..016f101da 100644 --- a/src/main/java/eu/siacs/conversations/entities/Message.java +++ b/src/main/java/eu/siacs/conversations/entities/Message.java @@ -6,6 +6,7 @@ import android.graphics.Color; import android.text.SpannableStringBuilder; import android.util.Log; +import com.google.common.io.ByteSource; import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.primitives.Longs; @@ -13,6 +14,7 @@ import com.google.common.primitives.Longs; import org.json.JSONException; import java.lang.ref.WeakReference; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; @@ -20,6 +22,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Set; +import java.util.stream.Collectors; import java.util.concurrent.CopyOnWriteArraySet; import eu.siacs.conversations.Config; @@ -35,6 +38,9 @@ import eu.siacs.conversations.utils.MessageUtils; import eu.siacs.conversations.utils.MimeUtils; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.Jid; +import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xml.Tag; +import eu.siacs.conversations.xml.XmlReader; public class Message extends AbstractEntity implements AvatarService.Avatarable { @@ -107,6 +113,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable protected boolean carbon = false; private boolean oob = false; protected URI oobUri = null; + protected List payloads = new ArrayList<>(); protected List edits = new ArrayList<>(); protected String relativeFilePath; protected boolean read = true; @@ -161,6 +168,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable null, null, null, + null, null); } @@ -189,6 +197,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable null, null, null, + null, null); } @@ -198,7 +207,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable final String remoteMsgId, final String relativeFilePath, final String serverMsgId, final String fingerprint, final boolean read, final String edited, final boolean oob, final String errorMessage, final Set readByMarkers, - final boolean markable, final boolean deleted, final String bodyLanguage, final String subject, final String oobUri, final String fileParams) { + final boolean markable, final boolean deleted, final String bodyLanguage, final String subject, final String oobUri, final String fileParams, final List payloads) { this.conversation = conversation; this.uuid = uuid; this.conversationUuid = conversationUUid; @@ -225,9 +234,21 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable this.bodyLanguage = bodyLanguage; this.subject = subject; if (fileParams != null) this.fileParams = new FileParams(fileParams); - } + if (payloads != null) this.payloads = payloads; + } + + public static Message fromCursor(Cursor cursor, Conversation conversation) throws IOException { + String payloadsStr = cursor.getString(cursor.getColumnIndex("payloads")); + List payloads = new ArrayList<>(); + if (payloadsStr != null) { + final XmlReader xmlReader = new XmlReader(); + xmlReader.setInputStream(ByteSource.wrap(payloadsStr.getBytes()).openStream()); + Tag tag; + while ((tag = xmlReader.readTag()) != null) { + payloads.add(xmlReader.readElement(tag)); + } + } - public static Message fromCursor(Cursor cursor, Conversation conversation) { return new Message(conversation, cursor.getString(cursor.getColumnIndex(UUID)), cursor.getString(cursor.getColumnIndex(CONVERSATION)), @@ -253,7 +274,8 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable cursor.getString(cursor.getColumnIndex(BODY_LANGUAGE)), cursor.getString(cursor.getColumnIndex("subject")), cursor.getString(cursor.getColumnIndex("oobUri")), - cursor.getString(cursor.getColumnIndex("fileParams")) + cursor.getString(cursor.getColumnIndex("fileParams")), + payloads ); } @@ -289,6 +311,7 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable values.put("subject", subject); values.put("oobUri", oobUri == null ? null : oobUri.toString()); values.put("fileParams", fileParams == null ? null : fileParams.toString()); + values.put("payloads", payloads.size() < 1 ? null : payloads.stream().map(Object::toString).collect(Collectors.joining())); return values; } @@ -841,6 +864,22 @@ public class Message extends AbstractEntity implements AvatarService.Avatarable this.oob = this.oobUri != null; } + public void addPayload(Element el) { + this.payloads.add(el); + } + + public List getCommands() { + if (this.payloads == null) return null; + + for (Element el : this.payloads) { + if (el.getName().equals("query") && el.getNamespace().equals("http://jabber.org/protocol/disco#items") && el.getAttribute("node").equals("http://jabber.org/protocol/commands")) { + return el.getChildren(); + } + } + + return null; + } + public String getMimeType() { String extension; if (relativeFilePath != null) { diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 843231f5c..fac226603 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -585,6 +585,11 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece } } message.markable = packet.hasChild("markable", "urn:xmpp:chat-markers:0"); + for (Element el : packet.getChildren()) { + if (el.getName().equals("query") && el.getNamespace().equals("http://jabber.org/protocol/disco#items") && el.getAttribute("node").equals("http://jabber.org/protocol/commands")) { + message.addPayload(el); + } + } if (conversationMultiMode) { message.setMucUser(conversation.getMucOptions().findUserByFullJid(counterpart)); final Jid fallback = conversation.getMucOptions().getTrueCounterpart(counterpart); diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index a1c4caa4a..9c9f9e086 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -244,6 +244,14 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.execSQL("PRAGMA cheogram.user_version = 2"); } + if(cheogramVersion < 3) { + db.execSQL( + "ALTER TABLE cheogram." + Message.TABLENAME + " " + + "ADD COLUMN payloads TEXT" + ); + db.execSQL("PRAGMA cheogram.user_version = 3"); + } + db.setTransactionSuccessful(); } finally { db.endTransaction(); diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/CommandButtonAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/CommandButtonAdapter.java new file mode 100644 index 000000000..66e204900 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/ui/adapter/CommandButtonAdapter.java @@ -0,0 +1,29 @@ +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.CommandButtonBinding; + +public class CommandButtonAdapter extends ArrayAdapter { + public CommandButtonAdapter(XmppActivity activity) { + super(activity, 0); + } + + @Override + public View getView(int position, View view, @NonNull ViewGroup parent) { + CommandButtonBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.command_button, parent, false); + binding.command.setText(getItem(position).getAttribute("name")); + binding.command.setFocusable(false); + binding.command.setClickable(false); + return binding.getRoot(); + } +} diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java index 5a30d5be9..c295d23da 100644 --- a/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +++ b/src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java @@ -24,6 +24,7 @@ import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; @@ -75,6 +76,7 @@ import eu.siacs.conversations.utils.TimeFrameUtils; import eu.siacs.conversations.utils.UIHelper; import eu.siacs.conversations.xmpp.Jid; import eu.siacs.conversations.xmpp.mam.MamReference; +import eu.siacs.conversations.xml.Element; public class MessageAdapter extends ArrayAdapter { @@ -616,6 +618,7 @@ public class MessageAdapter extends ArrayAdapter { final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted()); final Conversational conversation = message.getConversation(); final Account account = conversation.getAccount(); + final List commands = message.getCommands(); final int type = getItemViewType(position); ViewHolder viewHolder; if (view == null) { @@ -661,6 +664,7 @@ public class MessageAdapter extends ArrayAdapter { viewHolder.indicatorReceived = view.findViewById(R.id.indicator_received); viewHolder.encryption = view.findViewById(R.id.message_encryption); viewHolder.audioPlayer = view.findViewById(R.id.audio_player); + viewHolder.commands_list = view.findViewById(R.id.commands_list); break; case STATUS: view = activity.getLayoutInflater().inflate(R.layout.message_status, parent, false); @@ -830,6 +834,16 @@ public class MessageAdapter extends ArrayAdapter { } if (type == RECEIVED) { + if (commands != null && conversation instanceof Conversation) { + CommandButtonAdapter adapter = new CommandButtonAdapter(activity); + adapter.addAll(commands); + viewHolder.commands_list.setAdapter(adapter); + viewHolder.commands_list.setVisibility(View.VISIBLE); + viewHolder.commands_list.setOnItemClickListener((p, v, pos, id) -> { + ((Conversation) conversation).startCommand(adapter.getItem(pos), activity.xmppConnectionService); + }); + } + if (isInValidSession) { int bubble; if (!mUseGreenBackground) { @@ -938,5 +952,6 @@ public class MessageAdapter extends ArrayAdapter { protected ImageView contact_picture; protected TextView status_message; protected TextView encryption; + protected ListView commands_list; } } diff --git a/src/main/res/layout/message_content.xml b/src/main/res/layout/message_content.xml index 05af4e42c..38ba2e2f3 100644 --- a/src/main/res/layout/message_content.xml +++ b/src/main/res/layout/message_content.xml @@ -28,6 +28,14 @@ android:longClickable="true" android:visibility="gone"/> + + - \ No newline at end of file + -- 2.45.2