Speed up export

Stephen Paul Weber created

Change summary

src/cheogram/java/com/cheogram/ExportBackupService.java | 79 ++++++----
1 file changed, 50 insertions(+), 29 deletions(-)

Detailed changes

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<String,String> 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("</message></forwarded></result>\n");
                 final HashMultimap<String, Reaction> 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());