A src/cheogram/res/layout/command_button.xml => src/cheogram/res/layout/command_button.xml +8 -0
@@ 0,0 1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android">
+<TextView
+ android:id="@+id/command"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/buttonStyleSmall" />
+</layout>
M src/main/java/eu/siacs/conversations/entities/IndividualMessage.java => src/main/java/eu/siacs/conversations/entities/IndividualMessage.java +1 -1
@@ 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<ReadByMarker> 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
M src/main/java/eu/siacs/conversations/entities/Message.java => src/main/java/eu/siacs/conversations/entities/Message.java +43 -4
@@ 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<Element> payloads = new ArrayList<>();
protected List<Edit> 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<ReadByMarker> 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<Element> 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<Element> 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<Element> 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) {
M src/main/java/eu/siacs/conversations/parser/MessageParser.java => src/main/java/eu/siacs/conversations/parser/MessageParser.java +5 -0
@@ 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);
M src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java => src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +8 -0
@@ 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();
A src/main/java/eu/siacs/conversations/ui/adapter/CommandButtonAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/CommandButtonAdapter.java +29 -0
@@ 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<Element> {
+ 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();
+ }
+}
M src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java => src/main/java/eu/siacs/conversations/ui/adapter/MessageAdapter.java +15 -0
@@ 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<Message> {
@@ 616,6 618,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
final boolean isInValidSession = message.isValidInSession() && (!omemoEncryption || message.isTrusted());
final Conversational conversation = message.getConversation();
final Account account = conversation.getAccount();
+ final List<Element> commands = message.getCommands();
final int type = getItemViewType(position);
ViewHolder viewHolder;
if (view == null) {
@@ 661,6 664,7 @@ public class MessageAdapter extends ArrayAdapter<Message> {
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<Message> {
}
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<Message> {
protected ImageView contact_picture;
protected TextView status_message;
protected TextView encryption;
+ protected ListView commands_list;
}
}
M src/main/res/layout/message_content.xml => src/main/res/layout/message_content.xml +9 -1
@@ 28,6 28,14 @@
android:longClickable="true"
android:visibility="gone"/>
+ <ListView
+ android:id="@+id/commands_list"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:divider="@android:color/transparent"
+ android:dividerHeight="0dp"></ListView>
+
<RelativeLayout
android:id="@+id/audio_player"
android:layout_width="@dimen/audio_player_width"
@@ 63,4 71,4 @@
android:progress="100"/>
</RelativeLayout>
-</merge>>
\ No newline at end of file
+</merge>