MessagesImporter and MessagesReader refactoring

This commit is contained in:
merkost 2023-07-18 16:23:46 +10:00
parent 4d378e819c
commit 679236e3fa
2 changed files with 114 additions and 156 deletions

View File

@ -1,102 +1,46 @@
package com.simplemobiletools.smsmessenger.helpers package com.simplemobiletools.smsmessenger.helpers
import android.content.Context import android.content.Context
import android.util.JsonToken
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.simplemobiletools.commons.extensions.showErrorToast import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.helpers.ensureBackgroundThread import com.simplemobiletools.commons.helpers.ensureBackgroundThread
import com.simplemobiletools.smsmessenger.extensions.config import com.simplemobiletools.smsmessenger.extensions.config
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_FAIL import com.simplemobiletools.smsmessenger.models.*
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_NOTHING_NEW
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_OK
import com.simplemobiletools.smsmessenger.helpers.MessagesImporter.ImportResult.IMPORT_PARTIAL
import com.simplemobiletools.smsmessenger.models.MmsBackup
import com.simplemobiletools.smsmessenger.models.SmsBackup
import java.io.File
class MessagesImporter(private val context: Context) { class MessagesImporter(private val context: Context) {
enum class ImportResult {
IMPORT_FAIL, IMPORT_OK, IMPORT_PARTIAL, IMPORT_NOTHING_NEW
}
private val gson = Gson()
private val messageWriter = MessagesWriter(context) private val messageWriter = MessagesWriter(context)
private val config = context.config private val config = context.config
private var messagesImported = 0 private var messagesImported = 0
private var messagesFailed = 0 private var messagesFailed = 0
fun importMessages(path: String, onProgress: (total: Int, current: Int) -> Unit = { _, _ -> }, callback: (result: ImportResult) -> Unit) { fun importMessages(messagesBackup: List<MessagesBackup>, callback: (result: ImportResult) -> Unit) {
ensureBackgroundThread { ensureBackgroundThread {
try { try {
val inputStream = if (path.contains("/")) { messagesBackup.forEach { message ->
File(path).inputStream() try {
} else { if (message.backupType == BackupType.SMS && config.importSms) {
context.assets.open(path) messageWriter.writeSmsMessage(message as SmsBackup)
} } else if (message.backupType == BackupType.MMS && config.importMms) {
messageWriter.writeMmsMessage(message as MmsBackup)
inputStream.bufferedReader().use { reader ->
val jsonReader = gson.newJsonReader(reader)
val smsMessageType = object : TypeToken<SmsBackup>() {}.type
val mmsMessageType = object : TypeToken<MmsBackup>() {}.type
jsonReader.beginArray()
while (jsonReader.hasNext()) {
jsonReader.beginObject()
while (jsonReader.hasNext()) {
val nextToken = jsonReader.peek()
if (nextToken.ordinal == JsonToken.NAME.ordinal) {
val msgType = jsonReader.nextName()
if ((!msgType.equals("sms") && !msgType.equals("mms")) ||
(msgType.equals("sms") && !config.importSms) ||
(msgType.equals("mms") && !config.importMms)
) {
jsonReader.skipValue()
continue
}
jsonReader.beginArray()
while (jsonReader.hasNext()) {
try {
if (msgType.equals("sms")) {
val message = gson.fromJson<SmsBackup>(jsonReader, smsMessageType)
messageWriter.writeSmsMessage(message)
} else {
val message = gson.fromJson<MmsBackup>(jsonReader, mmsMessageType)
messageWriter.writeMmsMessage(message)
}
messagesImported++
} catch (e: Exception) {
context.showErrorToast(e)
messagesFailed++
}
}
jsonReader.endArray()
} else {
jsonReader.skipValue()
}
} }
else return@forEach
jsonReader.endObject() messagesImported++
refreshMessages() } catch (e: Exception) {
context.showErrorToast(e)
messagesFailed++
} }
jsonReader.endArray()
} }
refreshMessages()
} catch (e: Exception) { } catch (e: Exception) {
context.showErrorToast(e) context.showErrorToast(e)
messagesFailed++
} }
callback.invoke( callback.invoke(
when { when {
messagesImported == 0 && messagesFailed == 0 -> IMPORT_NOTHING_NEW messagesImported == 0 && messagesFailed == 0 -> ImportResult.IMPORT_NOTHING_NEW
messagesFailed > 0 && messagesImported > 0 -> IMPORT_PARTIAL messagesFailed > 0 && messagesImported > 0 -> ImportResult.IMPORT_PARTIAL
messagesFailed > 0 -> IMPORT_FAIL messagesFailed > 0 -> ImportResult.IMPORT_FAIL
else -> IMPORT_OK else -> ImportResult.IMPORT_OK
} }
) )
} }

View File

@ -9,49 +9,60 @@ import android.util.Base64
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.isQPlus import com.simplemobiletools.commons.helpers.isQPlus
import com.simplemobiletools.commons.helpers.isRPlus import com.simplemobiletools.commons.helpers.isRPlus
import com.simplemobiletools.smsmessenger.models.MmsAddress import com.simplemobiletools.smsmessenger.extensions.getConversationIds
import com.simplemobiletools.smsmessenger.models.MmsBackup import com.simplemobiletools.smsmessenger.models.*
import com.simplemobiletools.smsmessenger.models.MmsPart
import com.simplemobiletools.smsmessenger.models.SmsBackup
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
class MessagesReader(private val context: Context) { class MessagesReader(private val context: Context) {
fun forEachSms(threadId: Long, block: (SmsBackup) -> Unit) {
fun getMessagesToExport(
getSms: Boolean,
getMms: Boolean,
callback: (messages: List<MessagesBackup>) -> Unit
) {
val conversationIds = context.getConversationIds()
var smsMessages = listOf<SmsBackup>()
var mmsMessages = listOf<MmsBackup>()
if (getSms) {
smsMessages = getSmsMessages(conversationIds)
}
if (getMms) {
mmsMessages = getMmsMessages(conversationIds)
}
callback(smsMessages + mmsMessages)
}
private fun getSmsMessages(threadIds: List<Long>): List<SmsBackup> {
val projection = arrayOf( val projection = arrayOf(
Sms.SUBSCRIPTION_ID, Sms.SUBSCRIPTION_ID, Sms.ADDRESS, Sms.BODY, Sms.DATE, Sms.DATE_SENT, Sms.LOCKED, Sms.PROTOCOL, Sms.READ, Sms.STATUS, Sms.TYPE, Sms.SERVICE_CENTER
Sms.ADDRESS,
Sms.BODY,
Sms.DATE,
Sms.DATE_SENT,
Sms.LOCKED,
Sms.PROTOCOL,
Sms.READ,
Sms.STATUS,
Sms.TYPE,
Sms.SERVICE_CENTER
) )
val selection = "${Sms.THREAD_ID} = ?" val selection = "${Sms.THREAD_ID} = ?"
val selectionArgs = arrayOf(threadId.toString()) val smsList = mutableListOf<SmsBackup>()
context.queryCursor(Sms.CONTENT_URI, projection, selection, selectionArgs) { cursor ->
val subscriptionId = cursor.getLongValue(Sms.SUBSCRIPTION_ID) threadIds.map { it.toString() }.forEach { threadId ->
val address = cursor.getStringValue(Sms.ADDRESS) context.queryCursor(Sms.CONTENT_URI, projection, selection, arrayOf(threadId)) { cursor ->
val body = cursor.getStringValueOrNull(Sms.BODY) val subscriptionId = cursor.getLongValue(Sms.SUBSCRIPTION_ID)
val date = cursor.getLongValue(Sms.DATE) val address = cursor.getStringValue(Sms.ADDRESS)
val dateSent = cursor.getLongValue(Sms.DATE_SENT) val body = cursor.getStringValueOrNull(Sms.BODY)
val locked = cursor.getIntValue(Sms.DATE_SENT) val date = cursor.getLongValue(Sms.DATE)
val protocol = cursor.getStringValueOrNull(Sms.PROTOCOL) val dateSent = cursor.getLongValue(Sms.DATE_SENT)
val read = cursor.getIntValue(Sms.READ) val locked = cursor.getIntValue(Sms.DATE_SENT)
val status = cursor.getIntValue(Sms.STATUS) val protocol = cursor.getStringValueOrNull(Sms.PROTOCOL)
val type = cursor.getIntValue(Sms.TYPE) val read = cursor.getIntValue(Sms.READ)
val serviceCenter = cursor.getStringValueOrNull(Sms.SERVICE_CENTER) val status = cursor.getIntValue(Sms.STATUS)
block(SmsBackup(subscriptionId, address, body, date, dateSent, locked, protocol, read, status, type, serviceCenter)) val type = cursor.getIntValue(Sms.TYPE)
val serviceCenter = cursor.getStringValueOrNull(Sms.SERVICE_CENTER)
smsList.add(SmsBackup(subscriptionId, address, body, date, dateSent, locked, protocol, read, status, type, serviceCenter))
}
} }
return smsList
} }
// all mms from simple sms are non-text messages private fun getMmsMessages(threadIds: List<Long>, includeTextOnlyAttachment: Boolean = false): List<MmsBackup> {
fun forEachMms(threadId: Long, includeTextOnlyAttachment: Boolean = false, block: (MmsBackup) -> Unit) {
val projection = arrayOf( val projection = arrayOf(
Mms._ID, Mms._ID,
Mms.CREATOR, Mms.CREATOR,
@ -71,65 +82,67 @@ class MessagesReader(private val context: Context) {
Mms.SUBSCRIPTION_ID, Mms.SUBSCRIPTION_ID,
Mms.TRANSACTION_ID Mms.TRANSACTION_ID
) )
val selection = if (includeTextOnlyAttachment) { val selection = if (includeTextOnlyAttachment) {
"${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?" "${Mms.THREAD_ID} = ? AND ${Mms.TEXT_ONLY} = ?"
} else { } else {
"${Mms.THREAD_ID} = ?" "${Mms.THREAD_ID} = ?"
} }
val mmsList = mutableListOf<MmsBackup>()
val selectionArgs = if (includeTextOnlyAttachment) { threadIds.map { it.toString() }.forEach { threadId ->
arrayOf(threadId.toString(), "1") val selectionArgs = if (includeTextOnlyAttachment) {
} else { arrayOf(threadId, "1")
arrayOf(threadId.toString()) } else {
} arrayOf(threadId)
}
context.queryCursor(Mms.CONTENT_URI, projection, selection, selectionArgs) { cursor ->
val mmsId = cursor.getLongValue(Mms._ID)
val creator = cursor.getStringValueOrNull(Mms.CREATOR)
val contentType = cursor.getStringValueOrNull(Mms.CONTENT_TYPE)
val deliveryReport = cursor.getIntValue(Mms.DELIVERY_REPORT)
val date = cursor.getLongValue(Mms.DATE)
val dateSent = cursor.getLongValue(Mms.DATE_SENT)
val locked = cursor.getIntValue(Mms.LOCKED)
val messageType = cursor.getIntValue(Mms.MESSAGE_TYPE)
val messageBox = cursor.getIntValue(Mms.MESSAGE_BOX)
val read = cursor.getIntValue(Mms.READ)
val readReport = cursor.getIntValue(Mms.READ_REPORT)
val seen = cursor.getIntValue(Mms.SEEN)
val textOnly = cursor.getIntValue(Mms.TEXT_ONLY)
val status = cursor.getStringValueOrNull(Mms.STATUS)
val subject = cursor.getStringValueOrNull(Mms.SUBJECT)
val subjectCharSet = cursor.getStringValueOrNull(Mms.SUBJECT_CHARSET)
val subscriptionId = cursor.getLongValue(Mms.SUBSCRIPTION_ID)
val transactionId = cursor.getStringValueOrNull(Mms.TRANSACTION_ID)
context.queryCursor(Mms.CONTENT_URI, projection, selection, selectionArgs) { cursor -> val parts = getParts(mmsId)
val mmsId = cursor.getLongValue(Mms._ID) val addresses = getMmsAddresses(mmsId)
val creator = cursor.getStringValueOrNull(Mms.CREATOR) mmsList.add(
val contentType = cursor.getStringValueOrNull(Mms.CONTENT_TYPE) MmsBackup(
val deliveryReport = cursor.getIntValue(Mms.DELIVERY_REPORT) creator,
val date = cursor.getLongValue(Mms.DATE) contentType,
val dateSent = cursor.getLongValue(Mms.DATE_SENT) deliveryReport,
val locked = cursor.getIntValue(Mms.LOCKED) date,
val messageType = cursor.getIntValue(Mms.MESSAGE_TYPE) dateSent,
val messageBox = cursor.getIntValue(Mms.MESSAGE_BOX) locked,
val read = cursor.getIntValue(Mms.READ) messageType,
val readReport = cursor.getIntValue(Mms.READ_REPORT) messageBox,
val seen = cursor.getIntValue(Mms.SEEN) read,
val textOnly = cursor.getIntValue(Mms.TEXT_ONLY) readReport,
val status = cursor.getStringValueOrNull(Mms.STATUS) seen,
val subject = cursor.getStringValueOrNull(Mms.SUBJECT) textOnly,
val subjectCharSet = cursor.getStringValueOrNull(Mms.SUBJECT_CHARSET) status,
val subscriptionId = cursor.getLongValue(Mms.SUBSCRIPTION_ID) subject,
val transactionId = cursor.getStringValueOrNull(Mms.TRANSACTION_ID) subjectCharSet,
subscriptionId,
val parts = getParts(mmsId) transactionId,
val addresses = getMmsAddresses(mmsId) addresses,
block( parts
MmsBackup( )
creator,
contentType,
deliveryReport,
date,
dateSent,
locked,
messageType,
messageBox,
read,
readReport,
seen,
textOnly,
status,
subject,
subjectCharSet,
subscriptionId,
transactionId,
addresses,
parts
) )
) }
} }
return mmsList
} }
@SuppressLint("NewApi") @SuppressLint("NewApi")
@ -172,6 +185,7 @@ class MessagesReader(private val context: Context) {
stream.readBytes().toString(Charsets.UTF_8) stream.readBytes().toString(Charsets.UTF_8)
} }
} }
else -> { else -> {
usePart(partId) { stream -> usePart(partId) { stream ->
Base64.encodeToString(stream.readBytes(), Base64.DEFAULT) Base64.encodeToString(stream.readBytes(), Base64.DEFAULT)