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 0000000000000000000000000000000000000000..9f0df64c2386003607b1ecbd97d7b5bef749895e --- /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 a9a62237aa21d6e0745d6c41eb157f79b7044998..b16de580dead57b6f0d821c49e0537a21d2cd573 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 739a54ee6a648606d1fcc534021f248daba9a5d5..016f101da30974a4ac8a4d40238c96da6eddd962 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 843231f5c7e632ec475fbf95c9ef19499a9ad7ed..fac226603066e67fb2e0925ac20976e52fb8abe0 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 a1c4caa4a5bab56dc409e26e56cb1f75dd2d2e91..9c9f9e086c4d7481d10bce4289e1d7307944beda 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 0000000000000000000000000000000000000000..66e2049007d97d651d8fba723aba14507d315a67 --- /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 5a30d5be9e22d047370de30cf8814b273f204140..c295d23dab579db51da55d51fef45c9b558d010e 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 05af4e42c233ea7fcd25f43551aeb2eac051aa14..38ba2e2f35bdc29c529eb931f8e114b6098a657c 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 +