From 2c819a0dc9cc48ccdcc92ac8b35d2fc96c7689f3 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 12 Feb 2025 15:29:21 -0500 Subject: [PATCH] Speed up export --- .../com/cheogram/ExportBackupService.java | 79 ++++++++++++------- 1 file changed, 50 insertions(+), 29 deletions(-) diff --git a/src/cheogram/java/com/cheogram/ExportBackupService.java b/src/cheogram/java/com/cheogram/ExportBackupService.java index 0097719e7c0f8a4eb963fafb3a4ef20167de3715..c81bbece77d79d628e52533874512a451fa21e45 100644 --- a/src/cheogram/java/com/cheogram/ExportBackupService.java +++ b/src/cheogram/java/com/cheogram/ExportBackupService.java @@ -29,6 +29,7 @@ import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; import java.io.ByteArrayOutputStream; @@ -148,8 +149,12 @@ public class ExportBackupService extends Worker { } private void messageExport(SQLiteDatabase db, Account account, PrintWriter writer, Progress progress) { + final ImmutableMap emptyMap = ImmutableMap.of(); + final var mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); + mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + final var accountJid = account.getJid().toString(); final var notificationManager = getApplicationContext().getSystemService(NotificationManager.class); - Cursor cursor = db.rawQuery("select conversations.*, messages.* from messages left join cheogram.messages using (uuid) join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=? order by timeSent", new String[]{account.getUuid()}); + Cursor cursor = db.rawQuery("select conversations.mode, messages.type, messages.status, messages.serverMsgId, messages.timeSent, messages.counterpart, messages.trueCounterpart, messages.body, messages.subject, messages.payloads, messages.reactions, messages.occupant_id, messages.occupantId, messages.uuid, messages.remoteMsgId from messages left join cheogram.messages using (uuid) join conversations on conversations.uuid=messages.conversationUuid where conversations.accountUuid=? order by timeSent", new String[]{account.getUuid()}); int size = cursor != null ? cursor.getCount() : 0; Log.d(Config.LOGTAG, "exporting " + size + " messages for account " + account.getUuid()); int i = 0; @@ -157,58 +162,73 @@ public class ExportBackupService extends Worker { writer.write(archive.startTag().toString()); while (cursor != null && cursor.moveToNext()) { try { - final Conversation conversation = Conversation.fromCursor(cursor); - Message m = Message.fromCursor(cursor, conversation); - Element result = new Element("result", "urn:xmpp:mam:2"); - if (m.getServerMsgId() != null) result.setAttribute("id", m.getServerMsgId()); - Element forwarded = new Element("forwarded", "urn:xmpp:forward:0"); - final SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US); - mDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); - Element delay = forwarded.addChild("delay", "urn:xmpp:delay"); - Date date = new Date(m.getTimeSent()); + final var conversationMode = cursor.getInt(cursor.getColumnIndexOrThrow(Conversation.MODE)); + final var mType = cursor.getInt(cursor.getColumnIndex(Message.TYPE)); + final var mStatus = cursor.getInt(cursor.getColumnIndex(Message.STATUS)); + final var serverMsgId = cursor.getString(cursor.getColumnIndex(Message.SERVER_MSG_ID)); + final var timeSent = cursor.getLong(cursor.getColumnIndex(Message.TIME_SENT)); + final var counterpart = cursor.getString(cursor.getColumnIndex(Message.COUNTERPART)); + final var rawBody = cursor.getString(cursor.getColumnIndex(Message.BODY)); + final var subject = cursor.getString(cursor.getColumnIndex("subject")); + final var payloads = cursor.getString(cursor.getColumnIndex("payloads")); + + var result = new Element("result", "urn:xmpp:mam:2"); + if (serverMsgId != null) result.setAttribute("id", serverMsgId); + var forwarded = new Element("forwarded", "urn:xmpp:forward:0"); + final var delay = forwarded.addChild("delay", "urn:xmpp:delay"); + final var date = new Date(timeSent); delay.setAttribute("stamp", mDateFormat.format(date)); // TODO: time received? - Element message = new Element("message", "jabber:client").setAttribute("type", conversation.getMode() == Conversation.MODE_MULTI && m.getType() != Message.TYPE_PRIVATE && m.getType() != Message.TYPE_PRIVATE_FILE ? "groupchat" : "chat"); + var message = new Element("message", "jabber:client").setAttribute("type", conversationMode == Conversation.MODE_MULTI && mType != Message.TYPE_PRIVATE && mType != Message.TYPE_PRIVATE_FILE ? "groupchat" : "chat"); String outerId = null; - if (m.getStatus() <= Message.STATUS_RECEIVED) { - message.setAttribute("to", account.getJid()).setAttribute("from", m.getCounterpart()); - if (m.getRemoteMsgId() != null) outerId = m.getRemoteMsgId(); + if (mStatus <= Message.STATUS_RECEIVED) { + message.setAttribute("to", accountJid).setAttribute("from", counterpart); + final var remoteMsgId = cursor.getString(cursor.getColumnIndex(Message.REMOTE_MSG_ID)); + if (remoteMsgId != null) outerId = remoteMsgId; } else { - message.setAttribute("from", account.getJid()).setAttribute("to", m.getCounterpart()); - outerId = m.getUuid(); + message.setAttribute("from", accountJid).setAttribute("to", counterpart); + outerId = cursor.getString(cursor.getColumnIndex(Message.UUID)); } if (outerId != null) message.setAttribute("id", outerId); - if (m.getRawBody() != null) message.addChild(new Element("body").setContent(m.getRawBody())); - if (m.getSubject() != null) message.addChild(new Element("subject").setContent(m.getSubject())); - if (conversation.getMode() == Conversation.MODE_MULTI) { + if (rawBody != null) message.addChild(new Element("body").setContent(rawBody)); + if (subject != null) message.addChild(new Element("subject").setContent(subject)); + if (conversationMode == Conversation.MODE_MULTI) { + final var trueCounterpart = cursor.getString(cursor.getColumnIndex(Message.TRUE_COUNTERPART)); + var occupantId = cursor.getString(cursor.getColumnIndexOrThrow(Message.OCCUPANT_ID)); + final String legacyOccupant = cursor.getString(cursor.getColumnIndex("occupant_id")); + if (legacyOccupant != null) occupantId = legacyOccupant; final var x = new Element("x", "http://jabber.org/protocol/muc#user"); - if (m.getTrueCounterpart() != null) x.addChild("item", "http://jabber.org/protocol/muc#user").setAttribute("jid", m.getTrueCounterpart()); + if (trueCounterpart != null) x.addChild("item", "http://jabber.org/protocol/muc#user").setAttribute("jid", trueCounterpart); message.addChild(x); - if (m.getOccupantId() != null) message.addChild("occupant-id", "urn:xmpp:occupant-id:0").setAttribute("id", m.getOccupantId()); + if (occupantId != null) message.addChild("occupant-id", "urn:xmpp:occupant-id:0").setAttribute("id", occupantId); } - message.addChildren(m.getPayloads()); forwarded.addChild(message); result.addChild(forwarded); - writer.write(result.toString()); + final StringBuilder elementOutput = new StringBuilder(); + result.appendToBuilder(emptyMap, elementOutput, 3); + writer.write(elementOutput.toString()); + if (payloads != null) writer.write(payloads); + writer.write("\n"); final HashMultimap aggregatedReactions = HashMultimap.create(); - for (final var reaction : m.getReactions()) { + final var reactions = Reaction.fromString(cursor.getString(cursor.getColumnIndexOrThrow(Message.REACTIONS))); + for (final var reaction : reactions) { aggregatedReactions.put(reaction.occupantId == null ? (reaction.trueJid == null ? reaction.from.toString() : reaction.trueJid.toString()) : reaction.occupantId, reaction); } for (final var reactionSet : aggregatedReactions.asMap().values()) { final var reaction = reactionSet.iterator().next(); result = new Element("result", "urn:xmpp:mam:2"); forwarded = new Element("forwarded", "urn:xmpp:forward:0"); - message = new Element("message", "jabber:client").setAttribute("type", conversation.getMode() == Conversation.MODE_MULTI && m.getType() != Message.TYPE_PRIVATE && m.getType() != Message.TYPE_PRIVATE_FILE ? "groupchat" : "chat"); - message.setAttribute("from", reaction.from).setAttribute("to", reaction.received && conversation.getMode() != Conversation.MODE_MULTI ? account.getJid() : m.getCounterpart()); + message = new Element("message", "jabber:client").setAttribute("type", conversationMode == Conversation.MODE_MULTI && mType != Message.TYPE_PRIVATE && mType != Message.TYPE_PRIVATE_FILE ? "groupchat" : "chat"); + message.setAttribute("from", reaction.from).setAttribute("to", reaction.received && conversationMode != Conversation.MODE_MULTI ? accountJid : counterpart); if (reaction.envelopeId != null) message.setAttribute("id", reaction.envelopeId); final var reactionsEl = new Element("reactions", "urn:xmpp:reactions:0"); - reactionsEl.setAttribute("id", "groupchat".equals(message.getAttribute("type")) ? m.getServerMsgId() : outerId); + reactionsEl.setAttribute("id", "groupchat".equals(message.getAttribute("type")) ? serverMsgId : outerId); for (final var r : reactionSet) { reactionsEl.addChild("reaction", "urn:xmpp:reactions:0").setContent(r.reaction); } message.addChild(reactionsEl); - if (conversation.getMode() == Conversation.MODE_MULTI) { + if (conversationMode == Conversation.MODE_MULTI) { final var x = new Element("x", "http://jabber.org/protocol/muc#user"); if (reaction.trueJid != null) x.addChild("item", "http://jabber.org/protocol/muc#user").setAttribute("jid", reaction.trueJid); message.addChild(x); @@ -331,6 +351,7 @@ public class ExportBackupService extends Worker { writer.write(serverData.startTag().toString()); writer.write(host.startTag().toString()); writer.write(user.startTag().toString()); + writer.write("\n"); Element roster = new Element("query", "jabber:iq:roster"); if (!"".equals(account.getRosterVersion())) roster.setAttribute("ver", account.getRosterVersion()); @@ -380,8 +401,8 @@ public class ExportBackupService extends Worker { SQLiteDatabase db = database.getReadableDatabase(); final String uuid = account.getUuid(); messageExport(db, account, writer, progress); - messageExportCheogram(db, account, writer, progress); + writer.write("\n"); writer.write(user.endTag().toString()); writer.write(host.endTag().toString()); writer.write(serverData.endTag().toString());