From ca1e7b7c7a5514cb1e9e824584a8088cfc47eaba Mon Sep 17 00:00:00 2001 From: Moez Bhatti Date: Sun, 14 Feb 2021 00:19:30 -0500 Subject: [PATCH] Improve message sync speed & reliability --- .../moez/QKSMS/mapper/CursorToMessageImpl.kt | 2 - .../com/moez/QKSMS/mapper/CursorToPartImpl.kt | 8 +- .../moez/QKSMS/migration/QkRealmMigration.kt | 13 ++- .../QKSMS/repository/SyncRepositoryImpl.kt | 95 ++++++++++++------- .../com/moez/QKSMS/mapper/CursorToPart.kt | 4 +- .../main/java/com/moez/QKSMS/mapper/Mapper.kt | 2 +- .../main/java/com/moez/QKSMS/model/MmsPart.kt | 2 + 7 files changed, 81 insertions(+), 45 deletions(-) diff --git a/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt b/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt index 08bfb8f3..03cbe250 100644 --- a/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/mapper/CursorToMessageImpl.kt @@ -124,8 +124,6 @@ class CursorToMessageImpl @Inject constructor( ?.let { EncodedStringValue(subjectCharset, it).string } ?: "" textContentType = "" attachmentType = Message.AttachmentType.NOT_LOADED - - parts.addAll(cursorToPart.getPartsCursor(contentId)?.map { cursorToPart.map(it) } ?: listOf()) } } } diff --git a/data/src/main/java/com/moez/QKSMS/mapper/CursorToPartImpl.kt b/data/src/main/java/com/moez/QKSMS/mapper/CursorToPartImpl.kt index 7624f4cb..c057c140 100644 --- a/data/src/main/java/com/moez/QKSMS/mapper/CursorToPartImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/mapper/CursorToPartImpl.kt @@ -35,6 +35,7 @@ class CursorToPartImpl @Inject constructor(private val context: Context) : Curso override fun map(from: Cursor) = MmsPart().apply { id = from.getLong(from.getColumnIndexOrThrow(Telephony.Mms.Part._ID)) + messageId = from.getLong(from.getColumnIndexOrThrow(Telephony.Mms.Part.MSG_ID)) type = from.getStringOrNull(from.getColumnIndexOrThrow(Telephony.Mms.Part.CONTENT_TYPE)) ?: "*/*" seq = from.getIntOrNull(from.getColumnIndexOrThrow(Telephony.Mms.Part.SEQ)) ?: -1 name = from.getStringOrNull(from.getColumnIndexOrThrow(Telephony.Mms.Part.NAME)) @@ -43,9 +44,8 @@ class CursorToPartImpl @Inject constructor(private val context: Context) : Curso text = from.getStringOrNull(from.getColumnIndexOrThrow(Telephony.Mms.Part.TEXT)) } - override fun getPartsCursor(messageId: Long): Cursor? { - return context.contentResolver.query(CONTENT_URI, null, - "${Telephony.Mms.Part.MSG_ID} = ?", arrayOf(messageId.toString()), null) + override fun getPartsCursor(): Cursor? { + return context.contentResolver.query(CONTENT_URI, null, null, null, null) } -} \ No newline at end of file +} diff --git a/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt b/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt index c8dc96b9..a006f83b 100644 --- a/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt +++ b/data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt @@ -36,7 +36,7 @@ class QkRealmMigration @Inject constructor( ) : RealmMigration { companion object { - const val SchemaVersion: Long = 10 + const val SchemaVersion: Long = 11 } @SuppressLint("ApplySharedPref") @@ -223,6 +223,17 @@ class QkRealmMigration @Inject constructor( version++ } + if (version == 10L) { + realm.schema.get("MmsPart") + ?.addField("messageId", Long::class.java, FieldAttribute.INDEXED, FieldAttribute.REQUIRED) + ?.transform { part -> + val messageId = part.linkingObjects("Message", "parts").firstOrNull()?.getLong("contentId") ?: 0 + part.setLong("messageId", messageId) + } + + version++ + } + check(version >= newVersion) { "Migration missing from v$oldVersion to v$newVersion" } } diff --git a/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt b/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt index eefe1b83..4f03110b 100644 --- a/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt +++ b/data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt @@ -23,6 +23,7 @@ import android.content.ContentUris import android.net.Uri import android.provider.Telephony import com.f2prateek.rx.preferences2.RxSharedPreferences +import com.moez.QKSMS.extensions.forEach import com.moez.QKSMS.extensions.insertOrUpdate import com.moez.QKSMS.extensions.map import com.moez.QKSMS.manager.KeyManager @@ -31,6 +32,7 @@ import com.moez.QKSMS.mapper.CursorToContactGroup import com.moez.QKSMS.mapper.CursorToContactGroupMember import com.moez.QKSMS.mapper.CursorToConversation import com.moez.QKSMS.mapper.CursorToMessage +import com.moez.QKSMS.mapper.CursorToPart import com.moez.QKSMS.mapper.CursorToRecipient import com.moez.QKSMS.model.Contact import com.moez.QKSMS.model.ContactGroup @@ -45,6 +47,7 @@ import com.moez.QKSMS.util.tryOrNull import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.Subject import io.realm.Realm +import io.realm.RealmList import io.realm.Sort import javax.inject.Inject import javax.inject.Singleton @@ -55,6 +58,7 @@ class SyncRepositoryImpl @Inject constructor( private val conversationRepo: ConversationRepository, private val cursorToConversation: CursorToConversation, private val cursorToMessage: CursorToMessage, + private val cursorToPart: CursorToPart, private val cursorToRecipient: CursorToRecipient, private val cursorToContact: CursorToContact, private val cursorToContactGroup: CursorToContactGroup, @@ -103,25 +107,48 @@ class SyncRepositoryImpl @Inject constructor( keys.reset() + val partsCursor = cursorToPart.getPartsCursor() val messageCursor = cursorToMessage.getMessagesCursor() val conversationCursor = cursorToConversation.getConversationsCursor() val recipientCursor = cursorToRecipient.getRecipientCursor() - val max = (messageCursor?.count ?: 0) + + val max = (partsCursor?.count ?: 0) + + (messageCursor?.count ?: 0) + (conversationCursor?.count ?: 0) + (recipientCursor?.count ?: 0) var progress = 0 + // Sync message parts + partsCursor?.use { + partsCursor.forEach { + tryOrNull { + progress++ + val part = cursorToPart.map(partsCursor) + realm.insertOrUpdate(part) + } + } + } + // Sync messages messageCursor?.use { val messageColumns = CursorToMessage.MessageColumns(messageCursor) - val messages = messageCursor.map { cursor -> - progress++ - syncProgress.onNext(SyncRepository.SyncProgress.Running(max, progress, false)) - cursorToMessage.map(Pair(cursor, messageColumns)) + messageCursor.forEach { cursor -> + tryOrNull { + progress++ + syncProgress.onNext(SyncRepository.SyncProgress.Running(max, progress, false)) + val message = cursorToMessage.map(Pair(cursor, messageColumns)).apply { + if (isMms()) { + parts = RealmList().apply { + addAll(realm.where(MmsPart::class.java) + .equalTo("messageId", contentId) + .findAll()) + } + } + } + realm.insertOrUpdate(message) + } } - realm.insertOrUpdate(messages) } // Migrate blocked conversations from 2.7.3 @@ -133,46 +160,44 @@ class SyncRepositoryImpl @Inject constructor( // Sync conversations conversationCursor?.use { - val conversations = conversationCursor.map { cursor -> - progress++ - syncProgress.onNext(SyncRepository.SyncProgress.Running(max, progress, false)) - cursorToConversation.map(cursor).apply { - persistedData[id]?.let { persistedConversation -> - archived = persistedConversation.archived - blocked = persistedConversation.blocked - pinned = persistedConversation.pinned - name = persistedConversation.name - blockingClient = persistedConversation.blockingClient - blockReason = persistedConversation.blockReason + conversationCursor.forEach { cursor -> + tryOrNull { + progress++ + syncProgress.onNext(SyncRepository.SyncProgress.Running(max, progress, false)) + val conversation = cursorToConversation.map(cursor).apply { + persistedData[id]?.let { persistedConversation -> + archived = persistedConversation.archived + blocked = persistedConversation.blocked + pinned = persistedConversation.pinned + name = persistedConversation.name + blockingClient = persistedConversation.blockingClient + blockReason = persistedConversation.blockReason + } + lastMessage = realm.where(Message::class.java) + .sort("date", Sort.DESCENDING) + .equalTo("threadId", id) + .findFirst() } + realm.insertOrUpdate(conversation) } } - - realm.where(Message::class.java) - .sort("date", Sort.DESCENDING) - .distinct("threadId") - .findAll() - .forEach { message -> - val conversation = conversations.find { conversation -> conversation.id == message.threadId } - conversation?.lastMessage = message - } - - realm.insertOrUpdate(conversations) } // Sync recipients recipientCursor?.use { val contacts = realm.copyToRealmOrUpdate(getContacts()) - val recipients = recipientCursor.map { cursor -> - progress++ - syncProgress.onNext(SyncRepository.SyncProgress.Running(max, progress, false)) - cursorToRecipient.map(cursor).apply { - contact = contacts.firstOrNull { contact -> - contact.numbers.any { phoneNumberUtils.compare(address, it.address) } + recipientCursor.forEach { cursor -> + tryOrNull { + progress++ + syncProgress.onNext(SyncRepository.SyncProgress.Running(max, progress, false)) + val recipient = cursorToRecipient.map(cursor).apply { + contact = contacts.firstOrNull { contact -> + contact.numbers.any { phoneNumberUtils.compare(address, it.address) } + } } + realm.insertOrUpdate(recipient) } } - realm.insertOrUpdate(recipients) } syncProgress.onNext(SyncRepository.SyncProgress.Running(0, 0, true)) diff --git a/domain/src/main/java/com/moez/QKSMS/mapper/CursorToPart.kt b/domain/src/main/java/com/moez/QKSMS/mapper/CursorToPart.kt index bb392203..69fdc619 100644 --- a/domain/src/main/java/com/moez/QKSMS/mapper/CursorToPart.kt +++ b/domain/src/main/java/com/moez/QKSMS/mapper/CursorToPart.kt @@ -23,6 +23,6 @@ import com.moez.QKSMS.model.MmsPart interface CursorToPart : Mapper { - fun getPartsCursor(messageId: Long): Cursor? + fun getPartsCursor(): Cursor? -} \ No newline at end of file +} diff --git a/domain/src/main/java/com/moez/QKSMS/mapper/Mapper.kt b/domain/src/main/java/com/moez/QKSMS/mapper/Mapper.kt index 7f726e8c..29430f63 100644 --- a/domain/src/main/java/com/moez/QKSMS/mapper/Mapper.kt +++ b/domain/src/main/java/com/moez/QKSMS/mapper/Mapper.kt @@ -22,4 +22,4 @@ interface Mapper { fun map(from: From): To -} \ No newline at end of file +} diff --git a/domain/src/main/java/com/moez/QKSMS/model/MmsPart.kt b/domain/src/main/java/com/moez/QKSMS/model/MmsPart.kt index d1afb35d..aa3fa908 100644 --- a/domain/src/main/java/com/moez/QKSMS/model/MmsPart.kt +++ b/domain/src/main/java/com/moez/QKSMS/model/MmsPart.kt @@ -21,12 +21,14 @@ package com.moez.QKSMS.model import androidx.core.net.toUri import io.realm.RealmObject import io.realm.RealmResults +import io.realm.annotations.Index import io.realm.annotations.LinkingObjects import io.realm.annotations.PrimaryKey open class MmsPart : RealmObject() { @PrimaryKey var id: Long = 0 + @Index var messageId: Long = 0 var type: String = "" var seq: Int = -1 var name: String? = null