From 4f654044b4be713e33f764d009a9468cc163c1aa Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 30 Mar 2023 18:49:04 +0200 Subject: [PATCH] remove fallback body when rendering --- .../1.json | 18 ++++++++++--- .../android/database/dao/MessageDao.java | 27 ++++++++++++++----- .../database/entity/MessageEntity.java | 3 +++ .../model/MessageWithContentReactions.java | 16 +++++++++-- .../transformer/MessageTransformation.java | 2 ++ .../android/transformer/Transformer.java | 18 ++++++++++++- .../conversations/android/xml/Namespace.java | 1 + .../android/xmpp/manager/ArchiveManager.java | 8 ++++++ .../android/xmpp/model/fallback/Body.java | 20 ++++++++++++++ .../android/xmpp/model/fallback/Fallback.java | 20 ++++++++++++++ .../xmpp/model/fallback/package-info.java | 5 ++++ 11 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 app/src/main/java/im/conversations/android/xmpp/model/fallback/Body.java create mode 100644 app/src/main/java/im/conversations/android/xmpp/model/fallback/Fallback.java create mode 100644 app/src/main/java/im/conversations/android/xmpp/model/fallback/package-info.java diff --git a/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json b/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json index 65c3acb49..e6b328285 100644 --- a/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json +++ b/app/schemas/im.conversations.android.database.ConversationsDatabase/1.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 1, - "identityHash": "cc15c6de66482506c7f895ccaff971b4", + "identityHash": "a4cee026f132d06fdad6ff6db418d041", "entities": [ { "tableName": "account", @@ -1665,7 +1665,7 @@ }, { "tableName": "message", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `receivedAt` INTEGER, `sentAt` INTEGER, `outgoing` INTEGER NOT NULL, `toBare` TEXT, `toResource` TEXT, `fromBare` TEXT, `fromResource` TEXT, `occupantId` TEXT, `senderIdentity` TEXT, `messageId` TEXT, `stanzaId` TEXT, `stanzaIdVerified` INTEGER NOT NULL, `latestVersion` INTEGER, `acknowledged` INTEGER NOT NULL, `inReplyToMessageId` TEXT, `inReplyToStanzaId` TEXT, `inReplyToMessageEntityId` INTEGER, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`latestVersion`) REFERENCES `message_version`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToMessageEntityId`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT, `chatId` INTEGER NOT NULL, `receivedAt` INTEGER, `sentAt` INTEGER, `outgoing` INTEGER NOT NULL, `toBare` TEXT, `toResource` TEXT, `fromBare` TEXT, `fromResource` TEXT, `occupantId` TEXT, `senderIdentity` TEXT, `messageId` TEXT, `stanzaId` TEXT, `stanzaIdVerified` INTEGER NOT NULL, `latestVersion` INTEGER, `acknowledged` INTEGER NOT NULL, `inReplyToMessageId` TEXT, `inReplyToStanzaId` TEXT, `inReplyToMessageEntityId` INTEGER, `inReplyToFallbackStart` INTEGER NOT NULL, `inReplyToFallbackEnd` INTEGER NOT NULL, FOREIGN KEY(`chatId`) REFERENCES `chat`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`latestVersion`) REFERENCES `message_version`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE , FOREIGN KEY(`inReplyToMessageEntityId`) REFERENCES `message`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL )", "fields": [ { "fieldPath": "id", @@ -1780,6 +1780,18 @@ "columnName": "inReplyToMessageEntityId", "affinity": "INTEGER", "notNull": false + }, + { + "fieldPath": "inReplyToFallbackStart", + "columnName": "inReplyToFallbackStart", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "inReplyToFallbackEnd", + "columnName": "inReplyToFallbackEnd", + "affinity": "INTEGER", + "notNull": true } ], "primaryKey": { @@ -2681,7 +2693,7 @@ "views": [], "setupQueries": [ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'cc15c6de66482506c7f895ccaff971b4')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a4cee026f132d06fdad6ff6db418d041')" ] } } \ No newline at end of file diff --git a/app/src/main/java/im/conversations/android/database/dao/MessageDao.java b/app/src/main/java/im/conversations/android/database/dao/MessageDao.java index 9d58d35b0..333020b86 100644 --- a/app/src/main/java/im/conversations/android/database/dao/MessageDao.java +++ b/app/src/main/java/im/conversations/android/database/dao/MessageDao.java @@ -26,6 +26,7 @@ import im.conversations.android.database.model.MessageWithContentReactions; import im.conversations.android.database.model.Modification; import im.conversations.android.transformer.MessageContentWrapper; import im.conversations.android.transformer.MessageTransformation; +import im.conversations.android.xmpp.model.fallback.Body; import im.conversations.android.xmpp.model.reactions.Reactions; import im.conversations.android.xmpp.model.stanza.Message; import java.util.Collection; @@ -442,7 +443,7 @@ public abstract class MessageDao { + " c.type IN ('MUC','MUC_PM') THEN (SELECT resource FROM presence WHERE" + " accountId=c.accountId AND address=c.address AND occupantId=m.occupantId LIMIT" + " 1) ELSE NULL END) as occupantResource,modification,latestVersion as" - + " version,inReplyToMessageEntityId,encryption,message_version.identityKey,trust,(CASE" + + " version,inReplyToMessageEntityId,inReplyToFallbackStart,inReplyToFallbackEnd,encryption,message_version.identityKey,trust,(CASE" + " WHEN c.type IN ('MUC','MUC_PM') THEN (SELECT count(distinct(df.feature)) == 2" + " FROM disco_item di JOIN disco_feature df ON di.discoId = df.discoId WHERE" + " di.address=c.address AND df.feature IN('muc_membersonly','muc_nonanonymous'))" @@ -472,7 +473,7 @@ public abstract class MessageDao { + " c.type IN ('MUC','MUC_PM') THEN (SELECT resource FROM presence WHERE" + " accountId=c.accountId AND address=c.address AND occupantId=m.occupantId LIMIT" + " 1) ELSE NULL END) as occupantResource,modification,latestVersion as" - + " version,inReplyToMessageEntityId,encryption,message_version.identityKey,trust,(CASE" + + " version,inReplyToMessageEntityId,inReplyToFallbackStart,inReplyToFallbackEnd,encryption,message_version.identityKey,trust,(CASE" + " WHEN c.type IN ('MUC','MUC_PM') THEN (SELECT count(distinct(df.feature)) == 2" + " FROM disco_item di JOIN disco_feature df ON di.discoId = df.discoId WHERE" + " di.address=c.address AND df.feature IN('muc_membersonly','muc_nonanonymous'))" @@ -493,11 +494,12 @@ public abstract class MessageDao { public abstract ListenableFuture getPosition(final long chatId, final long messageId); public void setInReplyTo( - ChatIdentifier chat, - MessageIdentifier messageIdentifier, - Message.Type messageType, + final ChatIdentifier chat, + final MessageIdentifier messageIdentifier, + final Message.Type messageType, final Jid to, - String inReplyTo) { + final String inReplyTo, + final Body fallbackBody) { if (messageType == Message.Type.GROUPCHAT) { final Long messageEntityId = getMessageByStanzaId(chat.id, inReplyTo); setInReplyToStanzaId(messageIdentifier.id, inReplyTo, messageEntityId); @@ -505,6 +507,12 @@ public abstract class MessageDao { final Long messageEntityId = getMessageByMessageId(chat.id, to.asBareJid(), inReplyTo); setInReplyToMessageId(messageIdentifier.id, inReplyTo, messageEntityId); } + final int inReplyToFallbackStart = fallbackBody == null ? 0 : fallbackBody.getStart(); + final int inReplyToFallbackEnd = fallbackBody == null ? 0 : fallbackBody.getEnd(); + if (inReplyToFallbackStart != 0 || inReplyToFallbackEnd != 0) { + setInReplyToFallback( + messageIdentifier.id, inReplyToFallbackStart, inReplyToFallbackEnd); + } } @Query( @@ -521,6 +529,13 @@ public abstract class MessageDao { protected abstract void setInReplyToMessageId( final long id, String messageId, Long inReplyToMessageEntityId); + @Query( + "UPDATE message SET" + + " inReplyToFallbackStart=:inReplyToFallbackStart,inReplyToFallbackEnd=:inReplyToFallbackEnd" + + " WHERE id=:id") + protected abstract void setInReplyToFallback( + final long id, final int inReplyToFallbackStart, final int inReplyToFallbackEnd); + @Query( "SELECT id FROM message WHERE chatId=:chatId AND fromBare=:fromBare AND" + " messageId=:messageId") diff --git a/app/src/main/java/im/conversations/android/database/entity/MessageEntity.java b/app/src/main/java/im/conversations/android/database/entity/MessageEntity.java index c929580c2..9ddb95f81 100644 --- a/app/src/main/java/im/conversations/android/database/entity/MessageEntity.java +++ b/app/src/main/java/im/conversations/android/database/entity/MessageEntity.java @@ -71,6 +71,9 @@ public class MessageEntity { public String inReplyToStanzaId; @Nullable public Long inReplyToMessageEntityId; + public int inReplyToFallbackStart; + public int inReplyToFallbackEnd; + public static MessageEntity of(final long chatId, final MessageTransformation transformation) { final var entity = new MessageEntity(); entity.chatId = chatId; diff --git a/app/src/main/java/im/conversations/android/database/model/MessageWithContentReactions.java b/app/src/main/java/im/conversations/android/database/model/MessageWithContentReactions.java index 9eec214e5..97f24ddb5 100644 --- a/app/src/main/java/im/conversations/android/database/model/MessageWithContentReactions.java +++ b/app/src/main/java/im/conversations/android/database/model/MessageWithContentReactions.java @@ -57,6 +57,8 @@ public final class MessageWithContentReactions public long version; public boolean acknowledged; public Long inReplyToMessageEntityId; + public int inReplyToFallbackStart; + public int inReplyToFallbackEnd; public Encryption encryption; public IdentityKey identityKey; public Trust trust; @@ -95,8 +97,18 @@ public final class MessageWithContentReactions } public String textContent() { - final var content = Iterables.getFirst(this.contents, null); - return Strings.nullToEmpty(content == null ? null : content.body); + final var textContent = + Iterables.getFirst( + Iterables.filter(this.contents, c -> c.type == PartType.TEXT), null); + final var body = Strings.nullToEmpty(textContent == null ? null : textContent.body); + ; + if (inReplyToMessageEntityId != null + && inReplyToFallbackEnd > inReplyToFallbackStart + && inReplyToFallbackEnd <= body.length()) { + return body.substring(0, inReplyToFallbackStart) + body.substring(inReplyToFallbackEnd); + } else { + return body; + } } public boolean hasPreview() { diff --git a/app/src/main/java/im/conversations/android/transformer/MessageTransformation.java b/app/src/main/java/im/conversations/android/transformer/MessageTransformation.java index ce800315f..3b5d865c0 100644 --- a/app/src/main/java/im/conversations/android/transformer/MessageTransformation.java +++ b/app/src/main/java/im/conversations/android/transformer/MessageTransformation.java @@ -11,6 +11,7 @@ import im.conversations.android.xmpp.model.Extension; import im.conversations.android.xmpp.model.axolotl.Encrypted; import im.conversations.android.xmpp.model.correction.Replace; import im.conversations.android.xmpp.model.error.Error; +import im.conversations.android.xmpp.model.fallback.Fallback; import im.conversations.android.xmpp.model.jabber.Body; import im.conversations.android.xmpp.model.jabber.Thread; import im.conversations.android.xmpp.model.markers.Displayed; @@ -42,6 +43,7 @@ public class MessageTransformation extends Transformation { Replace.class, Reactions.class, Reply.class, + Fallback.class, Retract.class); private final BareJid senderIdentity; diff --git a/app/src/main/java/im/conversations/android/transformer/Transformer.java b/app/src/main/java/im/conversations/android/transformer/Transformer.java index 8f17b007f..682060088 100644 --- a/app/src/main/java/im/conversations/android/transformer/Transformer.java +++ b/app/src/main/java/im/conversations/android/transformer/Transformer.java @@ -3,6 +3,7 @@ package im.conversations.android.transformer; import android.content.Context; import androidx.annotation.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; import im.conversations.android.axolotl.AxolotlService; import im.conversations.android.database.ConversationsDatabase; import im.conversations.android.database.model.Account; @@ -11,11 +12,13 @@ import im.conversations.android.database.model.MessageIdentifier; import im.conversations.android.database.model.MessageState; import im.conversations.android.database.model.Modification; import im.conversations.android.database.model.StanzaId; +import im.conversations.android.xml.Namespace; import im.conversations.android.xmpp.Range; import im.conversations.android.xmpp.manager.ArchiveManager; import im.conversations.android.xmpp.model.DeliveryReceipt; import im.conversations.android.xmpp.model.axolotl.Encrypted; import im.conversations.android.xmpp.model.correction.Replace; +import im.conversations.android.xmpp.model.fallback.Fallback; import im.conversations.android.xmpp.model.markers.Displayed; import im.conversations.android.xmpp.model.muc.user.MucUser; import im.conversations.android.xmpp.model.reactions.Reactions; @@ -205,9 +208,22 @@ public class Transformer { if (Objects.nonNull(reply) && Objects.nonNull(reply.getId()) && Objects.nonNull(reply.getTo())) { + final var fallback = + Iterables.tryFind( + transformation.getExtensions(Fallback.class), + f -> Namespace.REPLY.equals(f.getFor())) + .orNull(); + final var fallbackBody = fallback == null ? null : fallback.getBody(); + LOGGER.info("fallbacks {}", transformation.getExtensions(Fallback.class)); + LOGGER.info("fallback body {}", fallbackBody); database.messageDao() .setInReplyTo( - chat, messageIdentifier, messageType, reply.getTo(), reply.getId()); + chat, + messageIdentifier, + messageType, + reply.getTo(), + reply.getId(), + fallbackBody); } return true; } diff --git a/app/src/main/java/im/conversations/android/xml/Namespace.java b/app/src/main/java/im/conversations/android/xml/Namespace.java index 273a66726..c8d7f39dc 100644 --- a/app/src/main/java/im/conversations/android/xml/Namespace.java +++ b/app/src/main/java/im/conversations/android/xml/Namespace.java @@ -34,6 +34,7 @@ public final class Namespace { public static final String ENTITY_CAPABILITIES_2 = "urn:xmpp:caps"; public static final String EXTERNAL_SERVICE_DISCOVERY = "urn:xmpp:extdisco:2"; public static final String FAST = "urn:xmpp:fast:0"; + public static final String FALLBACK = "urn:xmpp:fallback:0"; public static final String FLEXIBLE_OFFLINE_MESSAGE_RETRIEVAL = "http://jabber.org/protocol/offline"; public static final String FORWARD = "urn:xmpp:forward:0"; diff --git a/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java b/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java index 3398fb75f..cb3e7fa45 100644 --- a/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java +++ b/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java @@ -24,10 +24,12 @@ import im.conversations.android.xmpp.Range; import im.conversations.android.xmpp.XmppConnection; import im.conversations.android.xmpp.model.Extension; import im.conversations.android.xmpp.model.delay.Delay; +import im.conversations.android.xmpp.model.fallback.Fallback; import im.conversations.android.xmpp.model.mam.Fin; import im.conversations.android.xmpp.model.mam.Query; import im.conversations.android.xmpp.model.mam.Result; import im.conversations.android.xmpp.model.muc.user.MucUser; +import im.conversations.android.xmpp.model.reply.Reply; import im.conversations.android.xmpp.model.rsm.Set; import im.conversations.android.xmpp.model.stanza.Iq; import im.conversations.android.xmpp.model.stanza.Message; @@ -94,6 +96,12 @@ public class ArchiveManager extends AbstractManager { } } + if (forwardedMessage.hasExtension(Reply.class)) { + LOGGER.info( + "message with reply hasFallback {}", + forwardedMessage.hasExtension(Fallback.class)); + } + final var transformation = this.transformationFactory.create( forwardedMessage, stanzaId, receivedAt, privilegedExtensionBuilder.build()); diff --git a/app/src/main/java/im/conversations/android/xmpp/model/fallback/Body.java b/app/src/main/java/im/conversations/android/xmpp/model/fallback/Body.java new file mode 100644 index 000000000..4992756f6 --- /dev/null +++ b/app/src/main/java/im/conversations/android/xmpp/model/fallback/Body.java @@ -0,0 +1,20 @@ +package im.conversations.android.xmpp.model.fallback; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.Extension; + +@XmlElement +public class Body extends Extension { + + public Body() { + super(Body.class); + } + + public int getStart() { + return this.getOptionalIntAttribute("start").or(0); + } + + public int getEnd() { + return this.getOptionalIntAttribute("end").or(0); + } +} diff --git a/app/src/main/java/im/conversations/android/xmpp/model/fallback/Fallback.java b/app/src/main/java/im/conversations/android/xmpp/model/fallback/Fallback.java new file mode 100644 index 000000000..9b91a4581 --- /dev/null +++ b/app/src/main/java/im/conversations/android/xmpp/model/fallback/Fallback.java @@ -0,0 +1,20 @@ +package im.conversations.android.xmpp.model.fallback; + +import im.conversations.android.annotation.XmlElement; +import im.conversations.android.xmpp.model.Extension; + +@XmlElement +public class Fallback extends Extension { + + public Fallback() { + super(Fallback.class); + } + + public Body getBody() { + return this.getExtension(Body.class); + } + + public String getFor() { + return this.getAttribute("for"); + } +} diff --git a/app/src/main/java/im/conversations/android/xmpp/model/fallback/package-info.java b/app/src/main/java/im/conversations/android/xmpp/model/fallback/package-info.java new file mode 100644 index 000000000..aa84e6734 --- /dev/null +++ b/app/src/main/java/im/conversations/android/xmpp/model/fallback/package-info.java @@ -0,0 +1,5 @@ +@XmlPackage(namespace = Namespace.FALLBACK) +package im.conversations.android.xmpp.model.fallback; + +import im.conversations.android.annotation.XmlPackage; +import im.conversations.android.xml.Namespace;