Skip to content

Commit

Permalink
Improve message sync speed & reliability
Browse files Browse the repository at this point in the history
  • Loading branch information
moezbhatti committed Feb 14, 2021
1 parent bc6c19e commit ca1e7b7
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions data/src/main/java/com/moez/QKSMS/mapper/CursorToPartImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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)
}

}
}
13 changes: 12 additions & 1 deletion data/src/main/java/com/moez/QKSMS/migration/QkRealmMigration.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class QkRealmMigration @Inject constructor(
) : RealmMigration {

companion object {
const val SchemaVersion: Long = 10
const val SchemaVersion: Long = 11
}

@SuppressLint("ApplySharedPref")
Expand Down Expand Up @@ -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" }
}

Expand Down
95 changes: 60 additions & 35 deletions data/src/main/java/com/moez/QKSMS/repository/SyncRepositoryImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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<MmsPart>().apply {
addAll(realm.where(MmsPart::class.java)
.equalTo("messageId", contentId)
.findAll())
}
}
}
realm.insertOrUpdate(message)
}
}
realm.insertOrUpdate(messages)
}

// Migrate blocked conversations from 2.7.3
Expand All @@ -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))
Expand Down
4 changes: 2 additions & 2 deletions domain/src/main/java/com/moez/QKSMS/mapper/CursorToPart.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@ import com.moez.QKSMS.model.MmsPart

interface CursorToPart : Mapper<Cursor, MmsPart> {

fun getPartsCursor(messageId: Long): Cursor?
fun getPartsCursor(): Cursor?

}
}
2 changes: 1 addition & 1 deletion domain/src/main/java/com/moez/QKSMS/mapper/Mapper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ interface Mapper<in From, out To> {

fun map(from: From): To

}
}
2 changes: 2 additions & 0 deletions domain/src/main/java/com/moez/QKSMS/model/MmsPart.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ca1e7b7

Please sign in to comment.